Search code examples
iosswiftnslayoutanchorlayout-anchor

How to update anchor constraint in swift


I would like to create a menu like android in iOS. I'm using layout constraints to setup constraints. I'm facing the issue when I'm trying to update the left constraint of an image on button click (It should animate to clicked button's position). Can anyone help me? It should support landscape and portrait.

I don't want to user third party code and don't want to use the NSLayoutconstraint.

Landscape image Potrait Image

Here is my code.

class MenuViewController: UIViewController {
            var buttons: [UIButton] = []
            var imageView : UIImageView!

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

            override func viewDidAppear(_ animated: Bool) {
                super.viewDidAppear(animated)
                addMenuItems()
            }

            func addMenuItems() {

                for _ in 0...4 {
                    let button : UIButton = UIButton()
                    self.view.addSubview(button)
                    button.backgroundColor = UIColor.darkGray
                    button.addTarget(self, action: #selector(didSelectedButton(_:)), for: .touchUpInside)
                    self.buttons.append(button)

                }

                for (index, button) in buttons.enumerated() {

                    button.setTitle(" \(index)", for: .normal)
                    if index == 0 {
                        button.translatesAutoresizingMaskIntoConstraints = false
                        button.leftAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leftAnchor, constant: 0).isActive = true
                        button.widthAnchor.constraint(greaterThanOrEqualToConstant: 1).isActive = true
                        button.heightAnchor.constraint(equalToConstant: 50).isActive = true
                        button.topAnchor.constraint(equalTo: self.view.topAnchor, constant:200).isActive = true

                        self.imageView = UIImageView()
                        self.view.addSubview(self.imageView)
                        self.imageView.backgroundColor = UIColor.red

                        self.imageView.translatesAutoresizingMaskIntoConstraints = false
                        self.imageView.leftAnchor.constraint(equalTo: button.leftAnchor, constant: 0).isActive = true
                        self.imageView.widthAnchor.constraint(equalTo: button.widthAnchor, multiplier: 1).isActive = true
                        self.imageView.heightAnchor.constraint(equalToConstant: 4).isActive = true
                        self.imageView.topAnchor.constraint(equalTo: self.view.topAnchor, constant:250).isActive = true

                    }
                    else {

                        let preButton = buttons\[index - 1\]
                        button.translatesAutoresizingMaskIntoConstraints = false
                        button.leftAnchor.constraint(equalTo: preButton.rightAnchor, constant: 0).isActive = true
                        button.widthAnchor.constraint(equalTo: preButton.widthAnchor, multiplier: 1).isActive = true
                        button.heightAnchor.constraint(equalToConstant: 50).isActive = true
                        button.topAnchor.constraint(equalTo: self.view.topAnchor, constant:200).isActive = true
                        if index == self.buttons.count - 1 {
                            button.rightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.rightAnchor, constant: 0).isActive = true
                        }
                    }

                }

            }

            @objc func didSelectedButton(_ button:UIButton) {
                UIView.animate(withDuration: 1, animations: {
                    self.imageView.leftAnchor.constraint(equalTo: button.leftAnchor).isActive = true
                    self.imageView.layoutIfNeeded()
                }, completion: nil)
            }

        }

Solution

  • Using anchors creates NSLayoutContraints. You need to deactivate the previous ones when you create new ones.

    Add this property to your MenuViewController:

    var imageLeftConstraint: NSLayoutConstraint?
    

    Set it when you first create the constraint:

    imageLeftConstraint = self.imageView.leftAnchor.constraint(equalTo: button.leftAnchor, constant: 0)
    imageLeftConstraint?.isActive = true
    

    Then use it to deactivate the previous constraint before adding a new one:

    @objc func didSelectedButton(_ button:UIButton) {
        imageLeftConstraint?.isActive = false
        imageLeftConstraint = self.imageView.leftAnchor.constraint(equalTo: button.leftAnchor)
        imageLeftConstraint?.isActive = true
        UIView.animate(withDuration: 1, animations: {
            self.view.layoutIfNeeded()
        }, completion: nil)
    }
    

    Note: You need to call layoutIfNeeded() on the superView of imageView (aka self.view) because the constraint between button and imageView will be added to their common ancestor (self.view).