简体   繁体   English

以编程方式添加后,使用约束动画UIView

[英]Animate UIView with constraints after it was added programmatically

I have an UIView that I add programmatically to my UIViewController like this: 我有一个UIView,我以编程方式添加到我的UIViewController,如下所示:

if let myView = Bundle.main.loadNibNamed("MyView", owner: self, options: nil)?.first as? MyView
    {
        self.view.addSubview(myView)
        myView.translatesAutoresizingMaskIntoConstraints = false
        //Horizontal orientation
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[view]-10-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":myView]))
    }

Thats fine, the view is added with the needed space on left and right at (0,0) of the view controller. 多数民众赞成,视图在视图控制器的(0,0)左右两侧添加了所需的空间。

What I want to achieve is that the view animates in from the bottom of the view controller to the center, and later animates out from center to top. 我想要实现的是视图从视图控制器的底部动画到中心,然后从中心到顶部进行动画制作。 So first of all I tried to animate it to center from it's current position (0,0) 首先,我试图将它设置为动画,使其从当前位置(0,0)开始居中

So what I did is: 所以我做的是:

UIView.animate(withDuration: 1.0,
                   animations:
    {
        let center = NSLayoutConstraint(item: myView, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1, constant: CGFloat(0.0))
        self.view.addConstraint(center)
    })

Well the view is in the center but it is not animated. 那么视图位于中心,但它不是动画。 It directly jumps to it. 它直接跳到它。

I searched and found a lot of answers that say I have to set the constant of the constraint and then call layoutIfNeeded() but all this examples use references to constraints that were set before as an IBOutlet, which I do not have as I add my view programmatically. 我搜索并找到了很多答案,说我必须设置约束的常量,然后调用layoutIfNeeded()但所有这些示例都使用对之前设置为IBOutlet的约束的引用,我没有添加我的以编程方式查看。

How can I animate the view properly, from bottom to center then center to top? 如何正确地为视图设置动画,从底部到中心然后从中心到顶部?

Thank you already! 谢谢你!

Greetings 问候

EDIT 1 编辑1

Okay I'm going a bit further and explain what I want to achieve. 好的,我会更进一步解释我想要实现的目标。 On my view controller, after a 5 second timer that is displayed to the user, I want to show several questions, one after the other. 在我的视图控制器上,在向用户显示5秒计时器后,我想一个接一个地显示几个问题。

I want a question to slide in from bottom to center, been answered and then slide out from center to top. 我想要一个问题从下到中滑入,已经回答,然后从中心向上滑动。 Then the next question comes in from bottom to center, is answered slides out and so on. 然后下一个问题从下到中,从幻灯片中取出,等等。

So after a 5 second timer I call method named slideInQuestion that creates a Question and animates it in: 因此,在5秒计时器之后,我调用名为slideInQuestion的方法创建一个问题,并将其设置为动画:

func slideInQuestion()
{
    let question:QuestionView = createQuestion()
    UIView.animate(withDuration: 1.0,
                   animations:
    {
        self.yConstraint?.constant = CGFloat(0.0)
        self.view.layoutIfNeeded()
    })

The createQuestion method instantiates the question view and assigns the constraints to it createQuestion方法实例化问题视图并为其分配约束

func createQuestion() -> QuestionView
{
    if let questionView = Bundle.main.loadNibNamed("QuestionView", owner: self, options: nil)?.first as? QuestionView
    {
        self.view.addSubview(questionView)
        questionView.translatesAutoresizingMaskIntoConstraints = false
        //Horizontal orientation
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[view]-10-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":questionView]))
        yConstraint = NSLayoutConstraint(item: questionView,
                                              attribute: .centerY,
                                              relatedBy: .equal,
                                              toItem: self.view,
                                              attribute: .centerY,
                                              multiplier: 1,
                                              constant: CGFloat(UIScreen.main.bounds.height))
        self.view.addConstraint(yConstraint!)
        return questionView
    }
    return QuestionView()
}

Before, I defined the yConstraint as an Instance variable as suggested by Duncan C like this: 在此之前,我将yConstraint定义为Duncan C建议的实例变量,如下所示:

    var yConstraint:NSLayoutConstraint?

So what I expect what I have right now is: 所以我对现在的期望是:

The question view is created, it's center is on the center of the view controller + the height of the screen. 创建问题视图,它的中心位于视图控制器的中心+屏幕的高度。 That makes it outside of the visible view on bottom of the screen. 这使它在屏幕底部的可见视图之外。 Then it slides within 1 second from this position to centerY (0.0) of the screen. 然后它在1秒内从该位置滑动到屏幕的中心Y(0.0)。

But what it does right now, is sliding from top of the screen to the centerY of the screen and also at the same time it resizes to the margins of left and right like it is set in the first constraint 但它现在做的是,从屏幕顶部滑动到屏幕的中心Y,同时它调整到左右边距,就像它在第一个约束中设置一样

self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[view]-10-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":questionView]))

I'm totally confused right now I hope my explanation helps and someone has an idea. 我现在完全糊涂了,希望我的解释有所帮助,有人有个主意。

Thank you! 谢谢!

As I don't know how your View is created from your bundle, I've hacked together a quick example based of your code. 由于我不知道您的视图是如何从您的软件包中创建的,因此我根据您的代码将一个快速示例混合在一起。

The key is that self.view.layoutIfNeeded() is called twice. 关键是self.view.layoutIfNeeded()被调用两次。 Once after the view is added and the second time in the animation block. 一旦添加了视图,第二次在动画块中。 And the modification of the constraint is not within the animation block. 并且约束的修改不在动画块内。

import UIKit

class ViewController: UIViewController {

    var yConstraint:NSLayoutConstraint?

    override func viewWillAppear(_ animated: Bool) {

        self.createView()

        self.view.layoutIfNeeded()

        self.yConstraint?.constant = 32
        UIView.animate(withDuration: 1.0) {
            self.view.layoutIfNeeded()
        }

    }

    func createView(){

        let questionView = UIView()
        questionView.backgroundColor = UIColor.red

        let heightConstraint = NSLayoutConstraint(item: questionView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: self.view.bounds.height-64)
        questionView.addConstraint(heightConstraint)

        self.view.addSubview(questionView)

        questionView.translatesAutoresizingMaskIntoConstraints = false

        //Horizontal orientation
        self.view.addConstraints(
            NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[view]-10-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":questionView]))

        yConstraint = NSLayoutConstraint(item: questionView,
                                         attribute: .top,
                                         relatedBy: .equal,
                                         toItem: self.view,
                                         attribute: .top,
                                         multiplier: 1,
                                         constant: CGFloat(UIScreen.main.bounds.height))
        self.view.addConstraint(yConstraint!)
    }
}

Create a constraint and save it in an instance variable. 创建约束并将其保存在实例变量中。 Then when you want to animate it, change the constant on the constraint and call layoutIfNeeded from the animation block just like you would for animating a storyboard-based constraint. 然后,当您想要为其设置动画时,更改约束上的常量并从动画块调用layoutIfNeeded,就像为动画基于故事板的约束设置动画一样。

In your case, don't use let constraint = ... inside your function. 在你的情况下,不要在你的函数中使用let constraint = ... Instead, use: 相反,使用:

var centerConstraint: NSLayoutConstraint?

In viewWillAppear: 在viewWillAppear中:

centerConstraint = NSLayoutConstraint(item: myView, 
  attribute: .centerY, 
  relatedBy: .equal, 
  toItem: self.view, 
  attribute: .centerY,  
  multiplier: 1,  
  constant: CGFloat(0.0))
self.view.addConstraint(center)

And when you want to animate: 当你想要动画时:

UIView.animate(withDuration: 1.0,
                   animations:
    {
       centerConstraint.constant = newValue  //(Your value here.)
       myView.layoutIfNeeded()
    })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM