[iOS] Dynamic Change AutoLayout’s NSConstraint

Zippy Calculator‘s Japanese user wrote review.

It’s more easier to use if calculate buttons and history area position are swapped.

Zippy Calculator’s layout is horizontal layout now.
I added new feature that can swap those position. I submitted new version to App Store. If they approve the app smoothly, it will update next week.

zippy_calc_history_layout_demo

This post is how to implement this feature.

Finished Project

I made a new project to explain.

dynamic_autolayout_demo

Push below button, then swap left and right position of the views.

dynamic_autolayout1 dynamic_autolayout2

There are three NSConstraints in the view. Left, right and center.

auto_layout_xcode1

Add another three NSConstraint for swapped layout, and uncheck installed in Interface Builder.

auto_layout_xcode4

The uninstalled NSConstraint are displayed with gray color.

auto_layout_xcode2

auto_layout_xcode3

You finished opration in Interface Builder. Next, write a code.

import UIKit

class ViewController: UIViewController {
    
    //for Layout 1
    @IBOutlet weak var yellowLeadingConstraint: NSLayoutConstraint!
    @IBOutlet weak var yellowTrailingBlueLeadingConstraint: NSLayoutConstraint!
    @IBOutlet weak var blueTrailingConstraint: NSLayoutConstraint!
    
    //for Layout 2
    @IBOutlet weak var blueLeadingConstraint: NSLayoutConstraint!
    @IBOutlet weak var blueTrailingYellowLeadingConstraint: NSLayoutConstraint!
    @IBOutlet weak var yellowTrailingConstraint: NSLayoutConstraint!
    
    var isLayout1:Bool = true
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewWillLayoutSubviews() {
        updateViewConstraints()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func layoutSegmentValueChanged(sender: AnyObject) {
        let segment:UISegmentedControl = sender as! UISegmentedControl
        isLayout1 = segment.selectedSegmentIndex == 0
        
        updateViewConstraints()
    }
    
    override func updateViewConstraints() {
        if isLayout1 {
            //At first, deactivate layout2
            blueLeadingConstraint.active = false
            blueTrailingYellowLeadingConstraint.active = false
            yellowTrailingConstraint.active = false
            
            //Second, activate layout1
            yellowLeadingConstraint.active = true
            yellowTrailingBlueLeadingConstraint.active = true
            blueTrailingConstraint.active = true
            
            /**
             * You can also deactivate and activate constraints,
             * using NSLayoutConstraint's class methods.
             */
//            NSLayoutConstraint.deactivateConstraints([
//                blueLeadingConstraint,
//                blueTrailingYellowLeadingConstraint,
//                yellowTrailingConstraint
//            ])
//            NSLayoutConstraint.activateConstraints([xxxx,  xxxx,  xxx])
            
        }else{
            //At first, deactivate layout1
            yellowLeadingConstraint.active = false
            yellowTrailingBlueLeadingConstraint.active = false
            blueTrailingConstraint.active = false
            
            //Second, activate layout2
            blueLeadingConstraint.active = true
            blueTrailingYellowLeadingConstraint.active = true
            yellowTrailingConstraint.active = true
        }
        
        super.updateViewConstraints()
    }
}

It is no problem wherever you change NSConstraint’s value in the code. But if complex changing NSConstraints anyware will be problem. It prefer to change in updateViewConstraints(). In the code activate or deactivate NSConstraint’s active property. Note: at first turn off NSConstraint and turn on new NSConstraint. That’s order is important. or you see error log in the console.

I tried to call updateViewConstraints() in viewWillAppear(), but it had a problem that not work in the app’s launch time. I struggled it. I found solution. I have to call in viewWillLayoutSubviews(), and work collectly.

>> project file (.zip)

Comments are closed.