iOS - Cancel constraints animation - ios

I'm animating constraints the usual way:
centerXsettingsBtnConstraint.constant = aValue;
[UIView animateWithDuration:.5
animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
isAnimating = NO;}];
When I'm rotating the device I want to cancel this animation and update constraint with new values.
I tried to call
[self.view.layer removeAllAnimations];
but the animation still goes on till the rotation of the device is over. Only then I can update with new constraints, but during the rotation it's all a mess on the screen.
Is there a way to cancel constraints animation?

You must call the remove.. method on each subview:
[self.view.layer removeAllAnimations];
I'll use Swift:
for eachView in self.view.subviews {
eachView.layer.removeAllAnimations()
}

You need to remove animation on the specified view whose constraint or property is being changed in that particular animation.
Call removeAllAnimations() on the view on whom centerXsettingsBtnConstraint is applied.
You do not need to call removeAllAnimations() on each subview.
If you are not able to find the particular view, try to print someView.layer.animationKeys(), you will find the view on which animation is applied.

For Swift:
This will work to stop the animation of the constraint
yourComponent.layer.removeAnimation(forKey: "position")

Related

Object returns to the original position (after an animation) when keyboard appears

I have this strange new issue: this code (that works perfectly)
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
CGRect frame = _logoClaim.frame;
frame.origin.y -= 180;
_logoClaim.frame = frame;
} completion:NULL];
moves a view that contains a UIImageView and an UILabel to the top of my self.view.
The view that moves unhides a UITextField.
When I try to write text into the UITextField, obviously appear the keyboard.
And at this moment, the view animated before, returns to the original start position!!!
What is the reason?
Put a completion block in your animation and check the finished value. The keyboard is cancelling the animation and the finished bool will be NO. I would disable input in the UITextField until the animation is completed. Do this in your completion block.
EDIT
Looking at your duration and re-reading the question,I may be mistaken with what I think you mean. If this answer is incorrect I will remove it.
Also, search your code for _logoClaim.frame in case you are adjusting it onKeyboardWillAppear
You would need to create outlet for its bottom/top space constraints and update the constant value of it inside the animation block.
[UIView animateWithDuration:0.4
...
animations:^{
/*Update the value of the constraint outlet supposing it to be a bottom space constraint*/
_layoutConstraintBottom += 180;
[self.logoClaim layoutIfNeeded];
} completion:NULL];
Do not forget to call -layoutIfNeeded for your animating view.

How can I make a table view appear when a button is pressed/tapped?

I am trying to make a UITableView appear when a user taps on a button and disappear when the button is re-tapped.
I implemented the following code but nothing seems to appear when I tap the button.
- (IBAction)dropDown:(UIButton *)sender
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.6];
CGAffineTransform transfrom = CGAffineTransformMakeTranslation(0, 200);
self.markersTableView.transform = transfrom;
self.markersTableView.alpha = self.markersTableView.alpha * (-1) + 1;
[UIView commitAnimations];
}
What may potentially be the issue?
EDIT:
I was able to make the UITableView appear and disappear by adding self.markersTableView.hidden = YES; in viewDidLoad() and self.markersTableView.hidden = NO; in the IBAction method.
However, the table view disappears when I initially tap on the button as shown in the screenshot:
The fading of the rows is an indication it is moving down the screen, and then it disappears.
It only reappears when I re-tap on the UIButton the 2nd time.
Any clues?
Don't use a transform on your table view. That will make it LOOK like it's in a different position, but it won't respond to taps correctly.
Instead, use the newer UIView block-based animation methods and change the view's center.
The code might look like this (if you're NOT using auto-layout)
[UIView AnimateWithDuration: .2
animations: ^
{
CGPoint center = self.markersTableView.center;
center.y += 200;
self.markersTableView.center = center;
}
];
If you're using auto-layout, though, that won't work and you will need to animate the view's position using constraints.
From memory, here's an outline of how to do auto-layout based animations: You would create a vertical position constraint on your table view and connect the constraint to an IBOutlet in your view controller. In your animation code you change the constant of the constraint (the vertical position) and then in the animation block, send the view a needsLayout message.

UIView animation returns to origin

I have a UILabel inside of a UITableViewCell. I am trying to animate the label moving to the right when the user taps the cell. I have this code:
CGRect otherFrame = cellLabel.frame;
otherFrame.origin.x +=50;
[UIView animateWithDuration:1.0f animations:^{
cellLabel.frame = otherFrame;
}];
The odd thing that's happening is that the label is jumping 50 pixels to the left and animating back to its origin (where it was before the action began).
I actually had this working earlier in the week and didn't have any trouble with it, and after scouring through the revision history, I can't figure out where I've gone wrong. I must be missing something stupid.
EDIT:
Based on the answer from Jakub, I found that this works:
[UIView animateWithDuration:0.01f animations:^{}
completion:^(BOOL finished)
{
CGRect otherFrame = cellLabel.frame;
otherFrame.origin.x += 50;
[UIView animateWithDuration:1.0f animations:^{
cellLabel.frame = otherFrame;
}];
}];
Oddly, if I move all of the logic into a completion handler that performs a new animation after the first one completes (without the first actually doing anything), everything animates properly. This is super hacky, and I'm not at all happy with it.
Can anyone think of what would cause the initial animation to move the frame to the negative offset of the intended destination, only to animate it back to its origin, and why triggering a new animation as the completion handler of an empty animation would work?
EDIT 2:
I am going to mark Duncan's answer as the right one because it pointed me in the right direction, but I am still baffled why/how these symptoms can happen:
The animation moves the frame to the negative offset of the
destination, then animates it back to the origin (rather than from
the origin to the destination)
Running an empty animation block but adding another animation to the first block's completion handler animates correctly
Info for the attempted answers:
I have to use auto layout for the project, and as far as I know, I
can't disable it for a single view
I am not putting anything onto a background thread. I didn't try specifically dispatching to the main thread, but I think I was already there. Isn't all visible UI always on the main thread? It was animating, just not as expected.
The reason I didn't go the route of changing constraints to begin with is that I am using prototype cells and IB doesn't let you create IBOutlets from prototype cells. There's a bit of work to walk a constraint list and detect a specific one, so I left the constraints blank and tried to animate the frame (and as I said, it was working earlier in the week -- and still worked when animating from an animation block's completion handler).
So the final solution was to add constraints to the container cell (the important one here being the leading), then to animate, I had to find the constraint:
NSLayoutConstraint *titleLeadingConstraint = nil;
for( NSLayoutConstraint *constraint in cellLabel.superview.constraints )
{
if( constraint.firstItem == cellLabel && constraint.firstAttribute == NSLayoutAttributeLeading )
{
titleLeadingConstraint = constraint;
}
}
Then set the constraint constant:
titleLeadingConstraint.constant = 55.0;
Then set the animation block:
[UIView animateWithDuration:1.0f animations:^{
[self.view layoutIfNeeded];
}];
This solution should be more future proof (and robust, reliable and stable) than moving the frame, but it turned out to be a fair amount of work in discovery.
Do you have auto layout set in your storyboard or XIB by mistake? (It is on by default). If so you either need to turn AutoLayout off or animate a constraint rather than manipulating your view's frame.
Where are you calling this code?
Try calling UIView animateWithDuration:... method on the main thread, in a
dispatch_async(dispatch_get_main_queue(), ^{
// Your animation here
});
If this method is not executed on the main thread, it usually jumps to the final stage of the animation.
You should reset the origin after animation.

iOS: Animate subview without calling layoutSubviews

In layoutSubviews i layout my child views into appropriate positions, and for some of them apply some transformation. Now i want to make on this subviews repeatable scale animation to draw attention. But, when i launch animation with [UIView animate....], with changing transform property - fired layoutSubviews method, which conflict with animation (it override transform and animation not played).
Is there any good way (without bool flags) to handle this behavior?
Example:
- (void)layoutSubviews {
[super layoutSubviews];
myChildView.transform = CGAffineTransformMake....;
}
- (void)animate {
CGAffineTransform originalTransform = myChildView.transform;
[UIView animateWithDuration:0.1f
delay:0.0f
options:UIViewAnimationOptionAutoreverse
animations:^{
[UIView setAnimationRepeatCount:4];
//next line of code produce calling layoutSubviews
myChildView.transform = CGAffineTransformMakeScale(1.15f, 1.15f);
} completion:^(BOOL finished){
myChildView.transform = originalTransform;
//after this also called layoutSubviews
}];
}
Recently, I have encountered this issue. I override the method viewDidLayoutSubviews inside the view controller to control the animation.
- (void)viewDidLayoutSubviews {
// This method will be called after subview's layoutSubviews.
if (self.shouldAnimated) {
// Do animation at here.
}
}
However, this is just a dirty solution.
I suggest you would do better to not use layoutSubviews. Instead create and position and transform your subviews either inside the init method or a custom method called from the init.
I meet this problem also. And I add another view(called subView) as a subview, and add the myChildView to subView. This will not trigger the layoutSubviews of superview when I change the transform of myChildView. And why, you can see this UIView/CALayer: Transform triggers layoutSubviews in superview.
Is there any good way (without bool flags) to handle this behavior?
Yes. Rid off "layoutSubviews" method and set transform for children views in another method.
[self.parentView setUpChildrenViews];
...
[self.parentView animate];

UIScrollView cancel/stop setContentOffset:<CGPoint>animate:YES animation

I have a UIScrollView that I'm scrolling using setContentOffset:animate:YES but if the device rotates I want to be able to stop it. Is this possible? Or how would I go about writing my own implementation of the setContentOffset with animation? thanks!
scrollview.contentOffset = scrollView.contentOffset should stop the scrollView at its last scroll position.
Do this in willAnimateRotationTo..... in your viewController
If you have a UIScrollView sub-class you could do it in setFrame: , this is assuming the frame of the scrollview changes upon orientation change.
try to use this instead:
[UIView animateWithDuration:.25 animations:^
{
// replacement for setContentOffset:animated:
self.scrollView.contentOffset = scrollPositionBeforeKeyboardAdjustments;
}];
if you are struggling with the keyboard, see my answer
How to make a UITextField move up when keyboard is present?

Resources