iOS 11 safe area layout guide backwards compatibil

2019-01-10 03:35发布

Is enabling Safe Area Layout Guides compatible to iOS below 11?

enter image description here

12条回答
三岁会撩人
2楼-- · 2019-01-10 03:39

I found a more convenient way where you only need to subclass the NSLayoutConstraintthat is pinned to your safeArea.

It's kinda hacky since you have to get the ViewController from a UIView but in my opinion, that's an easy and good alternative until Apple finally fixes backward compatibility for the safeArea in Xibs.

Subclass:

class SafeAreaBackwardsCompatabilityConstraint: NSLayoutConstraint {
private weak var newConstraint: NSLayoutConstraint?

override var secondItem: AnyObject? {
    get {
        if #available(iOS 11.0, *) {}
        else {
            if let vc = (super.secondItem as? UIView)?.parentViewController, newConstraint == nil {
                newConstraint = (self.firstItem as? UIView)?.topAnchor.constraint(equalTo: vc.topLayoutGuide.bottomAnchor)
                newConstraint?.isActive = true
                newConstraint?.constant = self.constant
            }
        }
        return super.secondItem
    }
}

override var priority: UILayoutPriority {
    get {
        if #available(iOS 11.0, *) { return super.priority }
        else { return 750 }
    }
    set { super.priority = newValue }
}
}

private extension UIView {
var parentViewController: UIViewController? {
    var parentResponder: UIResponder? = self
    while parentResponder != nil {
        parentResponder = parentResponder!.next
        if let viewController = parentResponder as? UIViewController {
            return viewController
        }
    }
    return nil
}
}

Xib:

enter image description here

查看更多
一纸荒年 Trace。
3楼-- · 2019-01-10 03:43

There's definitely at least one backwards compatibility issue with iOS 11's safe area constraints that I've observed in the Xcode 9 GM--pushed view controllers with safe area constraints.

If your navigation bar is hidden and you push a safe area top-constrained view, the pushed view will overlap the status bar on iOS 9 & 10.

If the navigation bar is visible, and "under top bars" disabled, the pushed view will still slide up under the nav bar to get to the top of the screen. The navigation bar is placed correctly.

On iOS 11, the layout will be correct in both cases.

Here's a simple example: http://www.filedropper.com/foobar

And here's a video of it w/nav bar hidden (iOS 10.3 on left, iOS 11 on right): https://vimeo.com/234174841/1e27a96a87

Here's a version where the nav bar is visible (enabled in the nib): https://vimeo.com/234316256/f022132d57

I filed this as Radar #34477706.

Thanks to @Sander for pointing out the nav bar visible case.

查看更多
放我归山
4楼-- · 2019-01-10 03:43

Yes, your project/app will work in iOS versions prior to iOS 11 without any issue. In iOS versions prior to 11, it replaces/considers Safe Area Layout into normal AutoLayout and follows Rules of Top and Bottom layout guide.

I tested my existing project with and without 'SafeAreaLayout' on both platforms (iOS 11 and backward iOS 10). It's working fine.

Just make sure:

  • If you have designed your project/User Interface in AutoLayout; constraints of your UIElement follows/relative to Top and Bottom layout guide (not to superview). So by a single click (enable) on SafeAreaLayout option, will automatically implement SafeArea layout properly for all Interface Builders files in your storyboard.

  • If you have designed your project/User Interface in SafeAreaLayout; then it will automatically follow Top and Bottom layout guide in backward iOS.

Here is sample snapshot with result, By enabling or disabling Safe Area layout, won't effect on existing design.

Safe Area Layout: enter image description here

AutoLayout

enter image description here

In short, an answer to your question is: "Enabling Safe Area Layout Guides compatible to iOS prior to 11"

You can implement Safe Area Layout in your project/app and it will work fine with previous iOS versions by converting Safe Area Layout into Top and Bottom Layout.

查看更多
做自己的国王
5楼-- · 2019-01-10 03:50

When you have a generic ViewController that all your ViewControllers extend, another solution would be to put the items that should be adjusted in an IBOutletCollection and adjust them programmatically in that GenericViewController. Here's my code :

@IBOutlet var adjustTopSpaceViews: [UIView]?

override func viewDidLoad() {
    super.viewDidLoad()
    adjustViews()
    ....
}

func adjustViews() {
    guard let views = adjustTopSpaceViews,
        ProcessInfo.processInfo.operatingSystemVersion.majorVersion < 11 else {
            return
    }
    let statusBarHeight = UIApplication.shared.statusBarFrame.height
    for subview in views {
        subview.superview?.constraints.filter({ (constraint) -> Bool in
            return constraint.firstAttribute == .top
                && constraint.secondAttribute == .top
                && (constraint.firstItem as? UIView == subview || constraint.secondItem as? UIView == subview)
        }).forEach({ (constraint) in
            constraint.constant += (constraint.firstItem as? UIView == subview) ? statusBarHeight : -statusBarHeight
        })
    }
}
查看更多
淡お忘
6楼-- · 2019-01-10 03:51

Here's what I did with my Projects

In my case both my topConstraint and bottomConstraints are @IBOutlets. This is also compatible for iOS 8.

My initial configuration for the top and bottom constraints are for the normal iPhones, which is why I'm only editing the constraints for iPhone X

    // iOS 11 Layout Fix. (For iPhone X)
    if #available(iOS 11, *) {
        self.topConstraint.constant = self.topConstraint.constant + self.view.safeAreaInsets.top

        self.bottomConstraint.constant = self.bottomConstraint.constant + self.view.safeAreaInsets.bottom
    }

.

NOTE: self.view is your superView which is why I'm using it for safeAreaInsets

查看更多
ゆ 、 Hurt°
7楼-- · 2019-01-10 03:52

I was using this in Objective-C with good result for iOS 10. In case you use SafeArea in your xib then you may add in your viewDidLoad:

if (@available(iOS 11.0, *)) {}
else {
    self.edgesForExtendedLayout = UIRectEdgeNone;
}
查看更多
登录 后发表回答