Search code examples
iosiphoneautolayoutiphone-x

iPhone X Aligning object bottom to Safe Area ruins look on other devices


Question on iPhone X Autolayout quirks.

I have two buttons, previously these would be aligned bottom to the superview with an offset of 20 to not have them touching the bottom of the screen (I've since changed the link to Safe Area not Superview).

Here's the original setup: iPhone 8 - Setup

Looks good as expected on older iPhones. iPhone 8 20 Offset

Now the 20 constant on the bottom constraint now makes the buttons look funky and too far up from the home bar on iPhone X. iPhone X 20 Offset

Logically, I need to remove the 20 constant from the constraint on iPhone X and have the buttons aligned directly with the bottom of the safe area. iPhone X - Setup

Looks good on iPhone X now. iPhone X 0 Offset

But now it's placing the buttons too close to the bottom of the screen on non home bar phones. iPhone 8 0 Offset

Any immediate solutions to this problem that I missed in the Apple docs? Can't use size classes since the iPhone X size class overlaps with the other iPhones in this case.

I could easily code to detect for iPhone X and set the constant on the constraint to 0 but I was hoping for a more elegant solution.

Thanks,


Solution

  • The Apple Docs states there is a new declaration in iOS 11 which can be a solution to this problem. Currently iPhone X and iPhone 8 share the same size class so we must come up with another solution.

    var additionalSafeAreaInsets: UIEdgeInsets { get set }

    Add the following code below in your AppDelegate and all children of the rootViewController will inherit the additional safe area. Example screenshots below describe this behavior.

    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 Aligned to Safe Area

    enter image description here

    iPhone 8 Interface Builder Aligned to Safe Area

    enter image description here

    iPhone X Simulator - Master Screen

    enter image description here

    iPhone X Simulator - Details Screen

    enter image description here

    iPhone 8 Simulator - Master Screen

    enter image description here

    iPhone 8 Simulator - Details Screen

    enter image description here