简体   繁体   中英

View height changes unexpectedly (Swift)

I have set up constraints to allow UIView cv1 to change height dynamically according to screen size, with the heights of cv2 and cv3 fixed.

For some reason, when I run this as shown, cv3 's view height becomes 172.

Using the original cv3.heightAnchor.constraint(equalToConstant: cv3Height) in one or the other or both, I run into the same problem as here (which worked perfectly for cv2 ), with the added effect that where I replace it with h3, (narrow or wide) the view jumps to 172 only in that orientation but also gives me the conflicting constraints message.

I deleted ALL other constraints without luck.

Is this another bug? If so, is there a workaround?

Here is a minimized function, the entire function below:

func setConstraints() {
            
    cv3.translatesAutoresizingMaskIntoConstraints = false

    let g = view.safeAreaLayoutGuide
    
    let cv3Height: CGFloat = 125
    let h3 = cv3.heightAnchor.constraint(equalToConstant: cv3Height)
    h3.priority = .defaultHigh
    
    narrowConstraints = [
        // set cv3 height
        //cv3.heightAnchor.constraint(equalToConstant: cv3Height),
        h3, 

        // lock left, right and bottom to safe area
        cv3.leadingAnchor.constraint(equalTo: g.leadingAnchor),
        cv3(equalTo: g.trailingAnchor),
        cv3(equalTo: g.bottomAnchor),
    ]
    
    wideConstraints = [
        // set cv3 height
        //cv3.heightAnchor.constraint(equalToConstant: cv3Height),
        h3, 

        // lock bottom and right side of cv3 to safe area
        cv3(equalTo: g.trailingAnchor),
        cv3(equalTo: g.bottomAnchor),
        
        // make them all equal widths
        cv2.widthAnchor.constraint(equalTo: cv1.widthAnchor),
        cv3.widthAnchor.constraint(equalTo: cv2.widthAnchor),
    ]

    
    // activate the commonConstraints
    NSLayoutConstraint.activate(commonConstraints)
    
    if view.frame.width > view.frame.height {
        // wider than tall, so "landscape"
        NSLayoutConstraint.deactivate(narrowConstraints)
        NSLayoutConstraint.activate(wideConstraints)
    } else {
        // taller than wide
        NSLayoutConstraint.deactivate(wideConstraints)
        NSLayoutConstraint.activate(narrowConstraints)
    }
    
}

Here is the entire function:

func setConstraints() {
    
    cv1.translatesAutoresizingMaskIntoConstraints = false
    cv2.translatesAutoresizingMaskIntoConstraints = false
    cv3.translatesAutoresizingMaskIntoConstraints = false
    
    let g = view.safeAreaLayoutGuide
    
    let cv2Height: CGFloat = 190
    let h2 = cv2.heightAnchor.constraint(equalToConstant: cv2Height)
    h2.priority = .defaultHigh
    
    let cv3Height: CGFloat = 125
    let h3 = cv3.heightAnchor.constraint(equalToConstant: cv3Height)
    h3.priority = .defaultHigh
    
    narrowConstraints = [
        
        // lock top, left and right to safe area
        cv1.topAnchor.constraint(equalTo: g.topAnchor),
        cv1.leadingAnchor.constraint(equalTo: g.leadingAnchor),
        cv1.trailingAnchor.constraint(equalTo: g.trailingAnchor),
        
        // set cv2 height
        //cv2.heightAnchor.constraint(equalToConstant: cv2Height),
        h2,

        // lock left and right to safe area
        cv2.leadingAnchor.constraint(equalTo: g.leadingAnchor),
        cv2.trailingAnchor.constraint(equalTo: g.trailingAnchor),
        
        // lock top of cv2 to bottom of cv1
        cv2.topAnchor.constraint(equalTo: cv1.bottomAnchor),
        // lock bottom of cv2 to top of cv3
        cv2.bottomAnchor.constraint(equalTo: cv3.topAnchor),
        
        // set cv3 height
        //cv3.heightAnchor.constraint(equalToConstant: cv3Height),
        h3, 

        // lock left, right and bottom to safe area
        cv3.leadingAnchor.constraint(equalTo: g.leadingAnchor),
        cv3(equalTo: g.trailingAnchor),
        cv3(equalTo: g.bottomAnchor),
    ]
    
    wideConstraints = [
        
        // lock top, bottom, and left to safe area
        cv1.topAnchor.constraint(equalTo: g.topAnchor),
        cv1.bottomAnchor.constraint(equalTo: g.bottomAnchor),
        cv1.leadingAnchor.constraint(equalTo: g.leadingAnchor),
        // lock right side of cv1 to left side of cv2
        cv1.trailingAnchor.constraint(equalTo: cv2.leadingAnchor),
        
        // lock right side of cv2 to safe area
        cv2.trailingAnchor.constraint(equalTo: g.trailingAnchor),
        // lock top of cv2 to safe area
        cv2.topAnchor.constraint(equalTo: g.topAnchor),
        // lock bottom of cv2 to top of cv3
        cv2.bottomAnchor.constraint(equalTo: cv3.topAnchor),
        
        // set cv3 height
        //cv3.heightAnchor.constraint(equalToConstant: cv3Height),
        h3, 

        // lock bottom and right side of cv3 to safe area
        cv3(equalTo: g.trailingAnchor),
        cv3(equalTo: g.bottomAnchor),
        
        // make them all equal widths
        cv2.widthAnchor.constraint(equalTo: cv1.widthAnchor),
        cv3.widthAnchor.constraint(equalTo: cv2.widthAnchor),
    ]

    
    // activate the commonConstraints
    NSLayoutConstraint.activate(commonConstraints)
    
    if view.frame.width > view.frame.height {
        // wider than tall, so "landscape"
        NSLayoutConstraint.deactivate(narrowConstraints)
        NSLayoutConstraint.activate(wideConstraints)
    } else {
        // taller than wide
        NSLayoutConstraint.deactivate(wideConstraints)
        NSLayoutConstraint.activate(narrowConstraints)
    }
    
}

I looks like the issue is the every time the function is called, you're creating new constraints but not deactivating the old constraints from the previous time it was run.

For example, the first time it's called it'll create a set of wide and narrow constraints and activate the wide set. The next time it's called it'll create a new set of wide and narrow constraints and activate the narrow constraints from the second set without deactivating the wide constraints from the first set. Your call to deactivate it being called on the second set which would have on effect.

I'd guess the conflicting constraints message is showing the constraints from the first set that haven't been deactivated.


What I would do is split your function into two separate functions.

Make a setupConstraints function that only creates the arrays of constraints and activates just the commonConstraints . The important part would be to make sure this function is called only once.

Make a activateConstraintsForOrientation function that only contains the if statement at the bottom of your function. You'd then call this function anytime you think the orientation has changed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM