web-dev-qa-db-ja.com

UIViewControllerのビューの正しい境界の取得

IPadアプリケーションがあります。横向きでは、UIViewControllerのビューの実際の幅= 1024pxおよび高さ= 768-20(statusBar)-44(navigationBar)= 704px

したがって、この[1024 x 704]サイズを取得し、self.view.boundsを使用します。 [748 x 1024]を返しますが、これは間違っています!しかし、画面を2回回転させると(現在->縦->現在)、ビューの境界は正しい-[1024 x 704]になります。

ビューは次のように初期化されました。

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    self.view.backgroundColor = [UIColor lightGrayColor];
}

そして、境界は次のようになりました。

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}

だから質問は..最初に正しいビューの境界を取得するにはどうすればよいですか?

41
demon9733

他のいくつかの答えによると、あなたが見ている問題は、viewDidLoadbeforeと呼ばれているためです。 iPadは常にポートレートモードで初期化されるため、viewDidLoadでサイズ値を取得すると、それらは常にポートレートサイズになります-これは、設定した向きに関係ありません。

サイズを取得するには、方向/回転が発生し、viewDidAppearのサイズ値を取得します。


特に、プロジェクト設定やXcode Interface Builderでオリエンテーションを定義している場合、iOSがこれをうまく処理できない理由は特にわかりません。しかし、私には十分な理由があると確信しています;-)。

33
RichS

これを正しく行う方法

UIViewControllerサブクラスは、メソッドviewWillLayoutSubviewsこちらも参照 をオーバーライドする必要があります。

このメソッドが呼び出されると、viewControllerのviewは正しいサイズになり、レイアウトがサブビューに渡される前に、サブビューに必要な調整を加えることができます。

Swift

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    NSLog("bounds = \(self.view.bounds)")
}

Obj-C

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}

ドキュメンテーション

ビューの境界が変更されると、ビューはそのサブビューの位置を調整します。ビューがサブビューをレイアウトする前に、View Controllerでこのメソッドをオーバーライドして変更を加えることができます。このメソッドのデフォルト実装は何もしません。

強調された部分からわかるように、このメソッドはevery timeと呼ばれます。ViewControllerのビューは、ビューが最初に表示されるときだけでなく、サイズを変更します。これにより、回転や他の境界変更イベントに正しく応答できます

機能しないいくつかの方法

他の回答で提案されているいくつかのアプローチはうまく機能しないか、深刻な欠点があります。残念ながら、これには選択した回答が含まれます。これらのアプローチを避けることをお勧めします。それらを調べて、使用しない理由を説明します。しないでください。

  • viewDidLoadおよびviewWillAppear –これらの呼び出し中、viewの最終サイズはまだありません。 あなたが誤解するように、あなたが得るサイズは純粋な偶然によってのみ正しいでしょう。
  • viewDidAppear –これは遅すぎます。 ビューはすでに画面上にあり、ユーザーに表示されます。ここで変更を加えると、目に見える変更/突然のグリッチが発生し、アマチュアに見えます。もう一度お願いします–あなたのために、私のために、everyone'sのために:しないでください!あなたはそれよりも優れており、ユーザーも同様です。
  • UIScreen.mainScreen.bounds.size –これはextremely低レベルです。 UIViewControllerを実装しており、ビューのサイズは、ネストされているコントローラー(ナビゲーション、タブ、ページング、カスタムコントローラーなど)、デバイスの回転方法、および潜在的に、マルチタスクのために画面が分割されました。 mightこれらすべてを補正し、ビューの最終サイズを計算できる一方で、Appleが決定すると簡単に壊れる複雑で脆弱なコードになります。これらのメトリックのいずれかを変更します。 UIViewControllerをオーバーライドするだけで、viewWillLayoutSubviewsがこれをすべて行います。

これらの問題のあるアプローチは、正しい情報を提供しないこと以外は、自動回転や、マルチタスクジェスチャなど、View Controllerのビューのサイズを変更する他のイベントには役立ちません。これは本当にスムーズに処理したいものです。

お願い:チャンピオンになりましょう。正しい方法でやってください。 viewWillLayoutSubviewsを使用します。実装は、サイズの変更ごとに呼び出され、ユーザー、将来の自己、チームメンバー、そして私はそれを祝福します。ブラボー!

さらなるヒント

viewWillLayoutSubviewsが呼び出されると、唯一のビュー最終サイズにサイズ変更される階層はviewController.viewです。これに対するギブアウェイは、メソッドの名前です。 view…(View Controllerのルートview…WillLayout…(本当にすぐに、しかし発生していませんまだ…Subviews(ルートビューの下の階層のその他すべて)。

したがって、サブビューのレイアウトはまだ行われていません。 Everyルートの下の子ありませんまだ有効なfinalサイズがあります。子ビューからクエリするサイズ情報はすべて、せいぜい完全に間違っています。

より可能性が高い、そして非常に悪いことです、それは誤解を招くほど正しいでしょう

このhappensは、このサイズと向きのデフォルト、またはストーリーボードの設定のために、あなたが期待し必要としているものです。しかし、これは偶然であり、さまざまなデバイスのサイズや向きで信頼できるものではありません。

特定のサブビューのサイズが変更されたときに通知を受け、その正確な最終サイズを知る必要がある場合は、通常、その特定のlayoutSubviewsサブクラスのUIViewメソッドをオーバーライドする必要があります。

27
Benjohn

私はいつも使用しています:

CGSize sizeOfScreen = [[UIScreen mainScreen] bounds].size;

画面のサイズを取得するため、および:

CGSize sizeOfView = self.view.bounds.size;

viewのサイズを取得します。私はちょうどviewDidLoadでそれをテストし、それが返されました:

2012-07-17 10:25:46.562 Project[3904:15203] bounds = {768, 1004}

CGSizeは{Width、Height}として定義されているため、これは正しいです。

13
luksfarris

これは私の最後のプロジェクトで見つけたものです:self.viewのフレームは、この画面にナビゲーションバーなどがあるかどうかに応じてviewDidLoadの後に調整されます.

そのため、viewDidLoadの後に(おそらくviewWillAppearまたはviewDidAppearで)その値を使用するか、バーの高さを差し引いて手動で調整します。

2
Selkie

この問題にぶつかり、viewDidAppearの境界を取得することが私のニーズに合っていることがわかりました。

2
Dustin Kendall