web-dev-qa-db-ja.com

IntrinsicContentSizeおよびsizeThatFitsの適切な使用:autolayoutを使用したUIViewサブクラス

私はこの(とにかく)単純な質問に気を遣うようにしています。なぜなら、特にオートレイアウトに関しては、多くのUIViewのAPIの誤用が心配になることがあるからです。

非常に簡単にするために例を挙げます。画像アイコンと複数行ラベルを持つUIViewサブクラスが必要だと仮定しましょう。私が望む動作は、ビューの高さがラベルの高さによって変化することです(内部のテキストに合わせて)。また、Interface Builderでレイアウトしているので、次のようなものがあります。

simple view image

画像ビューに固定幅と高さを与え、ラベルに固定幅と位置(画像ビューに相対的)を与えるいくつかの制約付き:

simple view image, constraints shown

ここで、ラベルにテキストを設定する場合、ビューが適切に収まるように高さを変更するか、またはxibの高さと同じままにしておきます。自動レイアウトの前に、私は常にこのようなことをしていたでしょう:

CustoViewサブクラスファイルでは、sizeThatFits:を次のようにオーバーライドします。

- (CGSize) sizeThatFits:(CGSize)size{

    //this stands for whichever method I would have used
    //to calculate the height needed to display the text based on the font
    CGSize labelSize = [self.titleLabel intrinsicContentSize];

    //check if we're bigger than what's in ib, otherwise resize
    CGFloat newHeight = (labelSize.height <= 21) ? 51: labelSize.height+20;

    size.height = newHeight;

    return size;

}

そして、私は次のようなものを呼び出したよりも:

myView.titleLabel.text = @"a big text to display that should be more than a line";
[myView sizeToFit];

今、制約で考えると、自動レイアウトシステムはビューツリー要素でintrinsicContentSizeを呼び出してそのサイズを把握し、その計算を行うことを知っています。したがって、サブビューでintrinsicContentSizeをオーバーライドして前に示したsizeThatFits:メソッドで返されるものとまったく同じものです。ただし、以前はsizeToFitを呼び出すときにビューのサイズを適切に変更していましたが、現在はxibと組み合わせて、これは起こりません。

もちろん、サブクラスでテキストを編集するたびにsizeToFitを呼び出し、オーバーライドされたintrinsicContentSizeを呼び出して、sizeThatFits:とまったく同じサイズを返しますが、どういうわけかしません。これが適切な方法だと思います。

needsUpdateConstraintsupdateConstraintsをオーバーライドすることを考えていましたが、ビューの幅と高さはxibの自動サイズ変更マスクから推測および変換されるため、あまり意味がありません。

長い間、私がここで示したものを正確に作成し、完全な自動レイアウトをサポートする最もクリーンで最も正しい方法は何だと思いますか?

60
dev_mush

私はあなたがintrinsicContentSizeを定義する必要はないと思います。

これを考える2つの理由があります:

  1. Auto LayoutのドキュメントでintrinsicContentSizeについて説明する場合、コンテンツに基づいてサイズを計算できるボタンやラベルのような「リーフビュー」に関連するものとして参照します。アイデアは、他のビューで構成されていないため、ブランチではなく、ビュー階層ツリーのリーフであるということです。

  2. IntrinsicContentSizeは、実際には自動レイアウトの「基本的な」概念ではありません。基本的な概念は、単なる制約と、制約によってバインドされた属性です。固有のコンテンツサイズ、コンテンツを保持する優先度、および圧縮耐性の優先度は、サイズに関する内部制約を生成するために使用するのに本当に便利です。最終的なサイズは、これらの制約が通常の方法で他のすべての制約と相互作用した結果です。

だから何?したがって、「カスタムビュー」が実際には他のいくつかのビューの単なるアセンブリである場合、intrinsicContentSizeを定義する必要はありません。必要なレイアウトを作成する制約を定義するだけで、これらの制約によって必要なサイズが生成されます。

あなたが説明する特定のケースでは、ラベルからスーパービューへの> = 0ボトムスペース制約を設定し、画像からスーパービューへの別の制約を設定し、さらに低優先度の制約を設定しますビュー全体の高さゼロ。優先度の低い制約はアセンブリを縮小しようとしますが、他の制約はアセンブリの縮小を停止し、サブビューをクリップします。

IntrinsicContentSizeを明示的に定義しない場合、これらの制約から生じるサイズをどのように確認しますか? 1つの方法は、レイアウトを強制してから結果を観察することです。

もう1つの方法は、systemLayoutSizeFittingSize:(およびiOS8では、ほとんど予告されていないsystemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:)を使用することです。これは、intrinsicContentSizeよりもsizeThatFits:に近い従兄弟です。システムがビューの適切なサイズを計算するために使用するのは、固有のコンテンツサイズの制約やその他すべてを含む、ビューに含まれるすべての制約を考慮したものです。

残念ながら、複数行のラベルがある場合、preferredMaxLayoutWidthを設定して良好な結果を得る必要がありますが、それは別の話です...

71
algal