layoutSubviewsはいつ呼び出されますか?


264

layoutSubviewアニメーション中にメッセージが表示されないカスタムビューがあります。

画面いっぱいにビューがあります。画面の下部にカスタムサブビューがあり、ナビゲーションバーの高さを変更した場合、Interface Builderでサイズが正しく変更されます。layoutSubviewsビューが作成されたときに呼び出されますが、二度と呼び出されません。私のサブビューは正しくレイアウトされています。呼び出し中のステータスバーをオフに切り替えるlayoutSubviewsと、メインビューがサイズ変更をアニメーション化しても、サブビューが呼び出されません。

layoutSubviews実際にはどのような状況で呼ばれていますか?

私がしているautoresizesSubviewsに設定NO私のカスタムビューのために。そして、Interface Builderには、上部と下部の支柱と垂直矢印のセットがあります。


パズルのもう1つの部分は、ウィンドウをキーにする必要があることです。

[window makeKeyAndVisible];

それ以外の場合、サブビューは自動的にサイズ変更されません。

回答:


492

同様の質問がありましたが、答え(またはネットで見つけたもの)に満足できなかったので、実際に試してみました。

  • initlayoutSubviews呼ばれることはありません(duh)
  • addSubview:原因 layoutSubviews追加されているビューに呼び出されるが、ビューはそれが(ターゲット・ビュー)に加え、対象のすべてのサブビューされています
  • ビューは、フレームのサイズパラメータが異なる場合にのみ、フレームが設定されたビューをsetFrame インテリジェントに呼び出しlayoutSubviewsます
  • UIScrollViewをスクロールするlayoutSubviewsと、scrollViewとそのスーパービューで呼び出されます
  • デバイスを回転させるlayoutSubviewと、親ビュー(応答するviewControllersプライマリビュー)のみが呼び出さ れます。
  • ビューのサイズを変更するとlayoutSubviews、そのスーパービューが呼び出されます

私の結果-http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/


1
すばらしい答えです。私はいつも疑問に思いましたlayoutSubviews。呼び出されるinitWithFrame:原因はありlayoutSubviewsますか?
ロバート

2
@Robert-私はinitWithFrameを使用していたので...いいえ。
BadPirate

8
@BadPirate:はい。あなたはサイズを変更場合、私の実験によると、view1.1それが呼び出したlayoutSubviewsview1と、その後layoutSubviewsview1.1。この呼び出しは、上でそれを呼び出して、superviewsに無限に伝播していないview1.1.1だけで通話layoutSubviewsview1.1view1.1.1。サイズを変更せずに移動するだけlayoutSubviewsでは、それらのいずれも呼び出されません。
ジョアン・ポルテラ

1
viewDidLoadは、UIViewでは呼び出されません(UIViewControllerで呼び出されます)。UIViewが初期化された後、View Didロードが呼び出されます。
BadPirate 2013年

2
私の実験に基づいて、二番目のルールが正確でない場合があります、私は追加するときview1.2view1layoutSubviewsview1.2view1呼ばれているが、layoutSubviewsview1.1と呼ばれていません。(view1.1およびのview1.2サブビューですview1)。つまり、ターゲットビューのすべてのサブビューがlayoutSubviewsmethod と呼ばれるわけではありません
HongchaoZhang

96

@BadPirateによる以前の回答に基づいて、私はもう少し実験し、いくつかの明確化/修正を思い付きました。次のlayoutSubviews:場合にのみ、ビューで呼び出されることがわかりました。

  • 独自の境界(フレームではない)が変更されました。
  • 直接サブビューの1つの境界が変更されました。
  • サブビューがビューに追加されるか、ビューから削除されます。

いくつかの関連する詳細:

  • 境界は、新しい値が、異なっている場合にのみ、変更とみなされ、異なる起源を含みますlayoutSubviews:境界の原点を変更してスクロールを実行するため、UIScrollViewがスクロールするたびにが呼び出されるのはそのためです。
  • これがboundsプロパティに伝達される唯一のものであるため、フレームを変更しても、サイズが変更された場合にのみ境界が変更されます。
  • まだビュー階層にないビューの境界が変更さlayoutSubviews: れると、ビューが最終的にビュー階層に追加されるときにが呼び出されます
  • そして、完全を期すために、これらのトリガーは、layoutSubviewsを直接呼び出すのではなくsetNeedsLayout、フラグを設定/レイズするを呼び出します。実行ループの反復ごとに、ビュー階層内のすべてのビューについて、このフラグがチェックされます。フラグが立てられていることが判明したビューごとに、 layoutSubviews:が呼び出され、フラグがリセットされます。階層の上位のビューが最初にチェック/呼び出されます。

5
この答えは十分に賛成できません。それがトップの答えになるはずです。与えられた3つのルールはあなたが必要とするすべてです。これらのルールが完全に記述していないlayoutSubviewの動作に遭遇したことはありません。
ペルセルク

1
直接サブビューの境界が変更されたときに、layoutSubviewsが呼び出されるとは思いません。layoutSubviewsの呼び出しは、テストの副作用にすぎないと思います。サブビューの境界が変更されたときに、layoutSubviewsが呼び出されない場合があります。彼のアンサーは単なる実験ではなくAppleのドキュメントに基づいているため、frogcjnの回答を確認してください。
Simon Backx 2018年

19

https://developer.apple.com/library/prerelease/tvos/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/CreatingViews/CreatingViews.html#//apple_ref/doc/uid/TP40009503-CH5-SW1

レイアウトの変更は、次のイベントのいずれかがビューで発生するたびに発生する可能性があります。

a。ビューの境界の長方形のサイズが変更されます。
b。インターフェースの向きの変更が発生すると、通常、ルートビューの境界の長方形が変更されます。
c。ビューのレイヤーに関連付けられた一連のコアアニメーションサブレイヤーが変更され、レイアウトが必要になります。
d。アプリケーション は、ビューのsetNeedsLayout または  layoutIfNeededメソッドを呼び出すことにより、レイアウトを強制  的に実行します。
e。アプリケーションsetNeedsLayout は、ビューの基になるレイヤーオブジェクトのメソッドを呼び出して、レイアウトを強制し  ます。


また、重要なのは、ビューがビュースタックにまだ追加されていない場合、これらのイベントは呼び出されないことです。これには「can」という単語を含めますが、具体的には、アプリケーションのメインスレッドの次の利用可能なサイクルで、これらのイベントのいずれかによってレイアウトが必要であるとマークされているときに呼び出されます。
user1122069

13

BadPirateの回答のポイントの一部は、部分的にしか当てはまりません。

  1. addSubViewポイントについて

    addSubview 追加されるビュー、追加先のビュー(ターゲットビュー)、およびターゲットのすべてのサブビューで、layoutSubviewsが呼び出されます。

    これは、ビュー(ターゲットビュー)の自動サイズ変更マスクに依存します。自動サイズ変更マスクがオンの場合、layoutSubviewがそれぞれで呼び出されますaddSubview。自動サイズ変更マスクがない場合、ビューの(ターゲットビュー)フレームサイズが変更されたときにのみ、layoutSubviewが呼び出されます。

    例:UIViewをプログラムで作成した場合(デフォルトでは自動サイズ変更マスクはありません)、UIViewフレームがすべてで変更されない場合にのみ、LayoutSubviewが呼び出されますaddSubview

    この手法により、アプリケーションのパフォーマンスも向上します。

  2. デバイスの回転ポイント

    デバイスを回転すると、親ビュー(応答するviewControllerのプライマリビュー)でlayoutSubviewのみが呼び出されます。

    これは、VCがVC階層(ルートwindow.rootViewController)にある場合にのみ当てはまりますが、これは最も一般的なケースです。iOS 5では、VCを作成しても、それが別のVCに追加されていない場合、デバイスが回転してもこのVCは通知されません。したがって、そのビューは、layoutSubviewsを呼び出しても認識されません。


9

ソリューションを追跡して、シミュレートされた画面要素がオンになっているビュー(ステータスバーなど)でスプリングを変更できないというInterface Builderの主張まで追跡しました。メインビューではスプリングがオフになっているため、そのビューはサイズを変更できず、通話中バーが表示されたときに全体が下にスクロールされていました。

シミュレーション機能をオフにしてから、ビューのサイズを変更してスプリングを正しく設定すると、アニメーションが発生し、メソッドが呼び出されました。

これをデバッグする際の追加の問題は、メニューを使用して通話中のステータスを切り替えると、シミュレーターがアプリを終了することです。アプリを終了=デバッガーなし。


ビューのサイズが変更されると、layoutSubviewsが呼び出されるということですか?私はいつもそれがそうではないと思っていました...
Andrey Tarantsov 09年

そうです。ビューのサイズが変更されると、そのサブビューで何かを行う必要があります。あなたはそれを提供しない場合は、自動的にストラットなど、スプリングを使用してそれらを動かす
スティーブ・ウェラー

8

呼び出し [self.view setNeedsLayout]; のViewControllerにすることは、viewDidLayoutSubviewsを呼び出すようになります


5

あなたはlayoutIfNeededを見ましたか?

ドキュメントスニペットは以下のとおりです。アニメーション中にこのメソッドを明示的に呼び出した場合、アニメーションは機能しますか?

layoutIfNeeded必要に応じてサブビューをレイアウトします。

- (void)layoutIfNeeded

説明このメソッドを使用して、描画する前にサブビューのレイアウトを強制します。

iPhone OS 2.0以降で利用できます。


2

OpenGLアプリをSDK 3から4に移行すると、layoutSubviewsは呼び出されなくなりました。試行錯誤の末、MainWindow.xibを最後に開いて、ウィンドウオブジェクトを選択し、インスペクターで[ウィンドウの属性]タブ(左端)を選択して、[起動時に表示]をオンにしました。SDK 3では、それでもlayoutSubViews呼び出しを引き起こすために使用されていたようですが、4ではそうではありません。

6時間の欲求不満は終わりを告げました。


ウィンドウキーを作りましたか?そうでなければ、これはあらゆる種類の興味深い事柄が起こらない原因となる可能性があります。
スティーブウェラー

-2

どちらかと言えばlayoutSubviews呼び出されない場合の、あいまいであるが潜在的に重要なケースは次のとおりです。

import UIKit

class View: UIView {

    override class var layerClass: AnyClass { return Layer.self }

    class Layer: CALayer {
        override func layoutSublayers() {
            // if we don't call super.layoutSublayers()...
            print(type(of: self), #function)
        }
    }

    override func layoutSubviews() {
        // ... this method never gets called by the OS!
        print(type(of: self), #function)
    }
}

let view = View(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

1
なぜこの答えがここにあるのですか?
ドミニクブッチャー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.