【问题标题】:trying to animate a constraint in swift试图快速动画约束
【发布时间】:2014-10-28 06:50:30
【问题描述】:

我有一个UITextField,我想在点击它时放大它的宽度。我设置了约束并确保左侧约束的优先级低于我试图在右侧设置动画的约束。

这是我尝试使用的代码。

// move the input box
UIView.animateWithDuration(10.5, animations: {
    self.nameInputConstraint.constant = 8
    }, completion: {
        (value: Bool) in
        println(">>> move const")
})

这可行,但它似乎只是立即发生并且似乎没有任何动作。我尝试将其设置为 10 秒以确保我没有遗漏任何内容,但我得到了相同的结果。

nameInputConstraint 是我控制的约束的名称,它被拖动以从 IB 连接到我的类。

【问题讨论】:

标签: xcode swift constraints xcode6


【解决方案1】:

您需要先更改约束,然后为更新设置动画。
这应该在 superview 中。

self.nameInputConstraint.constant = 8

斯威夫特 2

UIView.animateWithDuration(0.5) {
    self.view.layoutIfNeeded()
}

Swift 3、4、5

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

【讨论】:

  • 有人能解释一下这是如何工作的吗?在任何 UIView 上调用 animationWithDuration 都会对继承自 UIView 的任何类进行动画处理并更改物理值?
  • 你提到的动画API是用来动画视图和图层的properties的。这里我们需要动画布局的变化。这就是更改布局约束的常量所需要的 - 仅更改常量没有任何作用。
  • @Jacky 也许你在 Objective-C 推理中误认为是强变量和弱变量。 Swift 闭包是不同的:一旦闭包完成,没有对象持有闭包中使用的self
  • 在我的情况下不起作用,它首先发生,该怎么办
  • 我花了一个小时才注意到,我必须在 superview 上调用 layoutIfNeeded...
【解决方案2】:

SWIFT 4 及更高版本:

self.mConstraint.constant = 100.0
UIView.animate(withDuration: 0.3) {
        self.view.layoutIfNeeded()
}

补全示例:

self.mConstraint.constant = 100
UIView.animate(withDuration: 0.3, animations: {
        self.view.layoutIfNeeded()
    }, completion: {res in
        //Do something
})

【讨论】:

  • 它不起作用我已经完成了这个 UIView.animate(withDuration: 10, delay: 0, options: .curveEaseInOut, animations: { self.leftViewHeightConstraint.constant = 200 self.leftView.layoutIfNeeded() } ,完成:无)
  • 你必须改变约束然后然后动画。
  • 虽然此代码 sn-p 可能是解决方案,但 including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
  • 以上这行让我开心:) 谢谢@Hadzi
【解决方案3】:

指出view.layoutIfNeeded() 仅适用于视图子视图,这一点非常重要。

因此,要为视图约束设置动画,重要的是在视图到动画superview上调用它,如下所示:

    topConstraint.constant = heightShift

    UIView.animate(withDuration: 0.3) {

        // request layout on the *superview*
        self.view.superview?.layoutIfNeeded()
    }

一个简单布局的例子如下:

class MyClass {

    /// Container view
    let container = UIView()
        /// View attached to container
        let view = UIView()

    /// Top constraint to animate
    var topConstraint = NSLayoutConstraint()


    /// Create the UI hierarchy and constraints
    func createUI() {
        container.addSubview(view)

        // Create the top constraint
        topConstraint = view.topAnchor.constraint(equalTo: container.topAnchor, constant: 0)


        view.translatesAutoresizingMaskIntoConstraints = false

        // Activate constaint(s)
        NSLayoutConstraint.activate([
           topConstraint,
        ])
    }

    /// Update view constraint with animation
    func updateConstraint(heightShift: CGFloat) {
        topConstraint.constant = heightShift

        UIView.animate(withDuration: 0.3) {

            // request layout on the *superview*
            self.view.superview?.layoutIfNeeded()
        }
    }
}

【讨论】:

  • 在 Swift 4 上更新高度约束,这在视图上调用 layoutIfNeeded 时有效,但不是超级视图没有。真的很有帮助,谢谢!
  • 这确实是正确答案。如果您不在 superview 上调用它,您的视图只会跳转到新位置。这是一个非常重要的区别。
【解决方案4】:

使用 Swift 5 和 iOS 12.3,根据您的需要,您可以选择以下 3 种方式之一来解决您的问题。


#1。使用UIViewanimate(withDuration:animations:)类方法

animate(withDuration:animations:) 具有以下声明:

使用指定的持续时间对一个或多个视图进行动画更改。

class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Void)

下面的 Playground 代码显示了animate(withDuration:animations:) 的可能实现,以便为自动布局约束的不断变化设置动画。

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        UIView.animate(withDuration: 2) {
            self.view.layoutIfNeeded()
        }
    }

}

PlaygroundPage.current.liveView = ViewController()

#2。使用UIViewPropertyAnimatorinit(duration:curve:animations:)初始化器和startAnimation()方法

init(duration:curve:animations:) 有以下声明:

使用内置的 UIKit 时序曲线初始化动画器。

convenience init(duration: TimeInterval, curve: UIViewAnimationCurve, animations: (() -> Void)? = nil)

下面的 Playground 代码显示了 init(duration:curve:animations:)startAnimation() 的可能实现,以便为自动布局约束的不断变化设置动画。

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        let animator = UIViewPropertyAnimator(duration: 2, curve: .linear, animations: {
            self.view.layoutIfNeeded()
        })
        animator.startAnimation()
    }

}

PlaygroundPage.current.liveView = ViewController()

#3。使用UIViewPropertyAnimatorrunningPropertyAnimator(withDuration:delay:options:animations:completion:)类方法

runningPropertyAnimator(withDuration:delay:options:animations:completion:) 具有以下声明:

创建并返回一个动画对象,该对象立即开始运行其动画。

class func runningPropertyAnimator(withDuration duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions = [], animations: @escaping () -> Void, completion: ((UIViewAnimatingPosition) -> Void)? = nil) -> Self

下面的 Playground 代码显示了runningPropertyAnimator(withDuration:delay:options:animations:completion:) 的可能实现,以便为自动布局约束的不断变化设置动画。

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let textView = UITextView()
    lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)

    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(textView)

        textView.backgroundColor = .orange
        textView.isEditable = false
        textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
        heightConstraint.isActive = true

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
        textView.addGestureRecognizer(tapGesture)
    }

    @objc func doIt(_ sender: UITapGestureRecognizer) {
        heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
        UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2, delay: 0, options: [], animations: {
            self.view.layoutIfNeeded()
        })
    }

}

PlaygroundPage.current.liveView = ViewController()

【讨论】:

  • 这么好的答案!
  • @Imanou 很好的答案,欣赏细节!可能添加选项的简短描述,有什么区别?对我真的很有帮助,而且我相信其他人会有一句话说明我为什么会选择一个而不是另一个。
【解决方案5】:

就我而言,我只更新了自定义视图。

// DO NOT LIKE THIS
customView.layoutIfNeeded()    // Change to view.layoutIfNeeded()
UIView.animate(withDuration: 0.5) {
   customViewConstraint.constant = 100.0
   customView.layoutIfNeeded() // Change to view.layoutIfNeeded()
}

【讨论】:

    【解决方案6】:

    观看this

    视频说您只需添加self.view.layoutIfNeeded(),如下所示:

    UIView.animate(withDuration: 1.0, animations: {
           self.centerX.constant -= 75
           self.view.layoutIfNeeded()
    }, completion: nil)
    

    【讨论】:

      猜你喜欢
      • 2018-02-10
      • 1970-01-01
      • 1970-01-01
      • 2013-08-06
      • 1970-01-01
      • 1970-01-01
      • 2013-12-04
      • 2019-06-03
      • 1970-01-01
      相关资源
      最近更新 更多