iPhone X以降では画面のアスペクトレシオが 2:1 の横長モデルも発売され、SafeAreaという概念が登場しました。四隅は角丸、上下左右のマージン、ステータスバー、ホームインジケータ等々を考慮する必要が出てきました。
Xcode 11.4
SafeArea
SafeAreaを考慮せずに放っておいて実際に困ることは、ステータスバーやホームインジケータにボタンや文字が被りユーザーからは使いにくいアプリとなってしまいます。
Appleもそこは適切な対応を求めています。特に広告は致命的ですね。
safeAreaInsets
Storyboardで設定する方法は他にありますが、ここでは動的にコードでSafeAreaの取得と画面回転のケースを想定してみたいと思います。
SafeArea上下左右は safeAreaInsets から取得することができます。ただしこれは viewDidLoadでは受け取れず、その後の viewDidAppear で受け取れます。(この前でも取れるようですが)
PortraitではSafeAreaと画面に上に44.0, 下に34.0の余白があります。
例)iPhone X
また、画面が回転するときは
viewSafeAreaInsetsDidChange()で回転後のサイズを取り出せますが
willAnimateRotation()でも可能です。
サンプルコード
これらのSafeAreaを計算するコードです。
ViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
import UIKit class ViewController: UIViewController { var image = UIImage() var imageView = UIImageView() var label = UILabel() var topPadding:CGFloat = 0 var bottomPadding:CGFloat = 0 var leftPadding:CGFloat = 0 var rightPadding:CGFloat = 0 override func viewDidLoad() { super.viewDidLoad() image = UIImage(named: "img")! imageView.image = image } // viewDidLoad()ではsafeAreaInsetsを拾えない // 画面回転にも対応する override func viewDidAppear(_ animated: Bool) { NotificationCenter.default.addObserver(self, selector: #selector(self.checkSafeArea(notification:)), name: UIDevice.orientationDidChangeNotification, object: nil) } @objc func checkSafeArea(notification: NSNotification){ // 画面の横幅を取得 // 以降、Landscape のみを想定 let screenWidth:CGFloat = view.frame.size.width let screenHeight:CGFloat = view.frame.size.height if #available(iOS 13, *) { let window = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .map({$0 as? UIWindowScene}) .compactMap({$0}) .first? .windows .filter({$0.isKeyWindow}) .first topPadding = window!.safeAreaInsets.top bottomPadding = window!.safeAreaInsets.bottom leftPadding = window!.safeAreaInsets.left rightPadding = window!.safeAreaInsets.right print("topPadding = \(topPadding)") print("bottomPadding = \(bottomPadding)") print("leftPadding = \(leftPadding)") print("rightPadding = \(rightPadding)") } // portrait var safeAreaWidth = screenWidth - leftPadding - rightPadding var safeAreaHeight = (screenHeight) - topPadding - bottomPadding // landscape if(screenWidth > screenHeight){ safeAreaWidth = screenWidth - leftPadding - rightPadding safeAreaHeight = (screenHeight) - topPadding - bottomPadding } print("safeAreaWidth = \(safeAreaWidth)") print("screenHeight = \(screenHeight)") let rect = CGRect(x: leftPadding, y: topPadding, width: safeAreaWidth, height: safeAreaHeight) // frame をCGRectで作った矩形に合わせる imageView.frame = rect // view に ImageView を追加する self.view.addSubview(imageView) // ラベルのサイズを設定 label.frame = CGRect(x:5, y:100, width:200, height:300) // ラベルの文字を設定 let str = "safeAreaWidth = " + safeAreaWidth.description + "\n" + "safeAreaHeight = " + safeAreaHeight.description + "\n\n" + "topPadding = " + topPadding.description + "\n" + "bottomPadding = " + bottomPadding.description + "\n" + "leftPadding = " + leftPadding.description + "\n" + "rightPadding = " + rightPadding.description + "\n" label.text = str // 最大行まで行数表示できるようにする label.numberOfLines = 0 // center寄せ label.textAlignment = NSTextAlignment.center label.center = CGPoint(x: safeAreaWidth/2, y:safeAreaHeight/2) // Viewにラベルを追加 self.view.addSubview(label) } } |
サンプル動画
画面を傾けて、あるいはSimulatorのDeviceから画面を回転させてください。
References:
safeAreaInsets – UIView | Apple Developer Documentation
Positioning Content Relative to the Safe Area | Apple Developer
UIEdgeInsets – UIKit | Apple Developer Documentation
iPhone X用にアプリケーションをアップデートする – iOS – Apple Developer
iPhone X – Overview – iOS Human Interface Guidelines