Відповіді:
Спробуйте це :
В Цілі С
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 втратила ці властивості, замінивши їх єдиним посібником щодо макета безпечної зони
Безпечна зона згідно 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-Series.
Тепер, якщо додано панель навігації
Отже, тепер, як отримати високу небезпечну площу? ми будемо використовуватиsafeAreaInset
Ось рішення, однак вони важливо відрізняються,
Перший:
self.view.safeAreaInsets
Це поверне EdgeInsets, тепер ви можете отримати доступ до верху та знизу, щоб знати вставки,
Другий:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
Перший ви берете вставки для перегляду, тому, якщо є панель навігації, він буде врахований, проте другий, який ви отримуєте доступ до SafeAreaInsets вікна, тому навігаційна панель не буде врахована
safeAreaLayoutGuide Коли вигляд видно на екрані, цей посібник відображає частину подання, яка не охоплена навігаційними панелями, панелями вкладок, панелями інструментів та іншими видами предків. (У tvOS безпечна область відображає область, не охоплена рамкою екрана.) Якщо подання наразі не встановлено в ієрархії перегляду або ще не видно на екрані, краї направляючих макетів дорівнюють краям подання.
Потім, щоб отримати висоту червоної стрілки на скріншоті, це:
self.safeAreaLayoutGuide.layoutFrame.size.height
Швидкий 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 дорівнює нулю . Просто поставте код вище в viewDidAppear (не в viewDidLoad)
Розширення Swift 5
Це можна використовувати як розширення та викликати за допомогою: 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
}
}