Odpowiedzi:
Spróbuj tego :
W celu C
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
}
W Swift
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let bottomPadding = window?.safeAreaInsets.bottom
}
keyWindow
zawsze było nil
tak, że zmieniłem to na windows[0]
i usunąłem ?
z opcjonalnego łańcucha, a potem zadziałało.
Aby uzyskać wysokość między prowadnicami układu, po prostu zrób
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
Więc full frame height = 812.0
,safe area height = 734.0
Poniżej znajduje się przykład, w którym zielony widok ma ramkę guide.layoutFrame
UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame
, który ma bezpieczną ramkę.
Swift 4, 5
Przypięcie widoku do zakotwiczenia obszaru bezpiecznego przy użyciu ograniczeń można wykonać w dowolnym miejscu cyklu życia kontrolera widoku, ponieważ są one kolejkowane przez interfejs API i obsługiwane po załadowaniu widoku do pamięci. Jednak uzyskanie wartości obszaru bezpiecznego wymaga czekania pod koniec cyklu życia kontrolera widoku, na przykład viewDidLayoutSubviews()
.
To podłącza się do dowolnego kontrolera widoku:
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
}
Wolę tę metodę niż wyrzucenie jej z okna (jeśli to możliwe), ponieważ tak zaprojektowano interfejs API, a co ważniejsze, wartości są aktualizowane podczas wszystkich zmian widoku, takich jak zmiany orientacji urządzenia.
Jednak niektóre niestandardowe kontrolery widoku prezentowanego nie mogą korzystać z powyższej metody (podejrzewam, że są w przejściowych widokach kontenerów). W takich przypadkach można pobrać wartości z głównego kontrolera widoku, który zawsze będzie dostępny w dowolnym miejscu w cyklu życia bieżącego kontrolera widoku.
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
(które można wywołać wielokrotnie), oto rozwiązanie, które zadziała nawet na viewDidLoad
: stackoverflow.com/a/53864017/7767664
Żadna z innych odpowiedzi tutaj nie zadziałała, ale tak się stało.
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
}
Wszystkie odpowiedzi tutaj są pomocne. Dziękujemy wszystkim, którzy zaoferowali pomoc.
Jednak, jak widzę, ten temat dotyczący bezpiecznego obszaru jest nieco zagmatwany, co nie wydaje się być dobrze udokumentowane.
Więc podsumuję to tutaj jak najdokładniej, aby ułatwić zrozumienie safeAreaInsets
, safeAreaLayoutGuide
i LayoutGuide
.
W iOS 7 firma Apple wprowadziła właściwości topLayoutGuide
i bottomLayoutGuide
w. UIViewController
Pozwoliły one na tworzenie ograniczeń, aby zawartość nie była ukrywana przez paski UIKit, takie jak pasek stanu, nawigacja lub pasek kart. Dzięki tym przewodnikom po układzie można było określić ograniczenia zawartości, unikając jej być ukryte przez górne lub dolne elementy nawigacyjne (paski UIKit, pasek stanu, pasek nawigacji lub pasek kart…).
Na przykład, jeśli chcesz, aby tableView zaczynał się od górnego ekranu, zrobiłeś coś takiego:
self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)
W iOS 11 firma Apple wycofała te właściwości, zastępując je przewodnikiem po układzie pojedynczego bezpiecznego obszaru
Obszar bezpieczny według Apple
Bezpieczne obszary pomagają umieścić widoki w widocznej części całego interfejsu. Kontrolery widoków zdefiniowane przez UIKit mogą umieszczać specjalne widoki na twoich treściach. Na przykład kontroler nawigacji wyświetla pasek nawigacji u góry zawartości bazowego kontrolera widoku. Nawet jeśli takie widoki są częściowo przezroczyste, nadal zasłaniają treść, która się pod nimi znajduje. W systemie tvOS obszar bezpieczny zawiera również wstawki przeskanowane ekranu, które reprezentują obszar objęty ramką ekranu.
Poniżej bezpieczny obszar wyróżniony w iPhone'ach 8 i iPhone'ach X:
safeAreaLayoutGuide
Jest właściwościąUIView
Aby uzyskać wysokość safeAreaLayoutGuide
:
extension UIView {
var safeAreaHeight: CGFloat {
if #available(iOS 11, *) {
return safeAreaLayoutGuide.layoutFrame.size.height
}
return bounds.height
}
}
To zwróci wysokość strzałki na twoim zdjęciu.
A co z uzyskaniem górnego „wycięcia” i dolnych wysokości wskaźników ekranu głównego?
Tutaj użyjemy safeAreaInsets
Bezpieczny obszar widoku odzwierciedla obszar nieobjęty przez paski nawigacji, paski kart, paski narzędzi i inne elementy nadrzędne, które zasłaniają widok kontrolera widoku. (W tvOS, bezpieczny obszar odzwierciedla obszar nie zakryty przez ramkę ekranu.) Bezpieczny obszar dla widoku uzyskuje się, stosując wstawki w tej właściwości do prostokąta granic widoku. Jeśli widok nie jest aktualnie zainstalowany w hierarchii widoków lub nie jest jeszcze widoczny na ekranie, wstawki krawędzi w tej właściwości mają wartość 0.
Poniższy rysunek przedstawia niebezpieczny obszar i odległość od krawędzi w telefonie iPhone 8 i iPhone X-Series.
Teraz, jeśli dodano pasek nawigacji
Jak więc teraz uzyskać wysokość obszaru niebezpiecznego? użyjemysafeAreaInset
Oto rozwiązania, które jednak różnią się ważną rzeczą,
Pierwszy:
self.view.safeAreaInsets
To zwróci EdgeInsets, możesz teraz uzyskać dostęp do góry i dołu, aby poznać wstawki,
Drugi:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
Pierwszy z nich jest wstawiany do widoku, więc jeśli istnieje pasek nawigacji, zostanie on uwzględniony, jednak drugi, do którego uzyskujesz dostęp do safeAreaInsets okna, więc pasek nawigacji nie będzie brany pod uwagę
W iOS 11 istnieje metoda, która informuje, kiedy zmienił się safeArea.
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
let top = view.safeAreaInsets.top
let bottom = view.safeAreaInsets.bottom
}
safeAreaLayoutGuide Gdy widok jest widoczny na ekranie, ten przewodnik odzwierciedla część widoku, która nie jest zakryta przez paski nawigacji, paski kart, paski narzędzi i inne widoki przodków. (W systemie tvOS bezpieczny obszar odzwierciedla obszar nie zakryty ramką ekranu). Jeśli widok nie jest obecnie zainstalowany w hierarchii widoków lub nie jest jeszcze widoczny na ekranie, krawędzie prowadnicy układu są równe krawędziom widoku.
Następnie, aby uzyskać wysokość czerwonej strzałki na zrzucie ekranu, jest to:
self.safeAreaLayoutGuide.layoutFrame.size.height
Swift 5, Xcode 11.4
`UIApplication.shared.keyWindow`
Daje ostrzeżenie o wycofaniu. Opcja „keyWindow” została wycofana w iOS 13.0: nie powinna być używana dla aplikacji obsługujących wiele scen, ponieważ zwraca okno klucza we wszystkich połączonych scenach ”z powodu połączonych scen. Używam w ten sposób.
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 Kto miał problem, gdy keyWindow jest równe nil . Po prostu umieść powyższy kod w viewDidAppear (nie w viewDidLoad )
Rozszerzenie Swift 5
Może być używane jako rozszerzenie i wywoływane z: 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
}
}
Rozszerzenie UIApplication jest opcjonalne, może być rozszerzeniem UIView lub czymkolwiek jest preferowane, lub prawdopodobnie nawet lepiej funkcją globalną.
Bardziej zaokrąglone podejście
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
}
}