I am moving two UIButtons with this animation:
[UIView animateWithDuration:1.0
delay:0.0
options: UIViewAnimationCurveEaseInOut
animations:^{
self.ViewA.frame = CGRectMake(self.ViewA.frame.origin.x, self.ViewA.frame.origin.y - 174, self.ViewA.frame.size.width, self.ViewA.frame.size.height);
self.ViewB.frame = CGRectMake(self.ViewB.frame.origin.x, self.ViewB.frame.origin.y - 174, self.ViewB.frame.size.width, self.ViewB.frame.size.height);
}
completion:^(BOOL finished){
}];
but as the animation runs until the user holds his finger on a UIButton, if he releases the animation is interrupted. Is there a method, or something to determine when this happens and fix the position of the buttons?
In your completion block, the finished parameter tells you whether your animation has completed or not. If its value is NO, then the animation has been terminated. This is how you know if you need to fix the positions, etc.
Related
I'm currently testing my apps for the release of IOS 8. I noticed that after I performed an animation block, the animation resets if I update the text of any label. I ran a simple example with one method shown below. Running this example results in the following:
Clicking myButton the first time- animation runs but resets when the label text is changed.
Clicking myButton the second time - animation runs but does not reset to original position.
It seems like this happens because the label text doesn't change. If I completely remove the line updating the text, this also stops the animation from resetting at the end.
I would like to fix this so that when the method runs, the label text can be updated without resetting the animation.
- (IBAction)move:(id)sender {
[UIView animateWithDuration:0.4 delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.myButton.center = CGPointMake(200, 300);
}completion:^(BOOL finished){
if(finished){
self.myLabel.text=#"moved";
}
}];
}
This problem can be caused by having Auto Layout set on the UIView. Strictly speaking, if you're using Auto Layout, then you shouldn't animate the absolute position of objects -- you should animate their constraints instead.
Changing the label text once your animation is underway triggers a layout refresh, and iOS shuffles everything around to comply with the original view constraints. (I suspect this is a behavioural change from iOS7).
Quick fix: un-check Auto Layout on the View, and this should work as expected.
Try this. Put the desired animation in the finish block also.
- (IBAction)move:(id)sender {
[UIView animateWithDuration:0.4 delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.myButton.center = CGPointMake(200, 300);
}completion:^(BOOL finished){
if(finished){
self.myLabel.text=#"moved";
self.myButton.center = CGPointMake(200, 300);
}
}];
}
I have a UIView drawn in Storyboard which holds some buttons and which is called viewHolder.
I get a higher position rectForAnimationBefore
and a lower position rectForAnimationAfter
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:nil completion:^(BOOL finish){
[self.viewHolder setFrame:rectForAnimationAfter];
}];
when this is excuted,the viewHolder do move down.After a second,it comes up as nothing was done.
I want to moveDown,but don't want moveUp Automatically.
Because autolayout in Storyboard?
By the way ,how to move it smoothly?
Thank you guys.
UPDATE:
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
[self.viewHolder setFrame:rectForAnimationAfter];
}
completion:^(BOOL finish){
}];
when I change code like this ,I can't move it down .
Here is my solution
unlock the autolayout!!!
First thing your animation block is nil. Here you should move your view down. At completion you are setting back you view without using any animation. For smoothness you need to move back your view animatedly.
I'm doing two animations on the same UIImageView, using blocks. Animations are not quite back to back, but almost; there is some logic inbetween.
animate UIImageView from one location on the view to another.
execute logic that determines whether image is allowed to stay there
if not allowed, undo the animation (UIImageView springs back to original location)
If I implement this as above, only the second animation shows (this is normal behavior from what I understand). If I nest the logic and the second animation block inside the completion block of the first, I see both animations, but there's a fair amount of code to jam into that completion block and it just seems ugly and out of place.
In the non-nested configuration, why does iOS want to cut short the previous animations and execute only the final one, and how can I force it to wait on the first one before going to the next? I don't think it needs to block the main thread or "sit and spin" in a completion block; I just want all animations to be shown. Tried adding delay to second animation to no avail.
Is this a job for CAKeyframeAnimation?
// first animation
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationCurveLinear
animations:^{movingColor.center = newCenter;
movingColor.transform = scaleTransform;}
completion:^(BOOL finished){ NSLog(#"forward animation done");}];
if (/* ...color is allowed at that location */) {
// do some stuff
} else {
// undo the animation
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationCurveLinear
animations:^{movingColor.center = origCenter;
movingColor.transform = scaleTransform;}
completion:^(BOOL finished){ NSLog(#"back animation done");}];
}
The second animation should be done conditionally inside the first's completion block. Putting it into a function will make it more readable.
- (void)moveColor:(UIView *)view to:(CGPoint)center delay:(NSTimeInterval)delay completion:(void (^)(BOOL finished))complete {
[UIView animateWithDuration:0.5 delay:delay options:UIViewAnimationCurveLinear animations:^{
view.center = center;
view.transform = scaleTransform; // probably don't need this
} completion:completion];
}
This way your logic can be separate from the animation code
- (void)someMethod {
[self moveColor:movingColor to:newCenter delay:0.0 completion:^(BOOL finished) {
if (/*it can stay*/) {
// stuff
} else {
[self moveColor:movingColor to:origCenter delay:2.0 completion:^(BOOL finished) {}];
}
}];
}
The correct way to do it is as you said to set the second one in the completition of the first one that is the correct way,
You can also adda delay on the start of the other, this may or may not work it depends on alot of variables
Your second animation will have a delay of .5 (time for first animation to complete )
[UIView animateWithDuration:0.5
delay:0.5
options: UIViewAnimationCurveLinear
animations:^{movingColor.center = origCenter;
movingColor.transform = scaleTransform;}
completion:^(BOOL finished){ NSLog(#"back animation done");}];
The animateWithDuration is executed asynchronously, and returns right away. That is, the next line of code is executed without waiting for the animation to finish. You either put your code in the completion block if you want it to be executed after the animation is finished, or you accept that the second animation is started (thus canceling the first) immediately.
If you want to indicate to the user that something was wrong by starting the animation, but not completing it, you could execute the second animation with a delay. Remember to also set the the UIViewAnimationOptionBeginFromCurrentState option if you delay the second animation with less than the duration of the first:
[UIView animateWithDuration:0.5
delay:0.25 //or some number
options: (UIViewAnimationCurveLinear | UIViewAnimationOptionBeginFromCurrentState)
animations:^{movingColor.center = origCenter;
movingColor.transform = scaleTransform;}
completion:^(BOOL finished){ NSLog(#"back animation done");}];
I've made something like:
[UIView animateWithDuration:2.0
delay:0
options:UIViewAnimationOptionRepeat
animations:^{
NSLog(#"Repeating...");
/** code **/
}
completion:^(BOOL finished) {
/** code **/
}];
But the animation does not repeat itself.
Am I missing something?
I've removed the color animation, but the animation doesn't repeat yet. But I must be doing something really wrong, 'cause I tried the following simple code and the animation still doesn't repeat:
[UIView animateWithDuration:1.0f
delay:0
options: UIViewAnimationOptionRepeat
animations:^{
NSLog(#"Repeating...");
self.alpha = 1;
}
completion:nil];
Depending on the animation (still would like to see your code) you might have to reverse your animation in order for it to repeat. For example, if your animation changes an alpha value from 0 to 1, repeating that, without reversing the alpha back to 0, isn't going to do much. You may need to add UIViewAnimationOptionAutoreverse to your animation options.
I am trying to perform an animation on a label where a flip animation happens and after it is done and after a delay, The text of the label changes.
It seems that the delay never happens. The text immediately changes after the flip completes although I am using UIView animateWithDuration:0.5 delay:4.0 in the completion block. If Instead I do a performSelector with Delay in the completion block (the commented statement) it works as expected. Any idea why the delay value is being ignored?
- (void) flipShapeWithText:(NSString *)text {
[UIView transitionWithView:someLabel duration:0.15 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
someLabel.text = text;
}completion:^ (BOOL finished){
// [self performSelector:#selector(updateLabelText:) withObject: #"New Text" afterDelay:4.0];
[UIView animateWithDuration:0.5
delay:4.0
options: UIViewAnimationOptionTransitionCrossDissolve
animations:^{
currentShapeNameLabel.text = #"New Text" ;}
completion:nil];
}];
}
The delay param of animateWithDuration:delay:options:animations:completion specifies the delay before the animation occurs. You are setting the text within the animation block so after the delay is over, the animations begin which immediately changes the text as that change is not animatable. To do what you want, change the text in the completion block as follows:
[UIView animateWithDuration:0.5
delay:4.0
options: UIViewAnimationOptionTransitionCrossDissolve
animations:^{ // anything animatable }
completion:^(BOOL finished) {
currentShapeNameLabel.text = #"New Text" ;}];
You can eliminate the delay if you want the animation to start immediately. If you want the text change to happen 4 secs after the animation completes add that delay in the completion block either with dispatch_after() or performSelector:withDelay:.
In my case, the problem was that earlier in the code I was calling UIView's snapshotViewAfterScreenUpdates with a value true. After changing that to false it worked fine.
try nesting in
dispatch_async(dispatch_get_main_queue(), ^{
});