I have an NSTIMER that counts the time, and on t = 10, it fires an animation but during that time it might happen that another animation is running. This causes the previously running animation to cut off.
Any idea? i thought UIVIEW animations were ran in diff threads. I cannot use a willstop selector here since t = 10 might happen while another animation is running and might not have ended yet.
You need to call [UIView setAnimationBeginsFromCurrentState:YES];
Related
I've created an UIView and some layers. I've organized those layers into superview-subview hierarchy with the root in UIView's layer, added some gesture recognizers and I am trying to manipulate layers' geometry on events from gesture recognizers (setting bounds and position for sure). I use implicit animations and CATransactions. No explicit animations are used.
The actual result is good enough if I create CATransaction with duration = 0. But if I set the duration to 0.2 I get some strange results: as event come not rapidly and previous transaction have time to be completed before the next starts everything is OK (for example on tapping), but if there is not enough time (for example on pinch or pan) the layer being resized starts jerking. Visually it looks like animation rolls back to the initial point and starts again to the new value.
I do not see any reason for this. I've tried to perform the layout in -[UIView layoutSubviews] and invoke setNeedsDisplay in gesture recognizers handlers. I've also tried to separate this logic into separate method but it does not help.
I repeat once more that I use only implicit animations. And what I want to know is why does it happen (but not how to work around).
Any clues?
I think, it's because you start new animation when previous not finished.
Maybe, you need to stop it like
[yourView.layer removeAnimationForKey:#"yourAnimationKey"];
and you need to implement animation delegate method
-(void)stopAnimation:(id)sender
{
yourView.frame = [[yourView.layer presentationLayer] frame]; // that set frame of your view to it's animation position
}
i wonder how i can have continuos and repeating animations on my Views w/o freezing the rest of my app - since my current approaches lead into 100% animation, 0% user interaction possible - which is kind of .. bad.
the animation in general is simple: i want to make an uilabel act like a siren, altering the alpha-property from 1 to 0 and back and so forth.
the basic animation block therefore is:
[UIView animateWithDuration:0.4f
delay:0.0f
options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionBeginFromCurrentState
animations:^(void){warningMessage.alpha = 0;}
completion:NULL];
i tried to call it in background, in backgroundthreads, even in an async GCD block, each time i loose control of the app - but the animation works fine.
(ok, apple docs say it MUST happen on main thread, but i tried..)
Now again, the question: how to have both of it? is it even possible in that way?
Thanks in advance!
P.S.: Sry if this is quite a repost, did not find a proper solution/question here!
P.P.S: yes, i know that sirens dont alter their alpha propery :P
did you try adding the UIViewAnimationOptionAllowUserInteraction to the options flag?
Well.. doing animations with uikit is for sure easy .. but I would not call it very efficient. You just animate the alpha, but there is much more happening in the background. :)
Animations already run in a different thread.. so starting it on an other thread won't help you.
As a solution I would register a CADisplayLink in your application and change the alpha of the view in it's update function ... that will be much more efficient if you run it all the time.
I want to fade-out a view as it is scrolling inside a parent UIScrollview. When the fade-out animation begins, the scroll view stops scrolling. It jumps to the correct position when the fade is complete.
My fade-out is achieved with animateWithDuration and block objects, triggered upon a page-change I detect in scrollViewWillBeginDragging.
Does anyone know how to make them both happen simultaneously? Just to be clear, I am not 'animating' the UIScrollView scrolling - rather it is happening via user interaction of swiping.
EDIT:
Here is the code I'm using to fade the UIView. This code is in a UIViewController derived class, which is the delegate for a UIScrollView. When the user starts dragging his finger, I want to fade out the subView. But when the user starts draggin a finger, the subview fades and the scrolling stops. After the subView has completely faded out, the the scroll view will then snap to the location where the user's finger is.
-(void)scrollViewWillBeginDragging:(UIScrollView*)scrollView
{
[UIView animateWithDuration:0.5
animations:^
{
self.subView.alpha = 0.0f;
}
completion:^(BOOL finished) { }];
}
A little late, but if you want to keep using blocks, you can use:
animateWithDuration:delay:options:animation:complete:
add "UIViewAnimationOptionAllowUserInteraction" to options to allow interaction while scrolling.
I'm sure that you will still have the lag problem. Here's the best way I can explain it. Please forgive me in advance since I'm probably using the wrong terms. All animations must run on the main thread. When you call an animation, iOS first *P*rocesses then it *R*enders before it generates *F*rames. It looks like this.
PPPPRRRRFFFFFFFFFFFFFFFFFF
But since ScrollViews don't know how long your animation is going to be or when it will end, it has to perform the animation like this.
PRFPRFPRFPRFPRFPRFPRFPRF
My theory is that the lag you are experiencing has to do with these two calls colliding on the main thread at the same time. I'm not sure how you would solve this problem other than with a faster chip. I've that you could push one animation to the CPU and one to the GPU, but I'm not that advanced at programming yet.
very interesting ... I've checked this out, and yes, i have the same effect ... Well, it seems that the animateWithDuration somehow blocks the main thread ... which is not logical, and the documentation doesn't say anything about it either ..
However there is an easy workaround, something similar to this: (i've set the animation duration to 3 so i can see that it's working while i'm moving my scroll view :) ...)
[UIView beginAnimations:#"FadeAnimations" context:nil];
[UIView setAnimationDuration:3];
self.subview.alpha = 0.0f;
[UIView commitAnimations];
I would suggest, since the opacity is based on the user's finger's movements in the UIScrollView, using the delegate method scrollViewDidScroll:. The scrollView passed as a parameter can be used to check the contentOffset which is simply a CGPoint indicating how far into the content view of the UIScrollView the user has scrolled. Something like this can be used to relate the scroll position to the opacity of a given view in a paginated UIScrollView:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// The case where I used this, the x-coordinate was relevant. You may be concerned with the y-coordinate--I'm not sure
CGFloat percent = ((int)(scrollView.contentOffset.x) % (int)(scrollView.frame.size.width)) / scrollView.frame.size.width;
if (percent > 0.0 && percent < 1.0) { // Of course, you can specify your own range of alpha values
relevantView.alpha = percent; // You could also create a mathematical function that maps contentOffset to opacity in a different way than this
}
}
According to information that is still not supposed to be widely released, all iOS 4.x versions completely block user interaction while the animation is in progress.
Isn't it interesting, though, that you're UITouches are obviously still registered during the animation? Hmm... maybe that HINTS that something NEW is coming in a yet-to-be-released version!
I.e., If you can, read the iOS 5 Beta documentation on UIView class methods.
I've been trying to make a short animation that reverses halfway through with a static method that can be called by my view controllers. This works fine.
But I need it to perform a selector when the reverse takes place, essentially in the middle of the animation. The method setAnimationDidStopSelector only fires when the entire animation is completed, not in the middle.
So I split the animation into two blocks. However, when I do this, the first animation is instantly completed, then the second one happens. I suspect it's because the animation is on the same view. Perhaps there can't be two separate animation blocks for the same view?
I've set up the second block with a different animation name, and with the appropriate delay, and even tried setAnimationBeginsFromCurrentState (even though it's discouraged in OS 4), but none seem to solve the problem.
Here's some sample code to get an idea of one of the ways I've tried. 'selector' is the method I need called halfway through (once the alpha is transparent).
[UIView beginAnimations:name context:view];
[UIView setAnimationDuration:(duration/2)];
[UIView setAnimationDelegate:delegate];
[UIView setAnimationDidStopSelector:selector];
[view setAlpha:0.0];
[UIView commitAnimations];
name = [name stringByAppendingString:#"_reverse"];
[UIView beginAnimations:name context:view];
[UIView setAnimationDelay:(duration/2)];
[UIView setAnimationDuration:(duration/2)];
[UIView setAnimationDelegate:delegate];
[UIView setAnimationDidStopSelector:selector];
[view setAlpha:1.0];
[UIView commitAnimations];
Edit: I want to avoid animation 'blocks' as I'd like the code to be compatible with pre-OS4. I'd also like to keep the method static so I can use it as a library method.
Have you tried putting the second part of the animation into a method, and use a NSTimer or performSelector: withObject: afterDelay:?
I don't know if these work or not, but UIView animations and drawing stuff tend to have weird interactions when a lot of stuff is done concurrently. For example, you set a couple animations to go off one after another in a single method. Spacing them out so that the first animation has no knowledge of the upcoming one might help. I do realize that you do set a delay on the second one, but UIViews are weird in that way.
Edit: Just tried it out, works like a charm.
I have an animation which I kick off like this:
[UIView beginAnimations:#"doThis" context:self];
[UIView setAnimationDuration:1.5f];
[UIView setAnimationDelay:2.5f];
Now, the problem is that this animation is told to start in 2.5 seconds. But in the meantime, something may happen and I don't want the animation anymore. However, CA will just animate that thing after 2.5 seconds, no matter what happens. How could I say "no, thank you, don't animate"?
I have other animations going on in different context and animationID, so I don't want to just remove all animations from the app. What's the most clean way of achieving this? Just running another nonsense-animation with same context and animationID and old targets?
If you re-set the properties that are currently animating, that should kill the animation. If you have an animation delegate/didStopSelector set, the method will be called with kCFBooleanFalse as the "finished" parameter in this case.
There is another related question here:
Cancel a UIView animation?
One other thing you could do is to put your animation code in a method and then use:
[self performSelector:#selector(animateMethod) withObject:nil afterDelay:2.5];
Then you can add a check in the beginning of "animateMethod" to see if the animation should still be performed. This however does not help if you want to cancel the animation while it is running.