Evgeniy Kleban
Evgeniy Kleban

Reputation: 6965

Animate constraints change UIViewController

I have "error" view, that is placed above navigation bar and hidden. When error occured, i want that view to smoothly show from top. I tried:

class AuthViewController: UIViewController {

  let error: ErrorView = {
    let error = ErrorView()
    error.setup()
    return error
  }()
  var topAnchor: NSLayoutConstraint!
  var botAnchor: NSLayoutConstraint!

  override func viewDidLoad() {
    setupErrorView()
  }

  private func setupErrorView(){
    view.addSubview(error)
    botAnchor = error.bottomAnchor.constraint(equalTo: view.topAnchor)
    botAnchor.isActive = true
    topAnchor = error.topAnchor.constraint(equalTo: view.topAnchor, constant: CGFloat(Offsets.navigationAndStatusBarHeight))
    topAnchor.isActive = false
    error.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    error.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
  }

  func showError(_ text: String){
    UIView.animate(withDuration: 2.0) {[weak self] in
      guard let weakSelf = self else { return }
      print("attempt to animate")
      weakSelf.error.show(text)
      weakSelf.botAnchor.isActive = false
      weakSelf.topAnchor.isActive = true
      weakSelf.view.setNeedsLayout()
    }
  }
}

class ErrorView: UIView {

  private var label: UILabel = {
    return LabelSL.regular()
  }()

  fileprivate func setup(){
    translatesAutoresizingMaskIntoConstraints = false
    backgroundColor = Theme.Color.orange.value
    addSubview(label)
  }

  fileprivate func show(_ text: String){
    let sideOffset: CGFloat = 10
    let verticalOffset: CGFloat = 10
    label.text = text
    label.topAnchor.constraint(equalTo: topAnchor, constant: verticalOffset).isActive = true
    label.leftAnchor.constraint(equalTo: leftAnchor, constant: sideOffset).isActive = true
    label.rightAnchor.constraint(equalTo: rightAnchor, constant: -sideOffset).isActive = true
    label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -verticalOffset).isActive = true
  }
}

Animation should be done when func showError(_ text: String){ method called, but it's not. View just appear instantly.

Upvotes: 0

Views: 134

Answers (1)

pacification
pacification

Reputation: 6018

You're trying to animate constraints in wrong way. You should set constraints outside of animation block and only layoutIfNeeded in animation:

func showError(_ text: String){
    botAnchor.isActive = false
    topAnchor.isActive = true
    error.show(text)
    UIView.animate(withDuration: 2.0) {
        self.view.layoutIfNeeded()
    }
}

Upvotes: 2

Related Questions