为什么 NSLayoutConstraint 动画只能工作一次?

why NSLayoutConstraint animation works only one time?

在我的项目中,我有一个 someView(UIView 类型),它位于 holderView(UIView 类型)内部。 someView 有 2 个状态。 在状态 1 中,someView 变大,在步骤 2 中,someView 变小。 当某些条件正确时,someView 显示在状态 1 中,当它不正确时,someView 显示在状态 2 中。 我想用动画来做这个,我在我的项目中使用 NSLayoutConstraint。我正在使用此代码(someViewHeight 和 someViewWidth 属于 NSLayoutConstraint 类型):

func minimizeSomeView() {
    someViewHeight.constant = holderView.frame.width
    someViewWidth.constant  = holderView.frame.height

    UIView.animate(withDuration: 1.0) {
        self.view.layoutIfNeeded()
    }
}

func maximizeSomeView() {
    someViewHeight.constant = holderView.frame.width/4
    someViewWidth.constant  = holderView.frame.height/4

    UIView.animate(withDuration: 1.0) {
        self.view.layoutIfNeeded()
    }
}

if someTextField.text != nil {
    minimizeSomeView()
} else. {
    maximizeSomeView()
}

我在 viewDidLayoutSubviews() 中定义了 someViewHeight 和 someViewWidth,这是我的代码:

class ViewController: UIViewController {

    private var holderView, someView: UIView!
    private var someViewHeight,someViewWidth: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
    
        super.viewDidLoad()
    
        view.backgroundColor = .white
    
        // Holder View
        holderView = UIView()
        holderView.backgroundColor = .red
        holderView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(holderView)
    
        NSLayoutConstraint.activate([
            holderView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            holderView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            holderView.topAnchor.constraint(equalTo: view.topAnchor, constant: 120),
            holderView.heightAnchor.constraint(equalToConstant: view.frame.height * 40 / 100)
        ])
    
        // Some View
        someView = UIView()
        someView.backgroundColor = .blue
        someView.translatesAutoresizingMaskIntoConstraints = false
        holderView.addSubview(someView)
    
        someView.topAnchor.constraint(equalTo: holderView.topAnchor).isActive = true
        someView.trailingAnchor.constraint(equalTo: holderView.trailingAnchor).isActive = true
    
        // Some TextField
        let someTextField = UITextField()
        someTextField.backgroundColor = .yellow
        someTextField.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(someTextField)
    
        NSLayoutConstraint.activate([
            someTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            someTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            someTextField.topAnchor.constraint(equalTo: view.bottomAnchor, constant: -100),
            someTextField.heightAnchor.constraint(equalToConstant: 50)
        ])
    
        someTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
    }


    override func viewDidLayoutSubviews() {
    
        someViewHeight = someView.heightAnchor.constraint(equalToConstant: holderView.frame.width)
        someViewHeight.isActive = true
        someViewWidth = someView.widthAnchor.constraint(equalToConstant: holderView.frame.width)
        someViewWidth.isActive = true
    }

    func minimizeSomeView() {
    
        someViewHeight.constant = holderView.frame.width/4
        someViewWidth.constant  = holderView.frame.height/4
    
        UIView.animate(withDuration: 1.0) {
            self.view.layoutIfNeeded()
        }
    }

    func maximizeSomeView() {
    
        someViewHeight.constant = holderView.frame.width
        someViewWidth.constant  = holderView.frame.height
    
        UIView.animate(withDuration: 1.0) {
            self.view.layoutIfNeeded()
        }
    }

    @objc func textFieldDidChange(_ textfield: UITextField) {

        if textfield.text!.count > 0 {
            self.minimizeSomeView()
        } else {
            self.maximizeSomeView()
        }
    }
}

您不应更新 viewDidLayoutSubviews 中的约束,尤其是在您使用自动布局时。你不需要这个块

添加约束后,您可以初始化并激活它们。 这里我们可以按比例指定约束。即 someView 的高度是 holderView 的 0.4。同样,someView 的宽度是 holderView.

的 0.4
  1. 要解决此问题,您可以执行以下更改

// 你的一些视图约束

    someView.topAnchor.constraint(equalTo: holderView.topAnchor).isActive = true
    someView.trailingAnchor.constraint(equalTo: holderView.trailingAnchor).isActive = true
    maximizeSomeView()
  1. 去掉viewDidLayouSubView代码

  2. 更新您的最大化和最小化事件。在这里我必须删除现有的约束,因为 .multiplier 是只读的 属性.

func  minimizeSomeView() {

removeExistingConstriant()

UIView.animate(withDuration: 1.0) { [unowned self] in

self.someViewWidth = self.someView.widthAnchor.constraint(equalTo: self.holderView.widthAnchor, multiplier: 1.0)

self.someViewWidth.isActive = true

self.someViewHeight = self.someView.heightAnchor.constraint(equalTo:
self.holderView.heightAnchor, multiplier: 1.0)

self.someViewHeight.isActive = true

self.view.layoutIfNeeded()

}

}
func  maximizeSomeView() {

removeExistingConstriant()

UIView.animate(withDuration: 1.0) { [unowned self] in

self.someViewWidth = self.someView.widthAnchor.constraint(equalTo: self.holderView.widthAnchor, multiplier: 0.4)

self.someViewWidth.isActive = true

self.someViewHeight = self.someView.heightAnchor.constraint(equalTo:
self.holderView.heightAnchor, multiplier: 0.4)

self.someViewHeight.isActive = true

self.view.layoutIfNeeded()

}

}
func  removeExistingConstriant(){

if  self.someViewHeight  !=  nil {

self.someViewHeight.isActive = false

}

if  self.someViewWidth  !=  nil {

self.someViewWidth.isActive = false

}

}