I need to update a view continuously depending on another view's position while it's moving across the screen. I tried using KVO on frame, but it seems to trigger only at the beginning of the animation. Is there a recommended way of doing this?
You want to have a look at the Core Animation presentation layer which should tell you the position during the animation.
I would add a repeatable timer and make something like:
[self updateView:_view
usingPosition:[_anotherView.presentationLayer position]];
(Don't you know _anotherView's animation in advance? Maybe then you can setup _view animation directly?)
Why not just set both animations for both view at the same time?
[UIImageView animateWithDuration:aDuration
delay:0
options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState
animations:^(void) {
view1.center = somePosition;
view2.center = someOtherPosition;
} completion:NULL];
They will move at the same time and with the option : UIViewAnimationOptionBeginFromCurrentState if you start an other animation before this one is finished the views will start at the point they were stopped.
But if it's not what you are looking for, can you update your question with precision on how view1 is moving.
Related
I am performing animation on my view. But I am facing few problems with that. I have to button's startAnimation and Stop Animation. For performing animation on my imageview I am using following code in startAnimation button's action:
UIViewAnimationOptions option = UIViewAnimationOptionRepeat + UIViewAnimationOptionAutoreverse;
[UIView animateWithDuration:2.5 delay:0.0 options:option
animations:^{
CGAffineTransform scale = CGAffineTransformMakeScale(2.0, 2.0);
CGAffineTransform translate = CGAffineTransformMakeTranslation(-50.0, -100.0);
CGAffineTransform mix = CGAffineTransformConcat(scale, translate);
imv.transform = mix;
}
completion:^(BOOL finished) {
}
];
This is working.
Now the problems I am facing are:
How to stop this animation on my stopAnimation button's action?
If ImageView is animation and I enter in background after that I again come in foreground ImageView sticks there and do not animates.
How to solve these issues?
On your stopAnimation button you would need to remove animations from the UIView's layer. First you must import Quartzcore.framework.
#import <QuartzCore/QuartzCore.h>
[yourView.layer removeAllAnimations];
As for your other issue of pausing your animation while going into the background and coming back to foreground, theres no way to 'pause' an animation. Your best bet would be to manually handle that situation by restarting your animation.
To stop the animation you can use
#import <QuartzCore/QuartzCore.h>
...
[imv.layer removeAllAnimations];
As for making an animation restart it is a complex problem and the answer depends on what you want the application to do when it restores. One possible answer would be to keep track of where you are animating the ImageView to and then in ApplicationDidEnterBackground you could cancel all the animations and simply move it there. Then when you restored it would be in the correct location.
If you actually want to continue the animations I would have a read of some other Stack Overflow answers and see if that helps.
Restoring animation where it left off when app resumes from background
iOS, Restarting animation when coming out of the background
For further reference, for the second part of your response you have to restore the matrix to the identity before doing anything:
imv.transform = CGAffineTransformIdentity;
I've got a weird issue with using UIView block-based animations. My app contains an assortment of images in scroll views and, after a while of using the app on the device, all animations in UIView animation blocks (such as the one below) stop animating. The animation is processed (in the example code below, the pageView does move) but this change isn't animated and happens instantly.
[UIView animateWithDuration:0.5 animations:^
{
self.pageView.center = CGPointMake(self.view.frame.size.width/2, 704.0/2);
self.pageView.transform = CGAffineTransformIdentity;
}
completion:^(BOOL finished)
{
NSLog(#"complete");
}];
Has anyone else experienced similar behaviour? I'm using an iPad on 5.1 and am wondering if it could be down to iOS version?
Thanks,
Michael
Check your memory usage. I am seeing the same thing you are -- eventually all of the animations stopped working. The animations didn't even run for me, and completion blocks weren't firing.
I found that I was holding on to a number of UIImageViews that weren't visible anymore because I wanted to re-show them later. Removing them and freeing up the UIImage made the problem go away. I now need to figure out how I'm going to get back the images but at least the animations work as expected.
When you're using transforms, animations can have a really unpredictable behavior if you use them separately. To avoid this, you ought to concatenate all your transforms into a single one via CGAffineTransformConcat and animate that single transform.
Try using some animation options and see if that helps?
For example here I'm letting the user interact and letting views redraw themselves during an animation :
[UIView animateWithDuration:0.5
delay:0
options: UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionAllowAnimatedContent
animations:^ {
self.pageView.center = CGPointMake(self.view.frame.size.width/2, 704.0/2);
self.pageView.transform = CGAffineTransformIdentity;
} completion:^ (BOOL finished) {
} ];
I want to implement a simple animation based on UIView's center property. I have a simple view and I can drag it (UIView touchesMoved is overriden). The animation should fade slowly like the view is moved under it's own inertia for some time after the user releases it. But for now I want simply to move the view after touch ends. Here is the code I have in touchesEnded:
int i;
for (i=1;i<4;i++)
{
self.center = CGPointMake(10*i, 12*i);
[self setNeedsDisplay];
usleep(100000);
}
The problem is when I run this, the code is executed nicely but "UIView" changes to late. I changed usleep time and other parameters but the result is the same. It looks like all the "pending changes" in the view are performed only after the overriden touchesEnded is finished.
Is this the right way of implementing such user interface feature or should I look for some other approaches?
Thanks in advance.
If you know the final positions you want your view center to have you could do this with an animation:
[UIView animateWithDuration:20
delay:0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
youView.center = CGPointMake(newCenterX, newCenterY);
}
completion:^(BOOL finished){
}];
If that code is being run inside of touchesEnded you are blocking the main thread and that is why the animation happens all at once when its done. You will need run this loop in the background to update the view over time. When you update the UI remember to always call back to the main thread.
Some Threading Options:
performSelectorInBackground:withObject:
performSelectorOnMainThread:withObject:waitUntilDone:
Grand Central Dispatch
Threading
I have a problem with performance on iPad. I need to drag UIView with animation. It's like self made UIScrollView, but with bigger bounce effect.
I use self made animation cycle with NSTimer (scheduletTimerWithInterval 0.3 sec). On each iteration I move view step by step.
But the problem is that it's freeze time to time. Not too much... but enough to feel.. Do anyone knows any approaches to do following thing in correct way, so it will work as charm?
Thanks in advance!
You should use [UIView beginAnimations] instead of the scheduled timer. For example, to move something over 2 seconds, you could do something like this:
YourView.center = CGPointMake(0,0);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:2];
YourView.center = CGPointMake(30, 30);
[UIView commitAnimations];
This will move YourView from 0,0 to 30,30 over 2 seconds (that's what the setAnimationDuration is for). So you would have a sequence of these if you wanted to do a bounce effect. You can also be notified when the animation completed by using
[UIView setAnimationDidStopSelector:#selector(somefunction)]
at which point you could kick off another animation in your function "somefunction".
Perhaps the timer causes your problem. The interval of animations should be something like that: 1.0/60.0f
So I have some animation blocks to create some very simple animations on my app (eg. a wheel spinning continuously). This is the code for the animation (I have changed if from the old commitanimations style block but was getting the same problem with that).
[UIView animateWithDuration:30 delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionCurveLinear
| UIViewAnimationOptionRepeat)
animations:^(void){
wheel.transform = CGAffineTransformMakeRotation(M_PI*-0.5);
}
completion:^(BOOL finished){
if(finished){NSLog(#"^^^^^^^^wheel^^^^FINSIHED");
]}
}];
The problem I have is that on OS4 when the app has been dismissed to the multitasking bar and resumed the animation stops. If on resume I reset the position when the app becomes active again, like this....
wheel.transform = CGAffineTransformMakeRotation(0);
then it continues.
This isn't ideal because my animation skips when it restarts. I don't understand why this happens or why I should need to do this.
The "on finished" method gets called almost immediately as the animation starts which is odd as this animation should never finish. Also it DOES NOT get called when the app becomes active and the animation actually stops.
Anyone any ideas or suggestions? I'm been struggling with this for some time now...
You should either setup a timer using NSTimer or use CADisplaylink like so:
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(animateImages:)]; and change the rotation CGAffineTransformMakeRotation(degrees) of your view in the delegate method.
I'm having a similar problem. I was able to overcome this problem by
turning off the ability for the app to run in the background by adding
the key: "Application does not run in background" and setting it to YES
in the info.plist. If your app does not need to run in the background and
is OK if it is brought back to its initial state when resuming then this
will work.
The syntax of your code is invalid; your code will not compile as shown. A correct version would be:
[UIView animateWithDuration:2 delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionCurveLinear
| UIViewAnimationOptionRepeat)
animations:^(void){
wheel.transform = CGAffineTransformMakeRotation(M_PI*-0.5);
}
completion:^(BOOL finished){
if(finished) NSLog(#"^^^^^^^^wheel^^^^FINSIHED");
}
];
The reason why the "on finished" block is called when the app resumes after suspension and you set the animation going again is that the wheel's transform is already at its final position (this is where you set it in your animations block). So, when the app resumes, you must first reset the wheel's transform (I would do this by setting it to CGAffineTransformIdentity) and then set the animation going.
A smarter approach would be to use CGAffineTransformRotate (and not CGAffineTransformMakeRotation). That way, you can set the transform based on what the transform is now, rather than setting it absolutely.
[UIView animateWithDuration:2 delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionCurveLinear
| UIViewAnimationOptionRepeat)
animations:^(void){
wheel.transform = CGAffineTransformRotate(view.transform, (M_PI*-0.5));
}
completion:^(BOOL finished){
// whatever
}
];
I presume that your wheel is symmetric with respect to your chosen angle, so the user will never see the difference, and the slip you're complaining about (when you reset the transform) will be eliminated.