Dapatkan ketinggian inset area aman bagian atas dan bawah

178

Pada iPhone X yang baru, apa cara yang paling tepat untuk mendapatkan ketinggian atas dan bawah untuk area yang tidak aman?

masukkan deskripsi gambar di sini

Tulleb
sumber

Jawaban:

366

Coba ini :

Dalam Tujuan C

if (@available(iOS 11.0, *)) {
    UIWindow *window = UIApplication.sharedApplication.keyWindow;
    CGFloat topPadding = window.safeAreaInsets.top;
    CGFloat bottomPadding = window.safeAreaInsets.bottom;
}

Dalam Swift

if #available(iOS 11.0, *) {
    let window = UIApplication.shared.keyWindow
    let topPadding = window?.safeAreaInsets.top
    let bottomPadding = window?.safeAreaInsets.bottom
}
pengguna6788419
sumber
1
@KrutikaSonawala bottomPadding + 10 (nilai Anda)
user6788419
12
Ini sepertinya tidak berhasil bagi saya - mencetak nilai UIApplication.shared.keyWindow? .SafeAreaInsets mengembalikan semua 0s. Ada ide?
Hai
2
Saya lakukan, saya bisa membatasi safeAreaLayoutGuides dari tampilan apa pun tetapi langsung mencetak nilai safeAreaInsets jendela kunci hanya mengembalikan nol persegi. Ini bermasalah karena saya perlu membatasi secara manual tampilan yang telah ditambahkan ke jendela kunci tetapi tidak ada dalam hierarki tampilan pengontrol tampilan root.
Hai
6
Bagi saya, keyWindowselalu niljadi saya mengubahnya windows[0]dan menghapus ?dari chaining opsional, lalu berhasil.
George_E
2
Ini hanya berfungsi ketika tampilan pengontrol sudah muncul.
Vanya
85

Untuk mendapatkan ketinggian antara panduan tata letak yang baru saja Anda lakukan

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

Jadi full frame height = 812.0,safe area height = 734.0

Di bawah ini adalah contoh di mana tampilan hijau memiliki bingkai guide.layoutFrame

masukkan deskripsi gambar di sini

Ladislav
sumber
Terima kasih itu adalah petunjuk yang juga saya pikirkan. Saya rasa tidak ada solusi yang lebih andal. Tetapi dengan ini saya hanya mendapatkan ketinggian penuh tidak aman, kan? Bukan dua ketinggian untuk setiap area?
Tulleb
2
Setelah pandangan ditata, ini akan memberi Anda jawaban yang benar
Ladislav
2
Sebenarnya jawaban @ user6788419 terlihat bagus juga dan jauh lebih pendek.
Tulleb
Saya mendapatkan ketinggian 812.0 dengan kode itu, menggunakan tampilan controller. Jadi saya menggunakan UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame, yang memang memiliki kerangka aman.
Ferran Maylinch
1
@FerranMaylinch Tidak yakin di mana Anda memeriksa ketinggian, tapi saya juga punya 812, yang ternyata karena memeriksa nilainya sebelum tampilan terlihat. Dalam hal ketinggian akan sama "Jika tampilan saat ini tidak diinstal dalam hierarki tampilan, atau belum terlihat di layar, tepi panduan tata letak sama dengan tepi tampilan" developer.apple.com/documentation/ uikit / uiview / ...
Nicholas Harlen
81

Cepat 4, 5

Untuk menyematkan tampilan ke jangkar area aman menggunakan kendala dapat dilakukan di mana saja dalam siklus hidup pengontrol tampilan karena mereka antri oleh API dan ditangani setelah tampilan dimasukkan ke dalam memori. Namun, mendapatkan nilai area aman memerlukan menunggu menjelang akhir siklus hidup pengontrol tampilan viewDidLayoutSubviews().

Ini dihubungkan ke pengontrol tampilan apa pun:

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
}

Saya lebih suka metode ini untuk mengeluarkannya dari jendela (bila memungkinkan) karena ini adalah cara API dirancang dan, yang lebih penting, nilai diperbarui selama semua perubahan tampilan, seperti perubahan orientasi perangkat.

Namun, beberapa pengendali tampilan khusus yang disajikan tidak dapat menggunakan metode di atas (saya curiga karena mereka berada dalam tampilan wadah sementara). Dalam kasus seperti itu, Anda bisa mendapatkan nilai dari pengontrol tampilan root, yang akan selalu tersedia di mana saja dalam siklus hidup pengontrol tampilan saat ini.

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
}
bsod
sumber
3
Silakan mendeklarasikan menggunakan let topSafeArea: CGFloat sebagai gantinya!
Gusutafu
hindari menggunakan viewDidLayoutSubviews(yang bisa disebut berkali-kali), inilah solusi yang akan berfungsi bahkan di viewDidLoad: stackoverflow.com/a/53864017/7767664
user924
@ user924 maka nilai-nilai tidak akan diperbarui ketika area aman berubah (yaitu bilah status double-height)
bsod
@bod maka ini akan lebih baik stackoverflow.com/a/3420550/7767664 (karena ini hanya untuk status bar dan bukan tampilan lainnya)
user924
Atau alih-alih melalui delegasi aplikasi, Anda hanya dapat menggunakan area aman API yang dibangun Apple ke pengontrol tampilan.
bsod
21

Tidak ada jawaban lain di sini yang berfungsi untuk saya, tetapi ini berhasil.

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
  }
ScottyBlades
sumber
1
Kode Anda akan berfungsi di bagian mana pun dari siklus hidup pengontrol tampilan. Jika Anda ingin menggunakan view.safeAreaInsets.top, maka Anda perlu memastikan bahwa Anda memanggilnya setelah di viewDidAppear atau yang lebih baru. Di viewDidLoad, Anda tidak akan mendapatkan view.safeAreaInsets.top dengan nilai yang benar karena belum diinisialisasi.
user3204765
1
jawaban terbaik sejauh ini
Rikco
17

Semua jawaban di sini sangat membantu, Terima kasih kepada semua orang yang menawarkan bantuan.

Namun seperti yang saya lihat bahwa topik area aman sedikit bingung yang sepertinya tidak terdokumentasi dengan baik.

Jadi saya akan meringkasnya di sini sebagai bubur mungkin untuk membuatnya mudah dimengerti safeAreaInsets, safeAreaLayoutGuidedan LayoutGuide.

Di iOS 7, Apple memperkenalkan topLayoutGuidedan bottomLayoutGuideproperti di UIViewController, Mereka memungkinkan Anda untuk membuat batasan untuk menjaga konten Anda agar tidak disembunyikan oleh bilah UIKit seperti status, navigasi, atau bilah tab. Mungkin saja dengan panduan tata letak ini untuk menentukan batasan pada konten, menghindarinya disembunyikan oleh elemen navigasi atas atau bawah (bilah UIKit, bilah status, nav atau bilah tab ...).

Jadi misalnya jika Anda ingin membuat tableView dimulai dari layar atas Anda telah melakukan sesuatu seperti itu:

self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)

Di iOS 11 Apple telah menghentikan properti ini menggantikannya dengan panduan tata letak area aman tunggal

Area aman menurut Apple

Area aman membantu Anda menempatkan tampilan dalam bagian yang terlihat dari keseluruhan antarmuka. Pengontrol tampilan yang ditentukan UIKit dapat memposisikan tampilan khusus di atas konten Anda. Misalnya, pengontrol navigasi menampilkan bilah navigasi di atas konten pengontrol tampilan yang mendasarinya. Bahkan ketika pandangan semacam itu sebagian transparan, mereka masih menyumbat konten yang ada di bawahnya. Di tvOS, area aman juga mencakup insets overscan layar, yang mewakili area yang dicakup oleh bezel layar.

Di bawah, area aman yang disorot di iPhone 8 dan iPhone X-series:

masukkan deskripsi gambar di sini

Ini safeAreaLayoutGuideadalah properti dariUIView

Untuk mendapatkan ketinggian safeAreaLayoutGuide:

extension UIView {
   var safeAreaHeight: CGFloat {
       if #available(iOS 11, *) {
        return safeAreaLayoutGuide.layoutFrame.size.height
       }
       return bounds.height
  }
}

Itu akan mengembalikan ketinggian Panah di gambar Anda.

Sekarang, bagaimana dengan mendapatkan tinggi "takik" dan ketinggian indikator layar beranda bawah?

Di sini kita akan menggunakan safeAreaInsets

Area tampilan yang aman mencerminkan area yang tidak tercakup oleh bilah navigasi, bilah tab, bilah alat, dan leluhur lainnya yang mengaburkan pandangan pengontrol tampilan. (Di tvOS, area aman mencerminkan area yang tidak tercakup oleh bezel layar.) Anda mendapatkan area aman untuk tampilan dengan menerapkan insets di properti ini ke persegi panjang batas tampilan. Jika tampilan saat ini tidak diinstal dalam hierarki tampilan, atau belum terlihat di layar, insets tepi pada properti ini adalah 0.

Berikut ini akan menunjukkan area tidak aman dan ada jarak dari tepi pada iPhone 8 dan salah satu dari iPhone X-Series.

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Sekarang, jika bilah navigasi ditambahkan

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Jadi, sekarang bagaimana cara mendapatkan ketinggian area yang tidak aman? kami akan menggunakansafeAreaInset

Berikut adalah solusi namun mereka berbeda dalam hal yang penting,

Pertama:

self.view.safeAreaInsets

Itu akan mengembalikan EdgeInsets, Anda sekarang dapat mengakses bagian atas dan bawah untuk mengetahui insets,

Kedua:

UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets

Yang pertama Anda mengambil insets tampilan, jadi jika ada bilah navigasi akan dipertimbangkan, namun yang kedua Anda mengakses safeAreaInsets jendela sehingga bilah navigasi tidak akan dianggap

mojtaba al moussawi
sumber
5

Di iOS 11 ada metode yang memberi tahu kapan safeArea telah berubah.

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

Aku bekerja dengan kerangka kerja CocoaPods dan dalam hal UIApplication.sharedini tidak tersedia maka saya menggunakan safeAreaInsetsdalam pandangan ini window:

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

safeAreaLayoutGuide Saat tampilan terlihat di layar, panduan ini mencerminkan bagian tampilan yang tidak tercakup oleh bilah navigasi, bilah tab, bilah alat, dan tampilan leluhur lainnya. (Di tvOS, area aman mencerminkan area yang tidak tertutup bezel layar.) Jika tampilan saat ini tidak dipasang dalam hierarki tampilan, atau belum terlihat di layar, tepi panduan tata letak sama dengan tepi tampilan.

Kemudian untuk mendapatkan ketinggian panah merah di tangkapan layar itu:

self.safeAreaLayoutGuide.layoutFrame.size.height
jahat
sumber
2

Swift 5, Xcode 11.4

`UIApplication.shared.keyWindow` 

Ini akan memberikan peringatan penghentian. '' keyWindow 'sudah tidak digunakan lagi di iOS 13.0: Sebaiknya tidak digunakan untuk aplikasi yang mendukung banyak adegan karena mengembalikan jendela kunci di semua adegan yang terhubung' karena adegan yang terhubung. Saya menggunakan cara ini.

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 })
    }
}
pengguna1039695
sumber
1

Objective-C Siapa yang punya masalah ketika keyWindow sama dengan nihil . Cukup masukkan kode di atas di viewDidAppear (bukan di viewDidLoad)

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

Ekstensi 5 Swift

Ini dapat digunakan sebagai Extension dan dipanggil dengan: 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
    }
}

Perpanjangan aplikasi UIA adalah opsional, bisa berupa perpanjangan UIView atau apa pun yang disukai, atau mungkin bahkan lebih baik fungsi global.

Theodore
sumber
0

Pendekatan yang lebih bulat

  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
    }
}
johndpope
sumber
0

Bagi Anda yang beralih ke mode lansekap, Anda harus memastikan untuk menggunakan viewSafeAreaInsetsDidChange setelah rotasi untuk mendapatkan nilai terbaru:

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
        }
}
Oz Shabat
sumber