IPhone X Выравнивание дна объекта в руинах Safe Area на других устройствах
Вопрос об iPhone X Autolayout quirks.
У меня есть две кнопки, ранее они были бы выровнены снизу до супервизора со смещением 20, чтобы они не касались нижней части экрана (с тех пор я изменил ссылку на безопасную зону, а не на просмотр).
Здесь оригинальная настройка: ![iPhone 8 - Setup]()
Выглядит хорошо, как ожидалось, на более старых iPhone. ![iPhone 8 20 Offset]()
Теперь 20 констант в нижнем ограничении теперь заставляют кнопки выглядеть фанки и слишком далеко от домашней панели на iPhone X. ![iPhone X 20 Offset]()
Логически, мне нужно удалить константу 20 из ограничения на iPhone X и выровнять кнопки непосредственно со дном безопасной области. ![iPhone X - Setup]()
Выглядит хорошо на iPhone X сейчас. ![iPhone X 0 Offset]()
Но теперь он помещает кнопки слишком близко к нижней части экрана на телефоны, не принадлежащие домашним барам. ![iPhone 8 0 Offset]()
Какие-либо немедленные решения этой проблемы, которые я пропустил в документах Apple? Невозможно использовать классы размера, так как в этом случае класс размера iPhone X накладывается на другие iPhone.
Я мог легко закодировать для обнаружения iPhone X и установить константу ограничения 0, но я надеялся на более элегантное решение.
Спасибо,
Ответы
Ответ 1
Apple Docs заявляет, что в iOS 11 есть новое объявление, которое может быть решением этой проблемы. В настоящее время iPhone X и iPhone 8 имеют один и тот же класс размеров, поэтому мы должны придумать другое решение.
var ДополнительныеSafeAreaInsets: UIEdgeInsets {get set}
Добавьте следующий код ниже в AppDelegate, и все дочерние элементы rootViewController наследуют дополнительную безопасную область. Примеры скриншотов ниже описывают это поведение.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
if !self.isIphoneX() {
self.window?.rootViewController?.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
}
return true
}
func isIphoneX() -> Bool {
if #available(iOS 11.0, *) {
if ((self.window?.safeAreaInsets.top)! > CGFloat(0.0)) {
return true;
}
}
return false
}
Интерфейс интерфейса iPhone X, совместимый с безопасным районом
![enter image description here]()
Создатель интерфейса iPhone 8, совместимый с безопасным районом
![enter image description here]()
Симулятор iPhone X - главный экран
![enter image description here]()
iPhone X Simulator - подробный экран
![enter image description here]()
Симулятор iPhone 8 - главный экран
![enter image description here]()
iPhone 8 Simulator - подробный экран
![enter image description here]()
Ответ 2
Еще один способ добиться этого прямо из раскадровки - создать два ограничения:
1. Между вашим элементом и безопасной областью с приоритетом 250 с константой 0
2. Между вашим элементом и дном суперпредставления с приоритетом 750 и константой 20 и отношением greater Than or Equal
.
![enter image description here]()
Ответ 3
Потратив немало времени, пытаясь решить проблему такого типа, изначально используя решение Marcos, я столкнулся с ситуацией, когда она не была решена - особенно в случае, когда "безопасная зона" была ненулевая высота, но не являющаяся безопасная область экрана означала, что смещение было 0 вместо минимального 20. Примером является контроллер произвольного вида с нижней безопасной областью, установленной через additionalSafeAreaInsets
.
Решение состояло в том, чтобы проверить, выравнивается ли наш вид с окном с ненулевой безопасной областью, когда вставки безопасной области изменяются, и отрегулировать нижнее смещение ограничения на безопасную область, основываясь на этом. Следующее приводит к смещению на 20 пунктов снизу на экране в прямоangularьном стиле и 0 на полноэкранном экране в стиле безопасной зоны (iPhone X, последний iPad Pro, слайдеры iPad и т.д.).
// In UIView subclass, or UIViewController using viewSafeAreaInsetsDidChange instead of safeAreaInsetsDidChange
@available(iOS 11.0, *)
override func safeAreaInsetsDidChange() {
super.safeAreaInsetsDidChange()
isTakingCareOfWindowSafeArea = self.isWithinNonZeroWindowBottomSafeArea
}
private var isTakingCareOfWindowSafeArea = false {
didSet {
guard isTakingCareOfWindowSafeArea != oldValue else { return }
// Different offset based on whether we care about the safe area or not
let offset: CGFloat = isTakingCareOfWindowSafeArea ? 0 : 20
// bottomConstraint is a required bottom constraint to the safe area of the view.
bottomConstraint.constant = -offset
}
}
extension UIView {
/// Allows you to check whether the view is dealing directly with the window safe area. The reason it the window rather than
/// the screen is that on iPad, slide over apps and such also have this nonzero safe area. Basically anything that does not have a square area (such as the original iPhones with rectangular screens).
@available(iOS 11.0, *)
var isWithinNonZeroWindowBottomSafeArea: Bool {
let view = self
// Bail if we're not in a window
guard let window = view.window else { return false }
let windowBottomSafeAreaInset = window.safeAreaInsets.bottom
// Bail if our window does not have bottom safe area insets
guard windowBottomSafeAreaInset > 0 else { return false }
// Bail if our bottom area does not match the window bottom - something else is taking care of that
guard windowBottomSafeAreaInset == view.safeAreaInsets.bottom else { return false }
// Convert our bounds to the window to get our frame within the window
let viewFrameInWindow = view.convert(view.bounds, to: window)
// true if our bottom is aligned with the window
// Note: Could add extra logic here, such as a leeway or something
let isMatchingBottomFrame = viewFrameInWindow.maxY == window.frame.maxY
return isMatchingBottomFrame
}
}