skip to Main Content

Posting a question for the first time here.

So I have been trying to make an animation of an UIimageView. I did that so far. So the image moves from the middle of the screen to the top. I want to be able to make that animation with constraints. But while trying to add some constraints, I receive this error "Unable to activate constraint with anchors error".

here is the code which I try to add some constraints to banditLogo imageview.

        override func viewDidLoad() {
    super.viewDidLoad()
    
    
    
    view.addSubview(banditLogo)
    
    view.translatesAutoresizingMaskIntoConstraints = false // autolayout activation
    
    chooseLabel.alpha = 0
    signInButtonOutlet.alpha = 0
    
    self.banditLogo.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 304).isActive = true
    self.banditLogo.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 94).isActive = true
    self.banditLogo.widthAnchor.constraint(equalToConstant: 224).isActive = true
    self.banditLogo.heightAnchor.constraint(equalToConstant: 289).isActive = true

  }

and here is the func that makes the animation.

this func is being called in viewDidAppear and animatedImage variable of the function is referred to banditLogo UIimageView.
so when the view screen loads up, the image moves to top of the view.

func logoAnimate(animatedImage: UIImageView!, animatedLabel: UILabel!) {
        UIView.animate(withDuration: 1.5, delay: 1, options: [.allowAnimatedContent]) {
        animatedImage.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 5).isActive = true
        animatedImage.leftAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leftAnchor, constant: 94).isActive = true

    }   completion: { (true) in
        UIView.animate(withDuration: 0.25) {
            animatedLabel.alpha = 1
      }
      
    }
}

2

Answers


  1. I animate views that have constraints by changing constraints, not setting them. Leave the constraints that are static "as is" – that is, use isActive = true. But those you wish to change? Put them in two arrays and activate/deactivte them. Complete the animation like you are by using UIView.animate.

    For instance, let’s say you wish to move banditLogo from top 304 to top 5, which appears to me to be what you trying to do. Leave all other constraints as is – left (which your code doesn’t seem to change), height, and width. Now, create two arrays:

    var start = [NSLayoutConstraint]()
    var finish = [NSLayoutConstraint]()
    

    Add in the constraints that change. Note that I’m not setting them as active:

    start.append(banditLogo.topAnchor.constraint(equalTo: safeAreaView.topAnchor, constant: 305))
    finish.append(banditLogo.topAnchor.constraint(equalTo: safeAreaView.topAnchor, constant: 5))
    

    Initialize things in viewDidLoad or any other view controller method as needed:

    NSLayoutConstraint.activate(start)
    

    Finally, when you wish to do the animation, deactivate/activate and tell the view to show the animation:

    NSLayoutConstraint.deactivate(start)
    NSLayoutConstraint.activate(finish)
    UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() }
    

    Last piece of critique, made with no intent of being offending.

    Something in your code posted feels messy to me. Creating a function to move a single view should directly address the view IMHO, not pass the view into it. Maybe you are trying to move several views this way – in which case this is good code – but nothing in your question suggests it. It’s okay to do the animation in a function – that way you can call it when needed. I do this all the time for something like this – sliding a tool overlay in and out. But if you are doing this to a single view, just address it directly. The code is more readable to other coders.

    Also, my preference for the start is in viewDidLoad unless the VC is part of a navigation stack. But in that case, don’t just use viewDidAppear, set things back to start in viewDidDisappear.

    EDIT: looking at the comments, I assumed that yes you have already used translatesAutoresizingMaskIntoConstraints = false properly on every view needed.

    Login or Signup to reply.
  2. You may find it easier to create a class-level property to hold the image view’s top constraint, then change that constraint’s .constant value when you want to move it.

    Here’s a quick example – tapping anywhere on the view will animate the image view up or down:

    class AnimLogoViewController: UIViewController {
    
        let banditLogo = UIImageView()
        
        // we'll change this constraint's .constant to change the image view's position
        var logoTopConstraint: NSLayoutConstraint!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            if let img = UIImage(systemName: "person.fill") {
                banditLogo.image = img
            }
            
            view.addSubview(banditLogo)
            
            // I assume this was a typo... you want to set it on the image view, not the controller's view
            //view.translatesAutoresizingMaskIntoConstraints = false // autolayout activation
            banditLogo.translatesAutoresizingMaskIntoConstraints = false // autolayout activation
    
            // create the image view's top constraint
            logoTopConstraint = banditLogo.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 304)
            // activate it
            logoTopConstraint.isActive = true
            
            // non-changing constraints
            self.banditLogo.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 94).isActive = true
            self.banditLogo.widthAnchor.constraint(equalToConstant: 224).isActive = true
            self.banditLogo.heightAnchor.constraint(equalToConstant: 289).isActive = true
    
            // animate the logo when you tap the view
            let t = UITapGestureRecognizer(target: self, action: #selector(self.didTap(_:)))
            view.addGestureRecognizer(t)
        }
        
        @objc func didTap(_ g: UITapGestureRecognizer) -> Void {
            // if the logo image view is at the top, animate it down
            //  else, animate it up
            if logoTopConstraint.constant == 5.0 {
                logoTopConstraint.constant = 304.0
            } else {
                logoTopConstraint.constant = 5.0
            }
            UIView.animate(withDuration: 1.5, animations: {
                self.view.layoutIfNeeded()
            })
        }
    
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search