Search code examples
iosswiftxcodeuiimageviewconstraints

Remove trailing constraint with ib action- Swift


I have created a View Controller with a segmented controller at the top. When you tap the segmented controller it just acts as a button and changes whether the imageView inside of the controller is portrait mode or in landscape mode just by calling a function that changes it to the according dimensions.

My problem is that the way I made it change is I just added contraints to the imageView, but when changing to landscape mode the trailing constraint doesn't get removed.

And, this is the code to change the imageView to portrait (The imageView already has the top and bottom contraints on it) :

func portraitContraints() {
    
    NSLayoutConstraint.activate ([
    // the trailing contraint
    imageView.trailingAnchor.constraint(equalTo: viewContainer.safeAreaLayoutGuide.trailingAnchor, constant: -75), // this is the contraints that doesn't get removed
    // the leading contraints
    imageView.leadingAnchor.constraint(equalTo: viewContainer.safeAreaLayoutGuide.leadingAnchor, constant: 75
    ])
   
    // the aspect ratio contraints
    imageView.addConstraint(NSLayoutConstraint(item: self.imageView as Any,attribute: .height,relatedBy: .equal,toItem: self.imageView,attribute: .width,multiplier: (4.0 / 3.0),constant: 0))
}

This is the code to change the imageView to landscape:

func landscapeContraints() {
    
    NSLayoutConstraint.activate([
    // the trailing contraints
    imageView.trailingAnchor.constraint(equalTo: viewContainer.safeAreaLayoutGuide.trailingAnchor, constant: 0),
    // the leading contraint
    imageView.leadingAnchor.constraint(equalTo: viewContainer.safeAreaLayoutGuide.leadingAnchor, constant: 0)
    ])
    // the aspect ratio contraint
    imageView.addConstraint(NSLayoutConstraint(item: self.imageView as Any,attribute: .height,relatedBy: .equal,toItem: self.imageView,attribute: .width,multiplier: (9.0 / 16.0),constant: 0))
}

This code works like a charm, except the only problem is that the trailing constraint from the portrait mode will stay on the view (the -75 constant constraint).

Landscape looks like this (notice the right side constant is -75): enter image description here

Portrait looks like this:

portrait mode


Solution

  • You can do this by adding both ratio constraints, with different priorities. Change the priorities to make the desired ratio "active".

    And, create leading and trailing constraint vars, so you can change their constants.

    Here's a quick example:

    class ToggleConstraintsViewController: UIViewController {
        
        let imageView = UIImageView()
        
        var isPortrait: Bool = true
        
        var portraitConstraint: NSLayoutConstraint!
        var landscapeConstraint: NSLayoutConstraint!
        var leadingConstraint: NSLayoutConstraint!
        var trailingConstraint: NSLayoutConstraint!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            imageView.backgroundColor = .blue
            
            imageView.translatesAutoresizingMaskIntoConstraints = false
            
            view.addSubview(imageView)
            
            let g = view.safeAreaLayoutGuide
            
            portraitConstraint = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 4.0/3.0)
            portraitConstraint.priority = .defaultHigh
            landscapeConstraint = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 9.0/16.0)
            landscapeConstraint.priority = .defaultLow
            
            leadingConstraint = imageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 75.0)
            trailingConstraint = imageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -75.0)
            
            NSLayoutConstraint.activate([
                // y-position constraint does not change
                imageView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
                
                // these will have their priorities changed when desired
                portraitConstraint,
                landscapeConstraint,
                
                // these will have their constants changed when desired
                leadingConstraint,
                trailingConstraint,
            ])
            
            let t = UITapGestureRecognizer(target: self, action: #selector(self.toggleOrientation(_:)))
            view.addGestureRecognizer(t)
        }
    
        @objc func toggleOrientation(_ g: UITapGestureRecognizer) -> Void {
            isPortrait.toggle()
            
            portraitConstraint.priority = isPortrait ? .defaultHigh : .defaultLow
            landscapeConstraint.priority = isPortrait ? .defaultLow : .defaultHigh
            
            leadingConstraint.constant = isPortrait ? 75.0 : 0.0
            trailingConstraint.constant = isPortrait ? -75.0 : 0.0
            
        }
        
    }
    

    Each time you tap the view, the imageView's width:height ratio will change, and the leading/trailing constraint constants will change.