有关iPhone X Autolayout怪癖的提问。
我有两个按钮,以前这些按钮会在超级视图的底部对齐,偏移量为20,不会让它们触及屏幕的底部(我已经将链接更改为安全区域而不是Superview)。
现在,底部约束的20常量现在使得按钮看起来很时髦,并且离iPhone X上的主页栏太远了。
逻辑上,我需要从iPhone X上的约束中删除20常量,并使按钮直接与安全区域的底部对齐。
我在Apple文档中遗漏了这个问题的直接解决方案吗?无法使用大小类,因为在这种情况下,iPhone X大小类与其他iPhone重叠。
我可以很容易地编码来检测iPhone X并将约束上的常量设置为0,但我希望有一个更优雅的解决方案。
谢谢,
答案 0 :(得分:8)
Apple Docs声称iOS 11中有一个新的声明可以解决这个问题。目前iPhone X和iPhone 8共享相同的大小类,因此我们必须提出另一种解决方案。
var additionalSafeAreaInsets: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 Interface Builder与安全区域对齐
iPhone 8界面生成器与安全区域对齐
iPhone X模拟器 - 主屏幕
iPhone X模拟器 - 详细信息屏幕
iPhone 8模拟器 - 主屏幕
iPhone 8模拟器 - 详细信息屏幕
答案 1 :(得分:3)
直接从情节提要中实现此目的的另一种方法是创建两个约束:
1.在元素和安全区域之间,具有250优先级和0常量
2.在元素和超级视图底部之间,具有750优先级和20常数且具有greater Than or Equal
关系。
答案 2 :(得分:1)
花了一些时间尝试解决此类问题后,最初使用Marcos's solution,我遇到了无法解决的问题-特别是在“安全区域”非零高度,但不是屏幕的安全区域,则表示偏移量是0,而不是最小值20。示例是通过additionalSafeAreaInsets
设置了底部安全区域的任意视图控制器。
解决方案是,当安全区域插入量发生变化时,检查我们的视图是否与具有非零安全区域的窗口对齐,并根据此调整约束的底部偏移量到安全区域。以下内容会导致矩形样式屏幕的底部偏移20pt,在带有安全区域样式屏幕的全屏(iPhone X,最新iPad Pro,iPad滑盖等)中导致偏移20pt。
// 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's safe area. The reason it's the window rather than
/// the screen is that on iPad, slide over apps and such also have this nonzero safe area. Basically anything that doesn't 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 doesn't have bottom safe area insets
guard windowBottomSafeAreaInset > 0 else { return false }
// Bail if our bottom area doesn't match the window's 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
}
}