UIViewのsetNeedsLayout、layoutIfNeeded、layoutSubviewsの関係は何ですか?


105

とメソッドの関係について明確な説明UIView's setNeedsLayoutlayoutIfNeededありlayoutSubviewsますか?そして、3つすべてが使用される実装例。ありがとう。

混乱しているのは、カスタムビューにsetNeedsLayoutメッセージを送信するとlayoutSubviews、このメソッドの後に呼び出される次のメッセージがスキップされてlayoutIfNeededしまうことです。ドキュメントから、フローがsetNeedsLayout> layoutIfNeeded呼び出される原因>呼び出される原因となることが予想layoutSubviewsされます。

回答:


104

私はまだ自分でこれを理解しようとしているので、これを懐疑的に受け止め、エラーが含まれている場合は許してください。

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、すぐにレイアウトされるように(必要に応じて、そのsuperviews)、それを強制的にビューに。


2
ありがとう-誰かが最終的にこれに答えてうれしいです。その間、私はまた、setNeedsDisplay(drawRectが呼び出される原因)とcontentModeについても詳しく調べる必要がありました。contentMode = UIViewContentModeRedrawの場合のみ、ビューの境界が変更されたときにsetNeedsDisplayが呼び出されます。他のcontentModeの選択は、ビューのスケーリング、ある程度のシフト、またはエッジへの整列のみを引き起こします。私のカスタムUITableViewCellは、向きの変更時に再描画したくありませんでした。contentModeをUIViewContentModeRedrawに設定するまで、それはスケーリングのみです。
ターファ

コード内の[self.subview1 layoutSubviews]は[self.subview1 setNeedsLayout]に置き換える必要があると思います。layoutSubviewsはオーバーライドされることを意図していますが、別のビューから、または別のビューに呼び出されることを意図していません。フレームワークがそれを呼び出すタイミングを決定する(効率化のために複数のレイアウト要求を1つの呼び出しにグループ化することによって)か、ビュー階層のどこかでlayoutIfNeededを呼び出すことによって間接的に行います。
ターファ

上記の私のコメントの修正:「... layoutSubviewsはオーバーライドされるか、自分から呼び出されることを意図していますが、別のビューから呼び出されることを意図していない...」
Tarfa

本当にわからないです[subview1 layoutSubviews]。のようにdrawRect、直接呼び出すことは想定されていません。しかしsetNeedsLayout、レイアウトフェーズ中に呼び出すと、ビューが同じレイアウトフェーズ中にレイアウトされるのか、それとも次のフェーズまで遅延するのかはわかりません。これがすべてどのように機能するかを本当に理解している誰かからの回答を見るのは素晴らしいことです...
n8gray 2010年

34

私はいくつかのケースでは、あなたが呼び出す必要がありますことをn8grayの答えに追加したいsetNeedsLayoutが続きますlayoutIfNeeded

たとえば、サブビューの配置が複雑で、autoresizingMaskまたはiOS6 AutoLayoutでは実行できない、UIViewを拡張するカスタムビューを作成したとします。カスタムポジショニングは、をオーバーライドすることで実行できますlayoutSubviews

例として、contentViewプロパティとedgeInsetscontentViewの周囲のマージンを設定できるプロパティを持つカスタムビューがあるとします。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フラグが設定されていないため、何も起こりません。


4
または[self layoutIfNeeded]、エッジインセットを設定した後、アニメーションブロック内で呼び出すことができるはずです。
スコットフィスター2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.