UIView's
setNeedsLayout
、layoutIfNeeded
、およびlayoutSubviews
の各メソッドの関係について、誰でも明確な説明を提供できますか?そして、3つすべてが使用される実装例。ありがとう。
混乱するのは、カスタムビューにsetNeedsLayout
メッセージを送信すると、このメソッドの後に呼び出されるのはlayoutSubviews
であり、layoutIfNeeded
をスキップすることです。ドキュメントから、フローはsetNeedsLayout
>であると予想されますlayoutIfNeeded
が呼び出されます> layoutSubviews
が呼び出されます。
私はまだ自分でこれを理解しようとしていますので、これを懐疑的に考えて、エラーが含まれていても許してください。
setNeedsLayout
は簡単です。UIViewのどこかに、レイアウトが必要であるとマークするフラグを設定するだけです。これにより、次の再描画が行われる前に、ビューでlayoutSubviews
が強制的に呼び出されます。 autoresizesSubviews
プロパティのため、多くの場合、これを明示的に呼び出す必要はありません。それが設定されている場合(デフォルト)、ビューのフレームを変更すると、ビューでサブビューがレイアウトされます。
layoutSubviews
は、すべての興味深いことを行う方法です。必要に応じて、レイアウトのdrawRect
と同等です。簡単な例は次のとおりです。
-(void)layoutSubviews {
// Child's frame is always equal to our bounds inset by 8px
self.subview1.frame = CGRectInset(self.bounds, 8.0, 8.0);
// It seems likely that this is incorrect:
// [self.subview1 layoutSubviews];
// ... and this is correct:
[self.subview1 setNeedsLayout];
// but I don't claim to know definitively.
}
AFAIK layoutIfNeeded
は通常、サブクラスでオーバーライドされることを意図していません。これは、ビューをレイアウトするときに呼び出すことを意図したメソッドです今。 Appleの実装は次のようになります。
-(void)layoutIfNeeded {
if (self._needsLayout) {
UIView *sv = self.superview;
if (sv._needsLayout) {
[sv layoutIfNeeded];
} else {
[self layoutSubviews];
}
}
}
ビューでlayoutIfNeeded
を呼び出して、ビュー(および必要に応じてそのスーパービュー)をすぐにレイアウトするように強制します。
場合によっては、setNeedsLayout
に続いてlayoutIfNeeded
を呼び出す必要があるというn8grayの答えを付け加えます。
たとえば、サブビューの配置が複雑で、autoresizingMaskまたはiOS6 AutoLayoutでは実行できないUIViewを拡張するカスタムビューを作成したとします。 layoutSubviews
をオーバーライドすることにより、カスタムポジショニングを行うことができます。
例として、contentView
プロパティとcontentViewの周囲にマージンを設定できるedgeInsets
プロパティを持つカスタムビューがあるとします。 layoutSubviews
は次のようになります。
- (void) layoutSubviews {
self.contentView.frame = CGRectMake(
self.bounds.Origin.x + self.edgeInsets.left,
self.bounds.Origin.y + self.edgeInsets.top,
self.bounds.size.width - self.edgeInsets.left - self.edgeInsets.right,
self.bounds.size.height - self.edgeInsets.top - self.edgeInsets.bottom);
}
edgeInsets
プロパティを変更するたびにフレームの変更をアニメーション化できるようにするには、次のようにedgeInsets
セッターをオーバーライドし、setNeedsLayout
に続いてlayoutIfNeeded
:
- (void) setEdgeInsets:(UIEdgeInsets)edgeInsets {
_edgeInsets = edgeInsets;
[self setNeedsLayout]; //Indicates that the view needs to be laid out
//at next update or at next call of layoutIfNeeded,
//whichever comes first
[self layoutIfNeeded]; //Calls layoutSubviews if flag is set
}
そのようにして、以下を行う場合、アニメーションブロック内のedgeInsetsプロパティを変更すると、contentViewのフレーム変更がアニメーション化されます。
[UIView animateWithDuration:2 animations:^{
customView.edgeInsets = UIEdgeInsetsMake(45, 17, 18, 34);
}];
SetEdgeInsetsメソッドでlayoutIfNeededへの呼び出しを追加しない場合、layoutSubviewsは次の更新サイクルで呼び出されるため、アニメーションは機能しません。これは、アニメーションブロックの外部で呼び出すことに相当します。
SetEdgeInsetsメソッドでlayoutIfNeededのみを呼び出す場合、setNeedsLayoutフラグが設定されていないため、何も起こりません。