回答:
これを試して :
Objective C
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
}
ではスウィフト
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let bottomPadding = window?.safeAreaInsets.bottom
}
keyWindow
はいつもnil
だったので、に変更してオプションのチェーンからwindows[0]
を削除しました?
が、うまくいきました。
あなただけのレイアウトガイド間の高さを取得するには
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
だからfull frame height = 812.0
、safe area height = 734.0
以下は、緑のビューのフレームがある例です guide.layoutFrame
UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame
していますが、これにはセーフフレームがあります。
スイフト4、5
制約を使用してビューをセーフエリアアンカーに固定するには、ビューコントローラーのライフサイクルのどこでも行うことができます。これらはAPIによってキューに入れられ、ビューがメモリに読み込まれた後に処理されます。ただし、安全領域の値を取得するには、のように、ビューコントローラのライフサイクルの終わりまで待つ必要がありますviewDidLayoutSubviews()
。
これは、任意のビューコントローラーに接続します。
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = view.safeAreaInsets.top
bottomSafeArea = view.safeAreaInsets.bottom
} else {
topSafeArea = topLayoutGuide.length
bottomSafeArea = bottomLayoutGuide.length
}
// safe area values are now available to use
}
この方法は、可能な場合はウィンドウから離すよりもこの方法を好みます。APIが設計された方法であり、さらに重要なのは、デバイスの向きの変更など、すべてのビューの変更中に値が更新されるためです。
ただし、一部のカスタム表示ビューコントローラーは上記の方法を使用できません(一時的なコンテナービューにあるためと思われます)。このような場合、ルートビューコントローラーから値を取得できます。ルートビューコントローラーは、現在のビューコントローラーのライフサイクルのどこでも常に使用できます。
anyLifecycleMethod()
guard let root = UIApplication.shared.keyWindow?.rootViewController else {
return
}
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = root.view.safeAreaInsets.top
bottomSafeArea = root.view.safeAreaInsets.bottom
} else {
topSafeArea = root.topLayoutGuide.length
bottomSafeArea = root.bottomLayoutGuide.length
}
// safe area values are now available to use
}
viewDidLayoutSubviews
(複数回呼び出すことができます)、ここにおいても動作するソリューションですviewDidLoad
:stackoverflow.com/a/53864017/7767664は
ここでの他の答えはどれも私にとってはうまくいきませんでしたが、これはうまくいきました。
var topSafeAreaHeight: CGFloat = 0
var bottomSafeAreaHeight: CGFloat = 0
if #available(iOS 11.0, *) {
let window = UIApplication.shared.windows[0]
let safeFrame = window.safeAreaLayoutGuide.layoutFrame
topSafeAreaHeight = safeFrame.minY
bottomSafeAreaHeight = window.frame.maxY - safeFrame.maxY
}
ここでの回答はすべて役に立ちました。助けてくれた皆さんに感謝します。
ただし、安全領域のトピックは少し混乱しているため、十分に文書化されていないようです。
私は理解しやすいようにできるだけドロドロとして、ここでそれを要約しますsafeAreaInsets
、safeAreaLayoutGuide
とLayoutGuide
。
iOS 7では、AppleがtopLayoutGuide
とbottomLayoutGuide
プロパティを導入しUIViewController
、ステータス、ナビゲーション、タブバーなどのUIKitバーによってコンテンツが非表示にならないようにするための制約を作成できるようになりました。これらのレイアウトガイドを使用して、コンテンツに対する制約を指定し、それを回避することができました。上部または下部のナビゲーション要素(UIKitバー、ステータスバー、ナビゲーション、タブバーなど)で非表示にする。
したがって、たとえば、tableViewをトップ画面から開始する場合は、次のようにします。
self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)
iOS 11では、Appleはこれらのプロパティを廃止し、単一のセーフエリアレイアウトガイドに置き換えました
アップルによる安全領域
安全な領域は、インターフェース全体の可視部分にビューを配置するのに役立ちます。UIKit定義のビューコントローラーは、コンテンツの上に特別なビューを配置する場合があります。たとえば、ナビゲーションコントローラーは、基になるビューコントローラーのコンテンツの上にナビゲーションバーを表示します。このようなビューが部分的に透明であっても、ビューはその下にあるコンテンツを覆い隠します。tvOSでは、安全領域には画面のオーバースキャンインセットも含まれます。これは、画面のベゼルで覆われた領域を表します。
以下は、iPhone 8およびiPhone Xシリーズで強調表示されている安全領域です。
のsafeAreaLayoutGuide
プロパティですUIView
の高さを取得するにはsafeAreaLayoutGuide
:
extension UIView {
var safeAreaHeight: CGFloat {
if #available(iOS 11, *) {
return safeAreaLayoutGuide.layoutFrame.size.height
}
return bounds.height
}
}
それはあなたの写真の矢の高さを返します。
では、上部の「ノッチ」と下部のホーム画面インジケーターの高さを取得するのはどうでしょうか。
ここでは、 safeAreaInsets
ビューの安全領域は、ナビゲーションバー、タブバー、ツールバー、およびビューコントローラーのビューを不明瞭にする他の祖先でカバーされていない領域を反映しています。(tvOSでは、安全領域は画面のベゼルで覆われていない領域を反映します。)ビューの安全領域を取得するには、このプロパティのインセットをビューの境界の長方形に適用します。ビューが現在ビュー階層にインストールされていない場合、またはまだ画面に表示されていない場合、このプロパティのエッジインセットは0です。
以下は、iPhone 8およびiPhone Xシリーズの1つでの危険な領域とエッジからの距離を示しています。
さて、ナビゲーションバーが追加された場合
では、安全でない領域の高さを取得する方法は?私たちは使用しますsafeAreaInset
ここに解決策がありますが、重要な点で異なります、
最初の1つ:
self.view.safeAreaInsets
これでEdgeInsetsが返され、上部と下部にアクセスしてインセットを確認できます。
二つ目:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
最初のビューはインセットを取得しているため、ナビゲーションバーがある場合はそれが考慮されますが、2番目のウィンドウはウィンドウのsafeAreaInsetsにアクセスしているため、ナビゲーションバーは考慮されません。
safeAreaLayoutGuide ビューが画面上に表示されている場合、このガイドは、ナビゲーションバー、タブバー、ツールバー、およびその他の祖先ビューで覆われていないビューの部分を反映しています。(tvOSでは、安全領域は、画面のベゼルで覆われていない領域を反映します。)ビューが現在ビュー階層にインストールされていないか、まだ画面に表示されていない場合、レイアウトガイドの端はビューの端と同じです。
次に、スクリーンショットの赤い矢印の高さを取得するには、次のようにします。
self.safeAreaLayoutGuide.layoutFrame.size.height
Swift 5、Xcode 11.4
`UIApplication.shared.keyWindow`
非推奨の警告が表示されます。'' keyWindow 'はiOS 13.0で非推奨になりました:シーンが接続されているため、接続されているすべてのシーンでキーウィンドウを返すため、複数のシーンをサポートするアプリケーションでは使用しないでください。私はこのように使用します。
extension UIView {
var safeAreaBottom: CGFloat {
if #available(iOS 11, *) {
if let window = UIApplication.shared.keyWindowInConnectedScenes {
return window.safeAreaInsets.bottom
}
}
return 0
}
var safeAreaTop: CGFloat {
if #available(iOS 11, *) {
if let window = UIApplication.shared.keyWindowInConnectedScenes {
return window.safeAreaInsets.top
}
}
return 0
}
}
extension UIApplication {
var keyWindowInConnectedScenes: UIWindow? {
return windows.first(where: { $0.isKeyWindow })
}
}
Objective-C keyWindowがnilと等しい ときに誰が問題を抱えたか。上記のコードを(viewDidLoadではなく)viewDidAppearに配置するだけです
Swift 5 Extension
これは拡張機能として使用でき、次で呼び出すことができます:UIApplication.topSafeAreaHeight
extension UIApplication {
static var topSafeAreaHeight: CGFloat {
var topSafeAreaHeight: CGFloat = 0
if #available(iOS 11.0, *) {
let window = UIApplication.shared.windows[0]
let safeFrame = window.safeAreaLayoutGuide.layoutFrame
topSafeAreaHeight = safeFrame.minY
}
return topSafeAreaHeight
}
}
UIApplicationの拡張機能はオプションであり、UIViewの拡張機能、または任意の優先機能、あるいはおそらくグローバル関数の拡張機能にすることができます。
より丸みのあるアプローチ
import SnapKit
let containerView = UIView()
containerView.backgroundColor = .red
self.view.addSubview(containerView)
containerView.snp.remakeConstraints { (make) -> Void in
make.width.top.equalToSuperView()
make.top.equalTo(self.view.safeArea.top)
make.bottom.equalTo(self.view.safeArea.bottom)
}
extension UIView {
var safeArea: ConstraintBasicAttributesDSL {
if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.snp
}
return self.snp
}
var isIphoneX: Bool {
if #available(iOS 11.0, *) {
if topSafeAreaInset > CGFloat(0) {
return true
} else {
return false
}
} else {
return false
}
}
var topSafeAreaInset: CGFloat {
let window = UIApplication.shared.keyWindow
var topPadding: CGFloat = 0
if #available(iOS 11.0, *) {
topPadding = window?.safeAreaInsets.top ?? 0
}
return topPadding
}
var bottomSafeAreaInset: CGFloat {
let window = UIApplication.shared.keyWindow
var bottomPadding: CGFloat = 0
if #available(iOS 11.0, *) {
bottomPadding = window?.safeAreaInsets.bottom ?? 0
}
return bottomPadding
}
}
ランドスケープモードに変更する場合は、ローテーション後にviewSafeAreaInsetsDidChangeを使用して最新の値を取得する必要があります。
private var safeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
override func viewSafeAreaInsetsDidChange() {
if #available(iOS 11.0, *) {
safeAreaInsets = UIApplication.shared.keyWindow!.safeAreaInsets
}
}