iPhone X将对象底部对齐到安全区域废墟在其他设备上查找

时间:2017-10-23 00:06:54

标签: ios iphone autolayout iphone-x

有关iPhone X Autolayout怪癖的提问。

我有两个按钮,以前这些按钮会在超级视图的底部对齐,偏移量为20,不会让它们触及屏幕的底部(我已经将链接更改为安全区域而不是Superview)。

这是原始设置: iPhone 8 - Setup

在较旧的iPhone上看起来不错。 iPhone 8 20 Offset

现在,底部约束的20常量现在使得按钮看起来很时髦,并且离iPhone X上的主页栏太远了。 iPhone X 20 Offset

逻辑上,我需要从iPhone X上的约束中删除20常量,并使按钮直接与安全区域的底部对齐。 iPhone X - Setup

现在在iPhone X上看起来不错。 iPhone X 0 Offset

但现在它将按钮放在非家用酒吧电话的屏幕底部附近。 iPhone 8 0 Offset

我在Apple文档中遗漏了这个问题的直接解决方案吗?无法使用大小类,因为在这种情况下,iPhone X大小类与其他iPhone重叠。

我可以很容易地编码来检测iPhone X并将约束上的常量设置为0,但我希望有一个更优雅的解决方案。

谢谢,

3 个答案:

答案 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与安全区域对齐

enter image description here

iPhone 8界面生成器与安全区域对齐

enter image description here

iPhone X模拟器 - 主屏幕

enter image description here

iPhone X模拟器 - 详细信息屏幕

enter image description here

iPhone 8模拟器 - 主屏幕

enter image description here

iPhone 8模拟器 - 详细信息屏幕

enter image description here

答案 1 :(得分:3)

直接从情节提要中实现此目的的另一种方法是创建两个约束:
 1.在元素和安全区域之间,具有250优先级和0常量
 2.在元素和超级视图底部之间,具有750优先级和20常数且具有greater Than or Equal关系。

enter image description here

答案 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

    }

}