I'm adding a UIView
to a view controller and want to it render and then immediately animate its movement, but a strange things is happening. The new view is being drawn at the destination where I want to animate it to and then animating to the origin that I had set it at. So it's essentially happening in reverse. Here's my code:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func buttonTapped(_ sender: Any) {
let square = UIView()
square.backgroundColor = .red
square.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(square)
[square.widthAnchor.constraint(equalToConstant: 100),
square.heightAnchor.constraint(equalToConstant: 100),
square.centerXAnchor.constraint(equalTo: view.centerXAnchor),
square.centerYAnchor.constraint(equalTo: view.centerYAnchor)].forEach { $0.isActive = true }
UIView.animate(withDuration: 2.0) {
var frameCopy = square.frame
frameCopy.origin.y -= 300
square.frame = frameCopy
}
}
}
If I comment out the UIView.animate
this is what gets created when I press the button, and this is what I expect
But then if I add the animation back in, this is what happens:
So as you can see the origin starts -300
off centre and then moves to the centre whereas I want to to start on centre and then move -300
off the screen. Can anyone help with this?
For the work I'm doing I need to add the view and then animate it immediately, I can't add the view previously and animate it later.
CodePudding user response:
I found out it's to do with the drawing cycle and if you change it to:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func buttonTapped(_ sender: Any) {
let square = UIView()
square.backgroundColor = .red
square.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(square)
[square.widthAnchor.constraint(equalToConstant: 100),
square.heightAnchor.constraint(equalToConstant: 100),
square.centerXAnchor.constraint(equalTo: view.centerXAnchor),
square.centerYAnchor.constraint(equalTo: view.centerYAnchor)].forEach { $0.isActive = true }
UIView.animate(withDuration: 0.0) {
square.layoutIfNeeded()
} completion: { _ in
UIView.animate(withDuration: 2.0) {
var frameCopy = square.frame
frameCopy.origin.y -= 300
square.frame = frameCopy
}
}
}
}
Everything works as you'd expect
CodePudding user response:
You shouldn't mix frame
based layout and AutoLayout as they will fight each other and whatever you're trying to do will be glitchy and not work well throughout iOS versions.
What you want to do is change the constraint set (deactivate old and activate new) and run layoutIfNeeded
on the changed view in an animation block to perform animations.
Your case seems to be simpler and you can just capture your centerYAnchor
constraint into a property and in the animation block do a constraint.constant = 300
or equivalent.