Üst ve alt yükseklikleri yerleştirerek güvenli alan elde edin


Yanıtlar:


368

Bunu dene :

In Objective C

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

In Swift

if #available(iOS 11.0, *) {
    let window = UIApplication.shared.keyWindow
    let topPadding = window?.safeAreaInsets.top
    let bottomPadding = window?.safeAreaInsets.bottom
}

1
@KrutikaSonawala bottomPadding + 10 (değeriniz)
user6788419

12
Bu benim için çalışmıyor gibi görünüyor - UIApplication.shared.keyWindow? .SafeAreaInsets değerini yazdırmak tüm 0'ları döndürür. Herhangi bir fikir?
Hai

2
Yaptım, herhangi bir görünümün safeAreaLayoutGuides karşı kısıtlayabilir ama doğrudan anahtar pencerenin safeAreaInsets değerini yazdırmak sadece sıfır bir rekt döndürür. Anahtar pencereye eklenen ancak kök görünüm denetleyicisinin görünüm hiyerarşisinde olmayan bir görünümü el ile kısıtlamam gerektiğinden bu sorunludur.
Hai

6
Benim keyWindowiçin her zaman nilöylesine değiştirdim windows[0]ve ?isteğe bağlı zincirleme kaldırıldı , sonra çalıştı.
George_E

2
Yalnızca bir denetleyicinin görünümü zaten göründüğünde çalışır.
Vanya

85

Düzen kılavuzları arasındaki yüksekliği elde etmek için yapmanız gereken

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

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

Aşağıda yeşil görünümün guide.layoutFrame

resim açıklamasını buraya girin


Teşekkürler, ben de düşündüm. Daha güvenilir bir çözüm olduğunu düşünmüyorum. Ama bununla birlikte sadece tam güvensiz yüksekliği elde ediyorum, değil mi? Her alan için iki yükseklik değil mi?
Tulleb

2
Görüşler ortaya konduğunda, bu size doğru cevabı verecektir
Ladislav

2
Aslında @ user6788419'un yanıtı da hoş görünüyor ve çok daha kısa.
Tulleb

Bir denetleyicinin görünümünü kullanarak, bu kod ile 812.0 bir yükseklik alıyorum. Bu yüzden UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFramegüvenli bir çerçeveye sahip kullanıyorum .
Ferran Maylinch

1
@FerranMaylinch Yüksekliği nerede kontrol ettiğinizden emin değilim, ancak görünüm görünmeden önce değeri kontrol etmekten kaynaklandığı 812 de vardı. Bu durumda yükseklikler aynı olur "Görünüm şu anda bir görünüm hiyerarşisine yüklenmemişse veya henüz ekranda görünmüyorsa, düzen kılavuzu kenarları görünümün kenarlarına eşittir" developer.apple.com/documentation/ uikit / uiview /…
Nicholas Harlen

81

Swift 4, 5

Bir görünümü güvenli bir alan bağlantısına sabitlemek için kısıtlamalar kullanarak görünüm denetleyicisinin yaşam döngüsünün herhangi bir yerinde yapılabilir, çünkü bunlar API tarafından sıraya alınır ve görünüm belleğe yüklendikten sonra işlenir. Ancak, güvenli alan değerlerinin elde edilmesi, bir görünüm denetleyicisinin yaşam döngüsünün sonuna doğru beklemeyi gerektirir viewDidLayoutSubviews().

Bu, herhangi bir görünüm denetleyicisine takılır:

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'nin nasıl tasarlandığı ve daha da önemlisi, cihaz yönlendirme değişiklikleri gibi tüm görünüm değişiklikleri sırasında değerler güncellendiğinden, bu yöntemi pencereden çıkarmak için (mümkünse) tercih ederim.

Ancak, bazı özel sunulan görünüm denetleyicileri yukarıdaki yöntemi kullanamazsınız (geçici kapsayıcı görünümlerinde olduğundan şüpheleniyorum). Bu gibi durumlarda, değerleri geçerli görünüm denetleyicisinin yaşam döngüsünde her zaman kullanılabilecek kök görünüm denetleyicisinden alabilirsiniz.

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
Lütfen yerine topSafeArea: CGFloat kullanarak bildirin!
Gusutafu

kullanmaktan kaçının viewDidLayoutSubviews(birden çok kez çağrılabilir), burada bile çalışacak bir çözüm var viewDidLoad: stackoverflow.com/a/53864017/7767664
user924

@ user924, güvenli alanlar değiştiğinde değerler güncellenmez (örn. çift yükseklik durum çubuğu)
bsod

@bsod o zaman bu daha iyi olacaktır stackoverflow.com/a/3420550/7767664 (çünkü sadece durum çubuğu içindir ve diğer görünümler için değildir)
user924

Ya da uygulama temsilcisinden geçmek yerine görünüm denetleyicisine yerleştirilmiş güvenli Apple API alanını kullanabilirsiniz.
bsod

21

Buradaki diğer cevapların hiçbiri benim için işe yaramadı, ama bu işe yaramadı.

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
Kodunuz, görünüm denetleyicisi yaşam döngüsünün herhangi bir bölümünde çalışır. View.safeAreaInsets.top kullanmak istiyorsanız, viewDidAppear veya daha sonraki bir sürümünde aradığınızdan emin olmanız gerekir. ViewDidLoad içinde, henüz başlatılmadığından view.safeAreaInsets.top dosyasını doğru değerle alamazsınız.
user3204765

1
şimdiye kadarki en iyi cevap
Rikco

17

Buradaki tüm cevaplar faydalı, Yardım sunan herkese teşekkürler.

Ancak güvenli alan konusunun iyi belgelenmiş görünmeyecek şekilde biraz karışık olduğunu gördüğüm gibi.

Ben burada özetleyecek Yani mümkün olduğunca pelte gibi kolay anlaşılması için safeAreaInsets, safeAreaLayoutGuideve LayoutGuide.

İOS 7'de Apple topLayoutGuideve bottomLayoutGuideözelliklerini tanıttı, UIViewControllerİçeriğinizin durum, gezinme veya sekme çubuğu gibi UIKit çubukları tarafından gizlenmesini önlemek için kısıtlamalar oluşturmanıza izin verdiler. Bu düzen kılavuzlarıyla, içeriğe ilişkin kısıtlamaları belirlemek ve bundan kaçınmak mümkün oldu. üst veya alt gezinme öğeleri (UIKit çubukları, durum çubuğu, gezinme veya sekme çubuğu…) tarafından gizlenmesini sağlar.

Yani, örneğin bir tableView üst ekrandan başlamak istiyorsanız böyle bir şey yaptınız:

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

İOS 11'de Apple, bu özellikleri tek bir güvenli alan düzeni kılavuzuyla değiştirerek bu özellikleri kullanımdan kaldırmıştır

Apple'a göre güvenli alan

Güvenli alanlar, görünümlerinizi genel arayüzün görünür kısmına yerleştirmenize yardımcı olur. UIKit tanımlı görünüm denetleyicileri özel görünümleri içeriğinizin üzerine yerleştirebilir. Örneğin, bir gezinti denetleyicisi temel görünüm denetleyicisinin içeriğinin üstünde bir gezinme çubuğu görüntüler. Bu tür görüşler kısmen şeffaf olsa bile, yine de altındaki içeriği kapatırlar. TVOS'ta güvenli alan, ekranın çerçevesinin kapladığı alanı temsil eden ekranın aşırı tarama eklerini de içerir.

Aşağıda, iPhone 8 ve iPhone X serilerinde vurgulanan güvenli bir alan:

resim açıklamasını buraya girin

safeAreaLayoutGuideBir özelliktirUIView

Yüksekliğini elde etmek için safeAreaLayoutGuide:

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

Bu, resminizdeki Ok yüksekliğini döndürecektir.

Şimdi, üst "çentik" ve alt ana ekran gösterge yüksekliklerini almaya ne dersiniz?

Burada kullanacağız safeAreaInsets

Bir görünümün güvenli alanı, gezinme çubukları, sekme çubukları, araç çubukları ve bir görünüm denetleyicisinin görünümünü gizleyen diğer ataların kapsamadığı alanı yansıtır. (TvOS'ta, güvenli alan ekranın çerçevesi tarafından kaplanmayan alanı yansıtır.) Bu özellikteki iç metinleri görünümün sınır dikdörtgenine uygulayarak görünüm için güvenli alanı elde edersiniz. Görünüm şu anda bir görünüm hiyerarşisine yüklenmemişse veya henüz ekranda görünmüyorsa, bu özellikteki kenar iç metinleri 0'dır.

Aşağıda güvensiz alan ve iPhone 8 ve iPhone X-Series'in birindeki kenarlardan olan mesafe gösterilecektir.

resim açıklamasını buraya girin

resim açıklamasını buraya girin

Şimdi, gezinme çubuğu eklendiyse

resim açıklamasını buraya girin

resim açıklamasını buraya girin

Peki, şimdi güvensiz alan yüksekliği nasıl elde edilir? kullanacağızsafeAreaInset

İşte çözümler ancak önemli bir şeyde farklılık gösterir,

Birincisi:

self.view.safeAreaInsets

Bu, EdgeInsets'i döndürecek, artık üstleri ve alt kısımları girerek girişleri öğrenebilirsiniz,

İkinci:

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

Birincisi görünüm eklerini alırsınız, bu nedenle bir gezinme çubuğu varsa dikkate alınır, ancak ikincisi pencerenin safeAreaInsets'e erişirsiniz, böylece gezinme çubuğu dikkate alınmaz


5

İOS 11'de safeArea'nın ne zaman değiştiğini söyleyen bir yöntem var.

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

3

Swift 5, Xcode 11.4

`UIApplication.shared.keyWindow` 

Kullanımdan kaldırma uyarısı verecektir. '' keyWindow 'iOS 13.0'da kullanımdan kaldırıldı: Bağlı sahneler nedeniyle bağlı tüm sahnelerde bir anahtar pencere döndürdüğü için birden fazla sahneyi destekleyen uygulamalar için kullanılmamalıdır. Ben bu yolu kullanıyorum.

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

2

Ben CocoaPods çerçeveler ile ve bu durumda çalışıyorum UIApplication.sharedolduğunu kullanılamaz sonra kullanmak safeAreaInsetsgörünümü en in window:

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

2

safeAreaLayoutGuide Görünüm ekranda görünür olduğunda, bu kılavuz görünümün gezinme çubukları, sekme çubukları, araç çubukları ve diğer ata görünümleri tarafından kapsanmayan kısmını yansıtır. (TvOS'ta, güvenli alan ekranın çerçevesini kaplamamış alanı yansıtır.) Görünüm şu anda bir görünüm hiyerarşisine yüklenmemişse veya henüz ekranda görünmüyorsa, düzen kılavuzu kenarları görünümün kenarlarına eşittir.

Sonra ekran görüntüsünde kırmızı ok yüksekliğini elde etmek için:

self.safeAreaLayoutGuide.layoutFrame.size.height

1

Objective-C keyWindow nil değerine sahip olduğunda kimin sorunu vardı . Yukarıdaki kodu viewDidAppear'a koymanız yeterlidir (viewDidLoad içinde değil)


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

1

Swift 5 Uzantısı

Bu bir Uzantı olarak kullanılabilir ve şunlarla çağrılabilir: 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'nın genişletilmesi isteğe bağlıdır, UIView'in veya tercih edilenin bir uzantısı veya muhtemelen daha iyi bir global işlev olabilir.


0

Daha yuvarlak bir yaklaşım

  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

Yatay moda geçenler için, en güncel değerleri almak için rotasyondan sonra viewSafeAreaInsetsDidChange kullandığınızdan emin olmalısınız :

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
        }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.