自動レイアウト制約を使用しているときに、ビューの現在の幅と高さを取得するにはどうすればよいですか?


108

フレームプロパティについては触れていません。これは、xibでのビューのサイズしか取得できないためです。制約のためにビューのサイズが変更されたときのことです(おそらく回転後、またはイベントに応答して)。現在の幅と高さを取得する方法はありますか?

幅と高さの制約を探すためにその制約を反復してみましたが、それはあまりクリーンではなく、固有の制約がある場合は失敗します(2つを区別できないため)。また、これは実際に幅と高さの制約がある場合にのみ機能し、サイズ変更のために他の制約に依存している場合には機能しません。

なぜこれが私にとってそれほど難しいのですか?ARG!


私は、ビューの境界が特定の時点で現在の幅と高さを保持していると考えていました。これがレイアウトプロセス中に調整されます。
Phillip Mills、

うーん、境界をチェックするつもりはありませんでした。今からやります。
yuf '19

回答:


246

答えは[view layoutIfNeeded]です。

理由は次のとおりです。

あなたはまだ検査することで、ビューの現在の幅と高さを取得view.bounds.size.widthしてview.bounds.size.height(またはあなたが演奏している場合を除き同等であるフレーム、view.transform)。

必要なものが既存の制約によって暗示されている幅と高さである場合、それらを解釈するために自動レイアウトシステムの制約解決ロジック全体を再実装する必要があるため、答えは制約を手動で検査することではありません。制約。代わりに、自動レイアウトにレイアウトの更新を依頼するだけで、制約が解決され、view.boundsの値が正しいソリューションで更新され、view.boundsが検査されます。

自動レイアウトでレイアウトを更新するにはどうすればよいですか?[view setNeedsLayout]自動レイアウトで実行ループの次のターンでレイアウトを更新する場合は、呼び出します。

ただし、レイアウトをすぐに更新して、現在の関数内で後で、または実行ループのターン前の別の時点で新しい境界値すぐにアクセスできるようにする場合は、およびを呼び出す必要が[view setNeedsLayout]あり[view layoutIfNeeded]ます。

2つ目の質問:「高さ/幅の制約を直接参照していない場合、どうすれば高さ/幅の制約を変更できますか?」

IBで制約を作成する場合、最善の解決策は、ビューコントローラーまたはビューでIBOutletを作成して、直接参照できるようにすることです。コードで制約を作成した場合は、それを作成したときに、内部の弱いプロパティの参照を保持する必要があります。他の誰かが制約を作成した場合は、ビューのview.constraintsプロパティ、および場合によってはビュー階層全体を調べ、重要なNSLayoutConstraintを見つけるロジックを実装することによって、制約を見つける必要があります。これはおそらく間違った方法です。その質問に対する単純な答えが保証されていない場合、境界サイズを決定する特定の制約も効果的に決定する必要があるためです。最終的な境界値は、複数の制約の非常に複雑なシステムの解決策になる可能性があります。


16
優れた答えと同様に、あまりにも、説明しました。1、2時間髪を抜いた後、助けてくれました。
ベンクリーガー2013

2
layoutIfNeeded素晴らしいですし、フレームがすぐに利用できるようになります。ただし、それは、それが呼び出されたビューのサブツリー内のすべてのビューに対する制約のレンダリングも強制します。たとえば、プログラムでビューを追加layoutIfNeededし、再帰ルーチンでそれらすべてを呼び出す場合、ビュー階層のレンダリングが非常に遅くなることがあります。(私はこれを難しい方法で学びました)優れた答えで述べたように、「setNeedsLayout」はより効率的であり、理想的には約1/60秒で発生する次のレイアウトパスでフレームを使用可能にします。
shmim 2014

1
私はこれが古いことを知っていますが、このメソッドをどこで呼び出しますか?でinitWithCoder
MayNotBe 2014年

4
境界を取得するためだけにレイアウトを強制するのはなぜですか?これは非効率であるように見え、複雑なインターフェースではこれは潜在的に高価になるでしょう。代わりに、viewDidLayoutSubviewsまたはviewWillLayoutSubviewsから最新の境界にアクセスし、cocoaに独自のレイアウトタイミングを処理させます。値またはフラグにアクセスする必要がある場合はプロパティを設定し、問題がある場合に複数の呼び出しを回避します。
smileBot 2015年

1
はい、実行ループが終了する前に、たとえば現在の関数呼び出しの範囲内で、自動レイアウト計算された値にアクセスする必要がある場合にのみ、レイアウトを強制する必要があります。
藻類

8

同様の問題がありUITableView、での制約設定に基づいてサイズ変更するに上部と下部の境界を追加する必要がありましたUIStoryboard。で更新された制約にアクセスできました- (void)viewDidLayoutSubviews。これは、ビューをサブクラス化してそのレイアウトメソッドをオーバーライドする必要がないので便利です。

/*** SET TOP AND BOTTOM BORDERS ON TABLE VIEW ***/
- (void)addBorders
{
    CALayer *topBorder           = [CALayer layer];
    topBorder.frame              = CGRectMake(0.0f, self.tableView.frame.origin.y, 320.0f, 0.5f);
    topBorder.backgroundColor    = [UIColor redColor].CGColor;

    CALayer *bottomBorder        = [CALayer layer];
    bottomBorder.frame           = CGRectMake(0.0f, (self.tableView.frame.origin.y + self.tableView.frame.size.height), 320.0f, 0.5f);
    bottomBorder.backgroundColor = [UIColor redColor].CGColor;

    [self.view.layer addSublayer:topBorder];
    [self.view.layer addSublayer:bottomBorder];
}

/*** GET AUTORESIZED FRAME DIMENSIONS ***/
- (void)viewDidLayoutSubviews{
    [self addBorders];
}

メソッドからメソッドを呼び出さない場合viewDidLayoutSubview、下の境界線が画面の外にあるため、上の境界線のみが正しく描画されます。


3
viewDidLayoutSubviewsが数回呼び出され、大量のレイヤーを作成しています... 別のレイヤーを追加する前に、存在チェックを追加する必要があります。
カルゼム2016年

何年かコメント何か前にオーバーライドする際後半...あなたもスーパークラスでこのメソッドを呼び出す必要があります:[super viewDidLayoutSubviews];
アレハンドロイバン

5

特にTableviewCellで、まだこのような問題に直面している可能性があるユーザー向けです。

メソッドをオーバーライドするだけです:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

UITableViewCellまたはUICollectionViewCellの場合は、セルのサブクラスを作成し、同じメソッドをオーバーライドします。

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

これが私のビューの実際の最終サイズを取得できる唯一の方法です
Benoit Jadinon

これは、aviewFromNibで取得する前にサブビューに実際にサイズを変更する時間を与えなかったため、制限付きビューの最終的なサイズを取得できる唯一の方法でした。ありがとうございました!
Azin Mehrnoosh

3

使用-(void)viewWillAppear:(BOOL)animatedして呼び出す [self.view layoutIfNeeded];-私が試した動作します。

使用-(void)viewDidLayoutSubviewsすると確実に機能しますが、このメソッドは、UIが更新/変更を要求するたびに呼び出されます。これは管理が難しくなります。ソフトキーは、このような呼び出しのループを回避するためにブール変数を使用することです。より良い使用viewWillAppear。覚えておいてくださいviewWillAppearビューが再びロードされている場合にも(再割り当てなし)と呼ばれます。


2

フレームはまだ有効です。最後に、ビューはフレームプロパティを使用してレイアウトします。すべての制約に基づいてそのフレームを計算します。制約は、初期レイアウトにのみ使用されます(そして、レイアウトサブビューが回転後などにビューで呼び出されるときはいつでも)。その後、位置情報はフレームプロパティにあります。それとも別の方法で見ていますか?


1
それ以外は見ています。幅/高さの制約を直接変更した後でも(それらの定数を変更して、フレームの値は変更されません。xibでIBOutletを使用しています)
yuf

制約を変更し、同じ関数でフレームを使用する必要があるため、私の問題は独特です。setNeedsLayoutを呼び出しても、すぐには更新されません。最初にレイアウトを待ってからフレームを使用する必要があると思います。私の2番目の質問は、高さ/幅の制約を直接参照していない場合、どうすれば変更できるのですか?
yuf

1
次に、dispatch_asyncを実行する必要があります。これにより、次のフレームまでコードを遅延させることができます。2番目の部分については...わからない...すべての制約を繰り返し、どれが適用可能かを探す必要があります... IBOutletsの方がはるかに簡単です。
12

IBOutletsの場合はTrueですが、操作するIBがないUIViewのカテゴリに関数を記述したいと思います。
yuf '19
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.