iOS 11 safe area layout guide backwards compatibility

前端 未结 16 1107
忘了有多久
忘了有多久 2020-12-02 05:42

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

相关标签:
16条回答
  • 2020-12-02 06:09

    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

    0 讨论(0)
  • 2020-12-02 06:10

    I managed to work with the new Safe Area layout guides and maintain backwards compatibility with iOS 9 and iOS 10: (EDIT: as pointed out in the comments by @NickEntin, this implementation will presume there is a status bar present, which won't be true in landscape on the iPhone X. Resulting in to much space to the top (20 points). It will run perfectly fine however.

    E.g. if you want a view to be 10 points below the status bar (and 10 points below the sensor housing on iPhone X):

    1. In your XIB, go to File Inspector and enable the safe are by checking Use Safe Area Layout Guides.
    2. Create a constraint from the view's top to the main view's top, with >= (greater than or equal) constraint, constant 30 (30 because we want 10 points spacing to the status bar which is 20 points high) and priority High (750).
    3. Create a constraint from the view's top to the Safe Area's top, with = (equal) constraint, constant 10 and priority Low (250).

    The same can be done for a view at the bottom (and for leading/trailing or left/right to the Safe Area):

    1. In your XIB, go to File Inspector and enable the safe are by checking Use Safe Area Layout Guides.
    2. Create a constraint from the view's bottom to the main view's bottom, with >= (greater than or equal) constraint, constant 10 and priority High (750).
    3. Create a constraint from the view's bottom to the Safe Area's bottom, with = (equal) constraint, constant 10 and priority Low (250).
    0 讨论(0)
  • 2020-12-02 06:11

    I have backward compatibility issues with WKWebView & Safe Area on iOS 9. By some reason, WKWebView simply ignores safe area layout settings.

    0 讨论(0)
  • 2020-12-02 06:14

    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:

    0 讨论(0)
  • 2020-12-02 06:19

    The backwards compatibility of Safe Areas for iOS 9 & iOS 10 only works if you are using storyboards. If you are using xibs, there is no layout guide to fall back to. https://forums.developer.apple.com/thread/87329

    The workarounds seem to be either

    (a) migrate your xibs into storyboards, or

    (b) add some additional constraints programmatically.

    If (a) is not really an option, the manual approach will be something like this:

    Assuming you have a view in your xib that you want to keep within the safe area (i.e. below any status bar or navigation bar).

    1. Add constraints in your xib between your view and the safe area for iOS 11. Assign the top constraint to a priority of 750.

    2. In your view controller, add a property:

      @property (nonatomic, strong) NSLayoutConstraint *topLayoutConstraint;
      

      And then in viewDidLayoutSubviews:

      - (void)viewDidLayoutSubviews {
          [super viewDidLayoutSubviews];
      
          if (@available(iOS 11, *)) {
              // safe area constraints already set
          }
          else {
              if (!self.topLayoutConstraint) {
                  self.topLayoutConstraint = [self.<yourview>.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor];
                  [self.topLayoutConstraint setActive:YES];
              }
          }
      }
      

      The new constraint will only be created for iOS 9 & iOS 10, has a default priority of 1000, and overrides the one in the xib.

    3. Repeat for a bottom constraint if you need to avoid the home indicator.

    Swift 4 version:

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        if #available(iOS 11, *) {
            // safe area constraints already set
        } else {
            if topLayoutConstraint == nil {
                topLayoutConstraint = <yourview>.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
                topLayoutConstraint?.isActive = true
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 06:20

    for iOS 9:

    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
    
        self.navigationController.navigationBar.translucent = NO;
    }
    

    if you enable autolayout and add view constraints to the safe area, you are good to go above iOS 11+ , but it may not work well with iOS 9 and your view may appear under your navigation bar. To solve this problem, you can disable translucent attribute in 'viewWillAppear:(BOOL)animated' method.

    To not to break previous state of translucent attribute of your navigation bar, you should keep previous value and re-set it in 'viewWillDisappear:(BOOL)animated'

    @interface YourViewController ()
    @property (nonatomic, assign) BOOL translucentState;
    @end
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
    
        self.translucentState = self.navigationController.navigationBar.translucent;
        self.navigationController.navigationBar.translucent = NO;
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
    
        self.navigationController.navigationBar.translucent = self.translucentState;
    }
    

    P.S. should not use edgesForExtendedLayout with doing this:

    self.edgesForExtendedLayout = UIRectEdgeNone;
    

    Check out Apple documentation: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621515-edgesforextendedlayout

    0 讨论(0)
提交回复
热议问题