はじめに
iPhone X向けのレイアウト対応のために、Safe Areaをプログラムで取得して、ビューの位置を計算しようとした時の話です。
Safe Areaについてはこちらの記事がとてもわかりやすかったです。
UIViewクラスのsafeAreaInsetsプロパティ
iOS11からUIView
クラスにsafeAreaInsets
プロパティが追加され、このプロパティでSafe Areaの上下マージンを取得することができます。
ただし、API Referenceによると、ビューがビューヒエラルキーに追加されるまで、このプロパティの値はzeroになると書かれています。これは実際にどういうことか、UIViewController
のself.view
を使って調べてみました。
検証コードは以下のとおりです。UIViewController
の表示が完了するまでのライフサイクルごとにself.view.safeAreaInsets
の値をログ出力しています。
#import "ViewController.h"
#define SAFE_AREA_LOG(insets) NSLog(@"%s T:%.1f R:%.1f B:%.1f L:%.1f", __FUNCTION__, insets.top, insets.right, insets.bottom, insets.left)
@implementation ViewController
- (void)loadView {
[super loadView];
SAFE_AREA_LOG(self.view.safeAreaInsets);
}
- (void)viewDidLoad {
[super viewDidLoad];
SAFE_AREA_LOG(self.view.safeAreaInsets);
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
SAFE_AREA_LOG(self.view.safeAreaInsets);
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
SAFE_AREA_LOG(self.view.safeAreaInsets);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
SAFE_AREA_LOG(self.view.safeAreaInsets);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
SAFE_AREA_LOG(self.view.safeAreaInsets);
}
@end
このコードをiPhone Xシミュレータで実行した時のログ出力結果は以下のとおりです。
-[ViewController loadView] T:0.0 R:0.0 B:0.0 L:0.0
-[ViewController viewDidLoad] T:0.0 R:0.0 B:0.0 L:0.0
-[ViewController viewWillAppear:] T:0.0 R:0.0 B:0.0 L:0.0
-[ViewController viewWillLayoutSubviews] T:44.0 R:0.0 B:34.0 L:0.0
-[ViewController viewDidLayoutSubviews] T:44.0 R:0.0 B:34.0 L:0.0
-[ViewController viewWillLayoutSubviews] T:44.0 R:0.0 B:34.0 L:0.0
-[ViewController viewDidLayoutSubviews] T:44.0 R:0.0 B:34.0 L:0.0
-[ViewController viewDidAppear:] T:44.0 R:0.0 B:34.0 L:0.0
viewWillAppear
まではzeroでした。viewDidLoad
でビューの位置計算をしたいっ、と思ったのですが、このタイミングではダメなようです。
結局のところ、今回は、viewDidLoad
で計算したかったこともあり、上マージン44と下マージン34をハードコードして対応してしまいました(イマイチ感あり)。
もう少し上手いやり方がないかは今後調べてみようと思います。
追記)
UIView#layoutSubviews
メソッド内でself.safeAreaInsets
を使うことでSafe Areaのマージンを取得することができました。位置計算はUIView
のlayoutSubviews
かUIViewController
のviewWillLayoutSubviews
で行うのが良さそうです。
ちなみに、画面回転させてLandscapeにしても、Top:0 Left:44 Bottom:21 Right:44のようにちゃんと回転後の値を取得できました。