iOS - How to make an animation track touches
What I don't grasp - and I'm comfortable using CAAnimations and gestures - is how something can be both animated and interactive.
It is because, having set up an animation, you can set that animation to any "frame" you wish. Thus you can track the animation in correspondence to the movement of a gesture.
The way this works is that an animation is a feature of the render tree, belonging to a CALayer. CALayer implements the CAMediaTiming protocol. The timeOffset
of a CALayer thus determines what "frame" of an animation that layer displays. If a complex animation involves many different layers, no problem; just set the timeOffset
of their mutual superlayer, to control the frame of the entire animation.
That in fact is exactly how the new iOS 7 interactive custom transition feature works (to which you rightly refer in your question). For instance, in this example code:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/iOS7bookExamples/bk2ch06p296customAnimation2/ch19p620customAnimation1/AppDelegate.m
... I keep updating the UIPercentDrivenInteractiveTransition to tell it how far through the gesture the user is, and the animation therefore tracks the gesture. Now ask yourself: how the heck is this possible???
Well, the UIPercentDrivenInteractiveTransition, in turn, behind the scenes, keeps adjusting the layer's timeOffset
to portray the animation at the corresponding frame. (You can actually add logging code to my example to see that this is true.)
Moreover, when I end the gesture at an incomplete point, the animation either hurries to its end or runs backwards to its beginning - again, this is because of the CAMediaTiming protocol, which lets you change the speed
of the animation, including a negative value to run it backwards.
iPhone animation based on input values (touches) not time
You may be able to use the hierarchical nature of timelines in layer trees to achieve what you’re looking for. Objects implementing CAMediaTiming
, which include both CAAnimation
and CALayer
, inherit a timespace from their parent, which they can modify (via scaling, shifting and repeating) and propagate to their children. By setting the speed
property of a layer to 0.0 and adjusting the timeOffset
property manually, you can decouple that layer (and all of its sublayers) from the usual notion of time.
What you’d do in your case is define the animations for all your menu items to animate their position along your desired CGPath
from time t0 to t1, with the appropriate timeOffset
on each animation to keep your items appropriately spaced. Note that a beginTime
of 0.0 on an animation is usually interpreted as starting from the time the animation was added to its layer, so if you want t0 to be 0.0, you'll probably have to set it to a tiny epsilon > 0.0.
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.beginTime = 1e-100;
animation.duration = 1.0;
animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;
animation.path = path;
animation.calculationMode = kCAAnimationPaced;
animation.timeOffset = timeOffset;
You’d then set the speed
property to 0.0 on a parent layer (containing only these menu items) and update its timeOffset
to values between t0 and t1 in response to your touch events.
This approach has two potential caveats. Because you’ve taken over the nature of time on this layer subtree, you probably won’t be able to animate other properties at the same time. Additionally, if you want coasting behavior for a fast flick, you’ll probably need to animate time forward on your own.
How to keep track of animation in Sprite Kit
As @KnightOfDragon suggested : animateWithTextures has a restore: parameter, if you set that to false, when you stop animating the last texture should stay on the sprite. If you go ahead and read the textures description, then you will know what texture it stopped on, and could plan accordingly
iOS Touch, Gestures, Animation
Why don't you consider using UIPanGestureRecognizer
. You can use the translationInView:
to move the box as moves the finger across. And when the gesture's state
is UIGestureRecognizerStateEnded
, you could use velocityInView:
to get the desired follow up effect.
CollectionView messes with animations in touchesBegan and touchesEnded methods
Have you tried delaysContentTouches = false
?
https://developer.apple.com/documentation/uikit/uiscrollview/1619398-delayscontenttouches
Tells the scrollView/collectionView to not delay touches on the cells. Worked for me to make responsive cells immediately when I started tapping on them. Didn't make my scrolling buggy either.
Tracking a Core Animation animation
I faced a similar problem. I used layers instead of views for the animation. You could try something like this.
- Draw each element as a CALayer and include them as sublayers for your container UIVIew's layer. UIViews are easier to animate, but you will have less control. Notice that for any view you can get it's layer with [view layer];
- Create a custom sublayer for your quadrilateral. This layer should have a property or several of properties you want to animate for this layer. Let's call this property "customprop". Because it is a custom layer, you want to redraw on each frame of the animation. For the properties you plan to animate, your custom layer class should return YES needsDisplayForKey:. That way you ensure -(void)drawInContext:(CGContextRef)theContext gets called on every frame.
- Put all animations (both circles and the quad) in the same transaction;
For the circles you can probably use CALayers and set the content, if it is an image, the standard way:
layer.contents = [UIImage imageNamed:@"circle_image.png"].CGImage;
Now, for the quad layer, subclass CALayer and implement this way:
- (void)drawInContext:(CGContextRef)theContext{
//Custom draw code here
}
+ (BOOL)needsDisplayForKey:(NSString *)key{
if ([key isEqualToString:@"customprop"])
return YES;
return [super needsDisplayForKey:key];
}
The transaction would look like:
[CATransaction begin];
CABasicAnimation *theAnimation=[CABasicAnimation animationWithKeyPath:@"customprop"];
theAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(1000, 1000)];
theAnimation.duration=1.0;
theAnimation.repeatCount=4;
theAnimation.autoreverses=YES;
theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
theAnimation.delegate = self;
[lay addAnimation:theAnimation forKey:@"selecting"];
[CATransaction setValue:[NSNumber numberWithFloat:10.0f]
forKey:kCATransactionAnimationDuration];
circ1.position=CGPointMake(1000, 1000);
circ2.position=CGPointMake(1000, 1000);
[CATransaction commit];
Now all the draw routines will happen at the same time. Make sure your drawInContext: implementation is fast. Otherwise the animation will lag.
After adding each sublayer to the UIViews's layer, rememeber to call [layer setNeedsDisplay]. It does not get called automatically.
I know this is a bit complicated. However, the resulting animations are better than using a NSTimer and redrawing on each call.
Touch button with highlighting animation multiple times in a row
Try adding delay:0.0 options:UIViewAnimationOptionAllowUserInteraction
to your other animateWithDuration:
block. Or, you can simplify your code by combining both animation blocks like this:
Code:
-(void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
UIColor *colorToChangeTo = highlighted ? [UIColor darkGrayColor] : [UIColor clearColor];
[UIView animateWithDuration:0.1
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.backgroundColor = colorToChangeTo;
} completion:^(BOOL finished) {}];
}
Related Topics
Crashlytics iOS - Log Caught Exception
Setting Maximum Number of Characters of 'Uitextview ' and 'Uitextfield '
Facebook Sdk 3.0: How to Receive User's E-Mail
Drop Cap with Nsattributedstring
iOS Changing Uiscrollview Scrollbar Color to Different Colors
What Do JSONserialization Options Do and How Do They Change JSONresult
Slservicetypefacebook Setinitialtext Is Not Working
Modulename-Swift.H File Not Found in Xcode8
Cannot Create Outlet Connections to Subviews in Interface Builder (Xcode 5)
Request Permissions Again After User Denies Location Services
How to Get Indexpath in Prepareforsegue
Get Advertisement Data for Ble in iOS
Reverse Geocode Location in Swift
Xcframework with Pods Dependencies
How to Calculate the Age Based on Nsdate
Getting Url of Uiimage Selected from Uiimagepickercontroller