iPhone animation - iphone

I trying to do an animation on the iPhone and I hope someone can help me out here as I'm stumped.
I am using CAKeyframeAnimation with CAPath to animate views around.
I have these settings set:
animation.removedOnCompletion = YES;
animation.fillMode = kCAFillModeRemoved;
So that the animation is removed from the layer when the animation finishes I want the view to respond to touches. As I have found that if I set:
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
then the view no longer responds to touches after the animation.
This has also had the negative side effect of having to manually position the view to its finished location when the animation finishes.
The theory of what I'm doing works but in practice there is a jump of the view back to its original position before I can manually set its position in the animationDidStop method.
So is it possible to have the final position of animation stay and the view respond to touches?

Setting the fill mode to kCAFileModeForwards causes the animation to appear as if it is at the last step in the animation, but it is only the appearance. You actually need to set whichever property you are animating. If it's the position, you need to call:
[layer setPosition:destinationPoint];
on the layer. Then you don't have to set the fill mode or tell it not to remove the animation on completion. You will need to make sure that you set the animation for key "position" in order to override the default animation with your own. Call -setPosition before you add the animation to the layer. Something like this:
[layer setPosition:destinationPoint];
[layer addAnimation:kfAnimation forKey:#"position"];

Related

UIView 3D transform final frame location

I have a UIView which has an X origin that makes it off screen to the right. Then, I do a keyframe animation with CATransform3D:
[NSValue valueWithCATransform3D:CATransform3DMakeTranslation(-view.width, 0, 0)]
The problem is, after the animation completes, the view's frame is visually in the correct place, but it still thinks it's off screen, so I can't interact with it. Logging its frame property also shows that it's offscreen, but visually, it's not.
The fill mode for the animation is kCAFillModeForwards, so the final value of the animation sticks.
What is the solution to this problem, that is, interacting with this view after the animation and notifying the view that it is indeed visible?
You can use CAAnimation delegate method -animationDidStop:finished: to fix it when the animation done:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
...
}
The better answer is to use UIView animation methods like animateWithDuration:animations:completion: (and variants on that method.)
Those methods let you do animations on the actual view properties, and when the animation is complete, the view is at it's actual destination location.
The center property is the easiest way to move views around. The frame property isn't valid if you've applied a transform, but the center property IS always valid for both reading and writing.
Core Animation only creates the illusion that your views move. The animation takes place on a display-only copy of the layer tree. You can query the properties of an animating layer by reading those same properties from the layer's presentationLayer. However, the view objects will not respond to the user interaction at their apparent location. The views still think they are at their original locations.
As the other poster said, you could use the animation completion delegate method to move the view to it's final location once the animation is over, but that's a lot of fussy work to do something that UIView animation does for you much more cleanly and simply.
Can you interact with the view even before you animate it?
is the view.userInteractionEnabled is set to YES??

Apply changes of CAAnimation while animating

I want to animate an UIView with a given CAAnimation and interact with the view while it's animating (the view has a gesture recognizer).
If I understand correctly CAAnimation only animates the view's layer, but the view itself will remain in the original position.
How can I make the UIView move with the CAAnimation?
If you only care about visual "stickiness" you can do:
[myAnimation setRemovedOnCompletion:NO];
But this is only visual. It doesn't change the actual layer's properties.
If you want the layer's properties to be updated to new values also, you can do something like:
// You have code above that to register the property you want to animate...
[myAnimation setFromValue:[NSNumber numberWithFloat:0.0f]];
[myAnimation setToValue:[NSNumber numberWithFloat:1.0f]];
Rather than attempting to move it, you could expand your UIView to cover the area you want to receive touches in, and then animate its layer within it. Make sure to set the view opaque property to NO.
You may want to add a sublayer to view.layer rather than moving the view's own layer, for example if you want the layer to go outside the bounds of the view without affecting the layout of subviews of the view.

Determining which subview was touched

I'm trying out the animation stuff with the iPhone and I have a small app that has circles falling from the top of the screen to the bottom. I added the UIViewAnimationOptionAllowUserInteraction option to the animation and set userInteractionEnabled = YES on both my sub views and my main view. I am generating the subview programmatically everytime my NSTimer fires, which makes the circles.
The problem I am having is that even if I implement - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event on my main view and my subviews, the event only ever fires on the main view regardless of how hard or how many times I smash the falling circles.
How do I get that event to fire on my circles instead? Why doesn't the touch fire on the view on top?
The problem is that when you perform a "tween" animation with Core Animation (which occurs implicitly when you use the UIView animation methods), the new value is immediately applied to what's known as the model layer tree.
The animation is committed to Core Animation, and the next time the main run loop occurs, Core Animation does the necessary rendering to make it look as if an animation is occurring. In reality, though, the model values of your view (e.g., its frame or transform properties) are changed immediately.
Try tapping at the bottom of the screen where the buttons land (assuming they finish on screen). If you do this, it should respond to the interaction event as you expect.
You could manually change the frame of your circles by constantly firing a timer and applying small changes at each step. This, however, is likely not ideal.
Another option is to make use of your view's layer's presentationLayer method. This layer represents the state of the layer at any point in time, as we see it on the screen. Off the top of my head, I imagine that your main view's touch handling method can iterate through each subview and check to see if the touch point intersects the frame of the subview's layer's presentation layer. If it does, then that means someone "touched" your subview.
for (UIView *subview in self.subviews)
{
CALayer *presentationLayer = (CALayer *)[subview.layer presentationLayer];
if (CGRectContainsPoint(presentationLayer.frame, touchPoint)
{
// Found the subview the user touched
}
}
I should note that presentationLayer returns id, because the presentation layer may be CALayer or any of its subclasses. So, for example, if you're using CAShapeLayer, then -presentationLayer will return a CAShapeLayer. Still, it's fine to just cast it to a CALayer since all you care about for the purposes of this task is to check its frame property.

UITextField editing issue - iPhone SDK

I am having an issue with my UITextFields in my iPhone app that I am hoping someone here may have seen before.
I have a few UITextField's on my screen, and when they are tapped and they keyboard appears they are covered. To overcome this, I animated the view to move up with:
-(void)moveViewUpFromValue:(float)fromOldValue toNewValue:(float)toNewValue keep:(BOOL)keep {
CABasicAnimation *theAnimation;
theAnimation=[CABasicAnimation animationWithKeyPath:#"transform.translation.y"];
theAnimation.duration=0.25;
theAnimation.repeatCount=1;
if(keep) {
[theAnimation setRemovedOnCompletion:NO];
[theAnimation setFillMode:kCAFillModeForwards];
}
theAnimation.fromValue=[NSNumber numberWithFloat:fromOldValue];
theAnimation.toValue=[NSNumber numberWithFloat:toNewValue];
[self.view.layer addAnimation:theAnimation forKey:#"animateLayer"];
}
This, visually, works great, however, when I go to edit (hold the screen down to get the magnifying glass), it doesn't appear. The interaction are is retained in the position before it moves.
Any ideas what could be going on here?
Many thanks,
Brett
This is an interesting way to animate such a thing, but not the most common one.
You're actually animating a transformation of a view - not the actual position of the view. Appearantly the magnifying glass doesn't obey the transformation (quite logical, for it would sheer, stretch and skew with the view otherwise).
So rather animate the position, i.e. self.view.frame or self.view.center (which boils down to the same thing). Furthermore I usually use [UIView beginAnimations...] etc but if your approach works, it's probably just as fine.
You've made the view appear to be in a different place, but because you haven't actually changed its position it is still effectively in the old location for things like hit testing, etc.
I would start by animating your view's center instead of its transform. This you can do with just a UIView animation instead of a CABasicAnimation. Additionally, your view will actually end up at the correct position when the animation is complete so things should work as you expect.

Halting In Progress CAKeyframeAnimation

I'm animating a UIView's frame property using a CAKeyframeAnimation on the view's CALayer and animating the "position" property. I'd like to be able to halt the animation at it's current state when the user taps the screen, but I'm unable to do so.
I can halt the animation easily enough by calling
[view.layer removeAnimationForKey:kFrameAnimationKey];
The problem is that neither the view's frame not it's layer's position are updated directly by the animation. If I look at the position property at the time the animation starts and when it ends in
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished
it has not changed.
It seems that you need to do that explicitly when the animation stops. But if the animation stops at some arbitrary point, you don't know how far it's gone. So the question is either how to make the animation update the layer's position property as it goes, or how to know how far the animation has gone when it's been stopped.
You can achieve this halting of the animation by grabbing the presentationLayer of your animating UIView's layer, then applying its frame to your UIView before removing the animation. For example:
movingView.frame = [[movingView.layer presentationLayer] frame];
[movingView.layer removeAnimationForKey:#"movementAnimation"];
This seems to provide the freezing of the UIView at the current animated position you're looking for.