why the autolayout is not updated if I use IBDesig

2019-04-12 18:44发布

问题:

here is the project of this problem in the google drive: https://drive.google.com/file/d/1Js0t-stoerWy8O8uIOJl_bDw-bnWAZPr/view?usp=sharing

I make a custom navigation bar (the red view in the picture below) using IBDesignable code below. I want, if the iPhone has top notch like iPhone X, iPhone XR, then the height of custom navigation bar is 88, otherwise the height is 64. I make an IBDesignable code so I can reuse this code

import UIKit

@IBDesignable
class CustomParentNavigationBarView: UIView {

    override func awakeFromNib() {
        super.awakeFromNib()
        self.setHeight()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        self.setHeight()
    }

    func setHeight() {
        let deviceHasTopNotch = checkHasTopNotchOrNot()
        layer.frame.size.height = deviceHasTopNotch ? 88 : 64
    }

    func checkHasTopNotchOrNot() -> Bool {
        if #available(iOS 11.0, tvOS 11.0, *) {
            // with notch: 44.0 on iPhone X, XS, XS Max, XR.
            // without notch: 20.0 on iPhone 8 on iOS 12+.
            return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
        }
        return false
    }

}

I assign this CustomParentNavigationBarView to the red view

and then, I put a label. that should located exactly below the red view (custom navigation bar, that uses IBDesignable)

it looks great if I run the app in the iPhone doesn't have top notch like iPhone8

but, if I run the app in iPhone that has top notch like iPhone XR, the label now inside the red view, it seems still following the previous autolayout to the red view height that previously 64 (in storyboard I am using iPhone 8), so when the custom navigation bar update to 88 when it is running on iPhone XR, the label still follow the previous 64 height, then it will locate inside the red view

here is the autolayout of custom nav bar (red view)

and here is the autolayout of the label

how to solve this issue ?

回答1:

The problem here is that you specify the frame height in setHeight(), but also specify the height with a layout constraint inside storyboard. One way to fix this is to specify an intrinsicContentSize for your view and not specify the height as a layout constraint in storyboard. (I.o.w. similar to the way you rely on the width of the label to be calculated for you)

@IBDesignable
class CustomParentNavigationBarView: UIView {

    lazy var deviceHasTopNotch: Bool = {
        if #available(iOS 11.0, tvOS 11.0, *) {
            // with notch: 44.0 on iPhone X, XS, XS Max, XR.
            // without notch: 20.0 on iPhone 8 on iOS 12+.
            return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
        }
        return false
    }()

    override var intrinsicContentSize: CGSize {
        let height:CGFloat = deviceHasTopNotch ? 88 : 64
        let width = super.intrinsicContentSize.width //Use whatever you prefer here.  Ex.UIScreen.main.bounds.size.width
        return CGSize(width: width, height: height)
    }
}

In Storyboard

Remove your current height constraint for the custom view. Set the Intrinsic Size property to Placeholder.

Example of the layout constraints for the custom nav bar.