【问题标题】:Swift - Animate Constraints in Custom UIViewSwift - 自定义 UIView 中的动画约束
【发布时间】:2021-06-23 12:02:49
【问题描述】:

我正在尝试为自定义 UIView 中的约束设置动画。结束布局是正确的,但我无法让UIView.animate 正常工作,而且我看到每个人都在从视图控制器调用self.view.layoutIfNeeded(),所以这也许是我的问题。我在自定义视图本身上调用self.layoutIfNeeded()。如果层次结构很重要,我有如下内容:ViewController > TableView > Cell > View > AnimationView。

class AnimationView: UIView {
    
    // MARK: - Properties
    let ratioMultiplier: CGFloat = 0.28
    var listLeadingConstraint: NSLayoutConstraint!
    var listTrailingConstraint: NSLayoutConstraint!
    var checkmarkLeadingConstraint: NSLayoutConstraint!
    var checkmarkTrailingConstraint: NSLayoutConstraint!
    
    // MARK: - UI Components
    lazy var listImageView: UIImageView = {
        let image = UIImage(named: "list.small")
        let result = UIImageView(image: image)
        return result
    }()
    
    lazy var checkmarkImageView: UIImageView = {
        let image = UIImage(named: "checkmark.small")
        let result = UIImageView(image: image)
        return result
    }()
    
    // MARK: - Inits
    init() {
        super.init(frame: .zero)
        setupView()
    }
    
    required init?(coder: NSCoder) {
        return nil
    }
    
    // MARK: - Methods
    func playAnimation() {
        // 1. change constraints
        listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
        listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
        checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
        checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
        self.layoutIfNeeded()
        
        // 2. animate constraints
        UIView.animate(withDuration: 3.0) {
            self.layoutIfNeeded()
        }
    }
    
    func setupView() {
        // 1. add subviews
        addSubview(listImageView)
        addSubview(checkmarkImageView)
        
        // 2. add constraints       
        ConstraintHelper.anchor(view: checkmarkImageView, options: [.top, .bottom])
        checkmarkLeadingConstraint = checkmarkImageView.leadingAnchor.constraint(equalTo: leadingAnchor)
        checkmarkTrailingConstraint = checkmarkImageView.trailingAnchor.constraint(equalTo: trailingAnchor)
        checkmarkLeadingConstraint.constant -= checkmarkImageView.frame.size.width * ratioMultiplier
        checkmarkTrailingConstraint.constant -= checkmarkImageView.frame.size.width * ratioMultiplier
        NSLayoutConstraint.activate([checkmarkLeadingConstraint, checkmarkTrailingConstraint])
        
        ConstraintHelper.anchor(view: listImageView, options: [.top, .bottom])
        listLeadingConstraint = listImageView.leadingAnchor.constraint(equalTo: leadingAnchor)
        listTrailingConstraint = listImageView.trailingAnchor.constraint(equalTo: trailingAnchor)
        NSLayoutConstraint.activate([listLeadingConstraint, listTrailingConstraint])
    }
}

附加信息:playAnimation() 在表格视图上从 cellForRow 调用,我也尝试使用 self.superview?.layoutIfNeeded() 调用但未成功。 ConstraintHelper 调用 translatesAutoresizingMaskIntoConstraints 并且约束正常工作,所以不用担心。任何帮助都会很棒。

【问题讨论】:

    标签: swift uiview nslayoutconstraint


    【解决方案1】:

    您最初的帖子不起作用的原因是您在约束更改后调用了您的第一个layoutIfNeeded()。当你的动画块被执行时,根本没有任何动画变化。

    无论你是在动画块中改变约束还是改变约束都没有关系。

    常见的步骤是:

    1. 在您的约束可能影响的最根视图上调用layoutIfNeeded()。在您的情况下,self 可以,因为所有涉及的约束都是针对子视图的。在某些情况下,您可能会更改本端视图的约束,然后您需要在其父视图上调用 layoutIfNeeded
    2. 进行约束更改:修改常量属性、激活/停用约束等。
    3. 在与第一步相同的视图上的动画块中调用 layoutIfNeeded()

    请尝试以下替代方案:

    func playAnimation() {
        // Request a layout to clean pending changes for the view if there's any.
        self.layoutIfNeeded()
        
        // Make your constraint changes.
        listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
        listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
        checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
        checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
        
        UIView.animate(withDuration: 3.0) {
            // Let your constraint changes update right now.
            self.layoutIfNeeded()
        }
    }
    

    【讨论】:

    • 谢谢,我第一个 layoutIfNeeded() 的顺序让我感到困惑。这个答案有效并且更有意义。
    【解决方案2】:

    搞定了。将约束更改移动到动画块修复它,尽管这与那里的大多数帖子形成对比,它们说在动画块之前进行约束更改,在准备中调用 layoutIfNeeded() 一次,然后调用第二个 layoutIfNeeded()在动画块中强制更新和动画。为什么这种行为在我的代码中有所不同,我仍然不确定......

    func playAnimation() {
    
        // update constraints & animate
        UIView.animate(withDuration: 3.0) {
            listLeadingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
            listTrailingConstraint.constant += (listImageView.frame.size.width * ratioMultiplier)
            checkmarkLeadingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
            checkmarkTrailingConstraint.constant += (checkmarkImageView.frame.size.width * ratioMultiplier)
            self.layoutIfNeeded()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2019-06-05
      • 1970-01-01
      • 2021-01-14
      • 1970-01-01
      • 2016-11-29
      相关资源
      最近更新 更多