NSLayoutConstraint animation finishes instantly - swift

I have a very simple project, I want to animate the right margin on a label, however when I try it, it finishes instantly.
#IBAction func onDo(sender:UIButton)
{
self.view.setNeedsLayout()
self.testConstraint.constant = 40.0
UIView.animateWithDuration(2.0, animations: { () -> Void in
self.view.setNeedsLayout()
}) { (complete:Bool) -> Void in
}
}
The project is here:
https://www.dropbox.com/s/9a0v0906riunkww/test2222.zip?dl=0
Am I missing something obvious?
Update #1
It seems it's a problem with UILabels specifically, a standard UIView, or UIButton animates fine....so whats wrong with a UILabel?
Update #2
If I set the UILabel View Content Mode to Center, then it works fine, but it doesn't look very smooth...very strange.

I may be off the mark here, but it looks to me as though your animation routine just does setNeedslayout, which will cause the screen to redraw instantly. You need to change some animatable parameter of the UIView in order to see something move. Like self.view.frame, perhaps. The animatable properties of UIView are listed here.

Try replacing self.view.setNeedsLayout() in animation closure with self.view.layoutIfNeeded()
#IBAction func onDo(sender:UIButton) {
self.testConstraint.constant = 40.0
UIView.animateWithDuration(2.0, animations: { () -> Void in
self.view.layoutIfNeeded()
}) { (complete:Bool) -> Void in
}
}

Related

After animation and keyboard close the animated image moves

I have a problem after animated a logo on the main login screen. The screen looks correct when visibly seeing the screen. Once entering the email address field and typing into the field (or closing the keyboard), the logo will go back to it's original location.
On viewDidAppear, I animate the logo.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
animateScreen()
}
private func animateScreen() {
mainLogo.fadeIn()
UIView.animate(withDuration: 1.2, animations: {
self.mainLogo.frame.origin.y -= 150
}) { (isCompleted) in
self.showFields()
}
}
I am using a storyboard with centerX and top constraints on the image initially centered on the login screens.
If there's a better solution, I'll gladly look at alternatives.

Swift 2: problems with UILabel position after the view is loaded

I have a little problem with an ULlabel. All I want to do is to move a UIlabel outside the view's bounds before it loads and then, when the view is loaded, show with a little animation.
So I have done this:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.lblQuestion.hidden = true
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.lblQuestion.center.y = -(self.view.bounds.height + 100)
}
It seems to be all ok because if I put a print("\\(self.lblQuestion.center.y)") in the viewDidAppear function I can see that the position is ok and the UIlabel is outside the bounds, like I want.
But if I wait 2 seconds and check with a new print("\\(self.lblQuestion.center.y)") inside a timer (for example) I see that the y position of the UIlabel is changed.
Does the UIView change its bounds after some event?
Can someone tell me what is happening, please?

Animate view by changing constraint constant

I have a datePicker that I want to animate in and out of view from the bottom by changing its' top constraint to the super view top.
I have an IBOutlet set to it and on viewDidLoad I'm allowed to change the constraint constant.
override func viewDidLoad() {
super.viewDidLoad()
self.datePickerTopConstraint.constant = self.view.frame.size.height // I can set this to whatever and it will persist
}
However via an IBAction I try to set the constant to another value and that doesn't persist.
#IBAction func showDatePicker() {
UIView.animateWithDuration(0.25, animations: {
() -> Void in
self.datePickerTopConstraint.constant = self.view.frame.size.height - self.datePicker.frame.size.height // Doesn't persist
self.view.layoutIfNeeded()
})
}
It seems that I can reverse this and have the datePicker appear in view (in viewDidLoad) and animate it out of view, but not have the datePicker appear out of view (like in the example above) and animate inside the view. What have I missed?
EDIT
By setting the top constraint constant to the super view's height I (for some reason I don't understand) also set the date picker's height to 0 which in turn makes the subtraction in showDatePicker pointless.
Restructure the code so that in the button's method work's by first working out the constant's new value and then calling the animation. Pull the height calculation in to it's own function. I think that self.datePicker.frame.size.height does not exist and results in 0, but I would check this using the debugger.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
datePickerTopConstraint.constant = constantForDatePickerViewHeightConstraint()
view.setNeedsLayout()
}
#IBAction func showDatePicker(button: UIButton) {
// Check constraint constant
if datePickerTopConstraint.constant == self.view.frame.size.height {
// Date picker is NOT visible
datePickerTopConstraint.constant = constantForDatePickerViewHeightConstraint()
} else {
// Date picker is visible
datePickerTopConstraint.constant = self.view.frame.size.height
}
UIView.animateWithDuration(0.25,
animations: {() -> Void in
self.view.layoutIfNeeded()
})
}
private func constantForDateOPickerViewHeightConstraint() -> CGFloat {
var value : CGFloat = 200.0
// Workout value you want to as the constant for the constraint.
return value
}
Try this:
func showDatePicker() {
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.25, animations: {
() -> Void in
self.datePickerTopConstraint.constant = self.view.frame.size.height - self.datePicker.frame.size.height // Doesn't persist
self.view.layoutIfNeeded()
})
}
You need to call layoutIfNeeded before the animation bock and in the block. Now it is calculating the views correctly. Like I said there is also no point in having any constraints set in the viewDidLoad, if you are going to do it anywhere do it in viewWillAppear. The view hasn't finished setting up in viewDidLoad so there is no constraints available to set properly. Calling layoutIfNeeded before the animation block fixes this mistake and you need it there anyway so it can calculate correctly in the future too.

Why layoutIfNeeded() allows to perform animation when updating constraints?

I have two states of UIView:
I have animation between them using #IBaction for button:
#IBAction func tapped(sender: UIButton) {
flag = !flag
UIView.animateWithDuration(1.0) {
if self.flag {
NSLayoutConstraint.activateConstraints([self.myConstraint])
} else {
NSLayoutConstraint.deactivateConstraints([self.myConstraint])
}
self.view.layoutIfNeeded() //additional line
}
}
Animation is working only when I add additional line:
But when I remove that line, then my UIView is updated but without any animation, it happens immediately.
Why this line makes such difference? How is it working?
As I understand it, changing a constraint doesn't update the layout of your views right away. It queues the changes, and they only take place the next time the system updates layout.
By putting layoutIfNeeded inside your animation block it forces the view changes to be applied during the animation block. Otherwise they take place later, after the animation has been set up.

How to simply animate an UIimageView?

I want to move a 40, 40 pixel square from the top to the bottom, then repeat that action over a period of 3 seconds. Where would I put the entire thing in the viewcontroller? There is no start button so would I put in in the viewdidload?
So far I have this
UIView.animateWithDuration(0.2, animations: {
}, completion: {
})
As somewhere to start. Would I set the image to equal UIView? What should It look like? the image is called mrock.
I tried using blank.moveToPoint(CGPoint(x: 0,y: 0)) in the animations part, but dont I need A UIBezierPath thing?
As #picciano stated you should have an IBOutlet to an UIImageView if you are using storyboards. So you in your view controller you can try something like this:
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//'img' is a picture imported in your project
self.imageView.image = UIImage(named: "img")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let newPos: CGFloat = 300.0
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.imageView.frame = CGRectMake(0, newPos, CGFloat(self.imageView.bounds.size.width), CGFloat(self.imageView.bounds.size.height))
})
}
First, consider putting the animation in viewWillAppear, then disable it if needed in viewDidDisappear.
Second, if you're using auto layout, you will probably need to create an IBOutlet for the relevant constraint and animate the constant property of the constraint rather than the position of the view object directly.