安全な領域インセットの上部と下部の高さを取得


回答:


366

これを試して :

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
}

1
@KrutikaSonawala bottomPadding + 10(your value)
user6788419

12
これは私にはうまくいかないようです-UIApplication.shared.keyWindow?.safeAreaInsetsの値を出力するとすべて0が返されます。何か案は?
Hai

2
私はそうしました、どのビューのsafeAreaLayoutGuidesに対しても制約できますが、キーウィンドウのsafeAreaInsetsの値を直接出力すると、ゼロの四角形が返されます。キーウィンドウに追加されたが、ルートビューコントローラーのビュー階層にないビューを手動で制約する必要があるため、これには問題があります。
ハイ

6
私にとってkeyWindowはいつもnilだったので、に変更してオプションのチェーンからwindows[0]を削除しました?が、うまくいきました。
George_E

2
コントローラのビューがすでに表示されている場合にのみ機能します。
Vanya

85

あなただけのレイアウトガイド間の高さを取得するには

let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height

だからfull frame height = 812.0safe area height = 734.0

以下は、緑のビューのフレームがある例です guide.layoutFrame

ここに画像の説明を入力してください


おかげで私も考えていたリードでした。もっと信頼できる解決策はないと思います。しかし、これで私は危険な高さだけを取得しますよね?各エリアに2つの高さはありませんか?
タレブ

2
ビューは、これはあなたに正しい答え与えるレイアウトされたら
ラディスラフ

2
実際、@ user6788419の回答も見栄えが良く、短くなっています。
タレブ

コントローラのビューを使用して、そのコードで812.0の高さを取得しています。したがって、私はを使用UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrameしていますが、これにはセーフフレームがあります。
Ferran Maylinch 2018年

1
@FerranMaylinchあなたが高さをチェックしている場所がわからないが、ビューが表示される前に値をチェックしたことが原因であることが判明した812もあった。その場合、高さは同じになります。「ビューが現在ビュー階層にインストールされていない場合、またはまだ画面に表示されていない場合、レイアウトガイドのエッジはビューのエッジと同じです。」developer.apple.com/documentation/ uikit / uiview /…
ニコラスハーレン

81

スイフト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
}

3
代わりにlet topSafeArea:CGFloatを使用して宣言してください!
グスタフ

使用は避けてくださいviewDidLayoutSubviews(複数回呼び出すことができます)、ここにおいても動作するソリューションですviewDidLoadstackoverflow.com/a/53864017/7767664は
user924

@ user924の場合、安全領域が変更されても値は更新されません(つまり、高さ2倍のステータスバー)
bsod

@bsodこれは良くなるstackoverflow.com/a/3420550/7767664(それが唯一のステータスバーではなく、他のビューのためだから)
user924

または、アプリデリゲートを経由する代わりに、Appleがビューコントローラーに組み込んだセーフエリアAPIを使用することもできます。
bsod

21

ここでの他の答えはどれも私にとってはうまくいきませんでしたが、これはうまくいきました。

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
  }

1
コードは、View Controllerのライフサイクルのどの部分でも機能します。view.safeAreaInsets.topを使用する場合は、viewDidAppear以降で呼び出す必要があることを確認する必要があります。viewDidLoadでは、まだ初期化されていないため、正しい値のview.safeAreaInsets.topを取得できません。
user3204765

1
これまでのベストアンサー
Rikco

17

ここでの回答はすべて役に立ちました。助けてくれた皆さんに感謝します。

ただし、安全領域のトピックは少し混乱しているため、十分に文書化されていないようです。

私は理解しやすいようにできるだけドロドロとして、ここでそれを要約しますsafeAreaInsetssafeAreaLayoutGuideLayoutGuide

iOS 7では、AppleがtopLayoutGuidebottomLayoutGuideプロパティを導入し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にアクセスしているため、ナビゲーションバーは考慮されません。


5

iOS 11には、safeAreaがいつ変更されたかを通知するメソッドがあります。

override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()
    let top = view.safeAreaInsets.top
    let bottom = view.safeAreaInsets.bottom
}

2

CocoaPodsフレームワークを使用していて、使用できない場合UIApplication.sharedは、viewで使用します。safeAreaInsetswindow

if #available(iOS 11.0, *) {
    let insets = view.window?.safeAreaInsets
    let top = insets.top
    let bottom = insets.bottom
}

2

safeAreaLayoutGuide ビューが画面上に表示されている場合、このガイドは、ナビゲーションバー、タブバー、ツールバー、およびその他の祖先ビューで覆われていないビューの部分を反映しています。(tvOSでは、安全領域は、画面のベゼルで覆われていない領域を反映します。)ビューが現在ビュー階層にインストールされていないか、まだ画面に表示されていない場合、レイアウトガイドの端はビューの端と同じです。

次に、スクリーンショットの赤い矢印の高さを取得するには、次のようにします。

self.safeAreaLayoutGuide.layoutFrame.size.height

2

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 })
    }
}

1

Objective-C keyWindownilと等しい ときに誰が問題を抱えたか。上記のコードを(viewDidLoadではなく)viewDidAppearに配置するだけです


1
UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 
CGFloat fBottomPadding = window.safeAreaInsets.bottom;

1

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の拡張機能、または任意の優先機能、あるいはおそらくグローバル関数の拡張機能にすることができます。


0

より丸みのあるアプローチ

  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
    }
}

0

ランドスケープモードに変更する場合は、ローテーション後に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
        }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.