web-dev-qa-db-ja.com

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

アニメーション中にlayoutSubviewメッセージを受け取らないカスタムビューがあります。

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

layoutSubviewsはどのような状況で実際に呼び出されますか?

カスタムビューのautoresizesSubviewsNOに設定しています。 Interface Builderには、上下の支柱と垂直矢印が設定されています。


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

[window makeKeyAndVisible];

その他のサブビューは自動的にサイズ変更されません。

254
Steve Weller

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

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

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

7
Steve Weller

私は同様の質問をしましたが、答え(またはネットで見つけることができるもの)に満足していなかったので、実際に試しましたが、ここに私が得たものがあります:

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

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

466
BadPirate

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

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

関連する詳細:

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

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。ビューのレイヤーに関連付けられているCore Animationサブレイヤーのセットが変更され、レイアウトが必要になります。
d。ビューのsetNeedsLayoutまたは_layoutIfNeededメソッドを呼び出すことにより、アプリケーションはレイアウトを強制的に実行します。
e。アプリケーションは、ビューの基になるレイヤーオブジェクトのsetNeedsLayoutメソッドを呼び出してレイアウトを強制します。

17
frogcjn

BadPirate's answer のポイントの一部は、部分的にのみ真です:

  1. addSubViewポイントの場合

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

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

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

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

  2. デバイスの回転点

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

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

9
Mohit Nigam

viewControllerで[self.view setNeedsLayout];を呼び出すと、viewDidLayoutSubviewsが呼び出されます

7
bademi

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

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

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

- (void)layoutIfNeeded

ディスカッションこのメソッドを使用して、描画前にサブビューのレイアウトを強制します。

可用性iPhone OS 2.0以降で使用できます。

4

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

6時間のフラストレーションが終わりました。

2
Tal Yaniv

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

[window makeKeyAndVisible];

その他のサブビューは自動的にサイズ変更されません。

0
Steve Weller

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))
0
milos