Edited at

【iOS11】safeAreaInsetsの値が取得できるタイミング


はじめに

iPhone X向けのレイアウト対応のために、Safe Areaをプログラムで取得して、ビューの位置を計算しようとした時の話です。

Safe Areaについてはこちらの記事がとてもわかりやすかったです。


UIViewクラスのsafeAreaInsetsプロパティ

iOS11からUIViewクラスにsafeAreaInsetsプロパティが追加され、このプロパティでSafe Areaの上下マージンを取得することができます。

ただし、API Referenceによると、ビューがビューヒエラルキーに追加されるまで、このプロパティの値はzeroになると書かれています。これは実際にどういうことか、UIViewControllerself.viewを使って調べてみました。

検証コードは以下のとおりです。UIViewControllerの表示が完了するまでのライフサイクルごとにself.view.safeAreaInsetsの値をログ出力しています。


ViewController.m


#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のマージンを取得することができました。位置計算はUIViewlayoutSubviewsUIViewControllerviewWillLayoutSubviewsで行うのが良さそうです。

ちなみに、画面回転させてLandscapeにしても、Top:0 Left:44 Bottom:21 Right:44のようにちゃんと回転後の値を取得できました。


参考