首页 文章

如何使用Swift中的约束来动画UIView?

提问于
浏览
38

在我的设计示例中,我有以下单一视图:

enter image description here

如您所见,它包含一些简单的约束:

  • 对齐水平和垂直中心,

  • 高度(设为常数)

  • 前导和尾随空格(设置为常量)

我想要达到的目的是从顶部看到这个红色/粉红色的视图"come in" . 传统上,在一个无约束的世界中,我只是修改 UIView.animateWithDuration 中的框架,但是我不确定我是如何在约束世界中做类似的 .

To reiterate my question, how can I make my view start out of scene and animate the view flying in from the top?

我已经考虑过制作垂直中心约束的动画(并随后调用 layoutIfNeeded ),但它没有达到预期的效果 .

谢谢你的帮助 .

5 回答

  • 12

    您可以做的是在 viewDidAppear 方法中添加以下代码 . 首先,为视图的中心Y约束创建 IBOutlet 属性,然后更改其常量值 .

    self.centerYConstraint.constant = 500.0
    self.view.layoutIfNeeded()
    
    UIView.animateWithDuration(Double(0.5), animations: {
            self.centerYConstraint.constant = 0
            self.view.layoutIfNeeded()
        })
    
  • 2

    你想要的实际上相当简单,我将详细说明如何在不搞乱优先级或时髦常数的情况下完成它 . 通过这种方式,我们可以获得适用于任何屏幕尺寸的动画 .

    TL; DR是您需要配置约束,然后在动画块内调用layoutIfNeeded来为更改设置动画 . 更多see this SO post .

    因此,我们需要两组约束来隐藏和显示视图 . 在 viewDidLoad 中,视图将被隐藏,然后在 viewDidAppear 或我们希望该视图滑入的任何位置 .

    故事板/ XIB设置

    所以首先要配置不会在IB(或代码中,无论你习惯哪种)中改变的约束 . 即;红色视图的高度,以及它到容器的前导和尾随空间 . 所以你的约束可能看起来像这样( note: 我没有设置宽度约束,但让前导空格和尾随空格定义宽度):

    Constraints

    现在您将看到IB将警告您没有为视图的Y位置配置约束(因此红色虚线)

    View with warnings

    您现在可以将顶部空间添加到容器约束并将其设置为占位符约束(复选框显示“在构建时删除”) . 我们这样做是因为我们想以编程方式控制视图的位置 .

    enter image description here

    这意味着一旦加载视图就不会存在此约束,但是当您告诉IB您知道如何在加载视图时处理此约束时,该约束将用于删除所有警告 .

    编码时间

    现在我们需要一种隐藏视图的方法,一种显示视图的方法 . 为此,我们将在视图上存储Y定位约束,然后为其设置动画 . 我们的viewController看起来像这样:

    @IBOutlet weak var redView: UIView!
    var redViewYPositionConstraint: NSLayoutConstraint?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.hideRedViewAnimated(false)
    }
    
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.showRedViewAnimated(true)
    }
    

    隐藏

    我们隐藏视图的方法可以简单地删除位置约束,然后添加一个红色视图底部等于视图控制器的视图顶部:

    func hideRedViewAnimated(animated: Bool) {
        //remove current constraint
        self.removeRedViewYPositionConstraint()
        let hideConstraint = NSLayoutConstraint(item: self.redView,
            attribute: .Bottom,
            relatedBy: .Equal,
            toItem: self.view,
            attribute: .Top,
            multiplier: 1,
            constant: 0)
        self.redViewYPositionConstraint = hideConstraint
        self.view.addConstraint(hideConstraint)
        //animate changes
        self.performConstraintLayout(animated: animated)
    }
    

    显示

    同样,我们的show约束会将视图的中心Y移动到控制器中心Y:

    func showRedViewAnimated(animated: Bool) {
        //remove current constraint
        self.removeRedViewYPositionConstraint()
        let centerYConstraint = NSLayoutConstraint(item: self.redView,
            attribute: .CenterY,
            relatedBy: .Equal,
            toItem: self.view,
            attribute: .CenterY,
            multiplier: 1,
            constant: 0)
        self.redViewYPositionConstraint = centerYConstraint
        self.view.addConstraint(centerYConstraint)
        //animate changes
        self.performConstraintLayout(animated: animated)
    }
    

    便利方法

    为了完整起见,我使用的便捷方法如下所示:

    func performConstraintLayout(animated animated: Bool) {
        if animated == true {
              UIView.animateWithDuration(1,
              delay: 0,
              usingSpringWithDamping: 0.5,
              initialSpringVelocity: 0.6,
              options: .BeginFromCurrentState,
              animations: { () -> Void in
                 self.view.layoutIfNeeded()
              }, completion: nil)
         } else {
              self.view.layoutIfNeeded()
         }
    }
    
    func removeRedViewYPositionConstraint() {
        if redViewYPositionConstraint != nil {
            self.view.removeConstraint(self.redViewYPositionConstraint!)
            self.redViewYPositionConstraint = nil
        }
    }
    

    您还可以使用一些CGRect数学检查红色视图是否可见:

    func isRedViewVisible() -> Bool {
        return CGRectContainsPoint(self.view.bounds, self.redView.frame.origin)
    }
    
  • 9

    试试这个:

    class ViewController: UIViewController {
    
        // red view
        @IBOutlet weak var myView: UIView!
        //  vertical align contraint
        @IBOutlet weak var verticalConstraint: NSLayoutConstraint!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            verticalConstraint.constant = (myView.bounds.height + self.view.bounds.height)/2
        }
    
        override func viewDidAppear(animated: Bool) {
            super.viewDidAppear(animated)
            self.view.layoutIfNeeded()
            UIView.animateWithDuration(0.5) {
                self.verticalConstraint.constant = 0
                self.view.layoutIfNeeded()
            }
        }
    
    }
    

    请注意约束中的 firstItemsecondItem 顺序 . 上面的代码假设Superview是 firstItem

    screenshot


    替代方法是定义 two 约束:

    • Center Y Alignment constraint (Superview.CenterY == View.CenterY)优先级= 750

    • Vertical Space Constraint (SuperView.Top == View.Bottom)优先级= 500

    screenshot

    并调整 priority 以确定应采用哪种约束 .

    class ViewController: UIViewController {
    
        //  vertical align contraint
        @IBOutlet weak var centerYConstraint: NSLayoutConstraint!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            centerYConstraint.priority = 250
        }
    
        override func viewDidAppear(animated: Bool) {
            super.viewDidAppear(animated)
            self.view.layoutIfNeeded()
            UIView.animateWithDuration(0.5) {
                self.centerYConstraint.priority = 750
                self.view.layoutIfNeeded()
            }
        }
    
    }
    
  • 64

    对于任何寻找像请求这样的简单动画的人:

    对于上滑动画,您需要使用[...] CGAffineTransformMakeTranslation(x,y) . 原因是对于向上滑动动画,您需要先将视图移出屏幕,然后将其恢复到原始位置 . 所以在你的viewDidLoad中只使用:yourView.transform = CGAffineTransformMakeTranslation(0,500)
    这会将视图移出屏幕,在本例中位于底部 . 现在在viewDidAppear中,您可以使用以下命令显示视图:UIView.animateWithDuration(0.7,延迟:0.0,usingSpringWithDamping:0.5,
    initialSpringVelocity:0.5,选项:[],动画:,完成:无)

    使用这些行,您可以在UIView上添加所需的约束并将其放在何处你需要在ViewController中 .

  • 6

    对我来说,动画的最佳方式 Swift 3 & 4

    self.constraint.constant = -150
    
    UIView.animate(withDuration: 0.45) { [weak self] in
        self?.view.layoutIfNeeded()
    }
    

相关问题