custom begining keyframe of CAAnimation / changing animation speed during animation - ios

Is it possible to set custom begining keyframe of animation from collada, .dae file?
For example to make an animation a half-length of animation.
Or is it possible to change animation speed during that animation?

You can change the timing of an animation so that you start at an arbitrary time, it is also possible to change the speed of an animation (before it is added to the node).
Look at the timeOffset and speed properties in the CAMediaTiming protocol (which CAAnimation confirms to)
I've previously written about how use these and the other timing properties to control the timing of an animation.

Related

Animate view and stop midway

I want to make a view/header collaspe/expand animation, but I also want to be able to pause or stop from expanding/collapsing while the user takes his finger off the screen.
I have a current implementation for this, but the animation is not very smooth.
I will also add some images to explain better what I want to do.
So the first image is how it should look when expanded.
The second image is how it should look if the user decides to take take his finger off the screen while he would scroll slowly so the animation/the view should stop in a state similar to that.
The third image is how it should look when it is collapsed.
If someone has any recommendation how I could achieve this I would be very grateful.
If you are targeting iOS 10+, use UIViewPropertyAnimator which encapsulates an animation and allows scrubbing and controlling the animation:
Start, pause, resume, and stop animations; see the methods of the UIViewAnimating protocol.
Add animation blocks after the original animations start using the addAnimations(_:) and addAnimations(_:delayFactor:) methods.
Scrub through a paused animation by modifying the fractionComplete property.
Change the animation’s direction using the isReversed property.
Modify the timing and duration of a partially complete animation by pausing the animation and using the continueAnimation(withTimingParameters:durationFactor:) method to finish it.
You just need to wire up touches to UIViewPropertyAnimator object encapsulating the animation.
There are many good tutorials on how to start with it, e.g. this one.

Observe progress of UIView.animateWithDuration/CABasicAnimation?

Is there a way to observe the "time progress" of UIView.animateWithDuration...family of methods from UIView /alternatively CA animations?
I am animating a view's frame and I need to be informed how it is progressing.
My line of thinking was I can either
1) tap into CAAnimation related stuff or
2) observe the animated properties (like frame) and do my own calculations each screen frame.
Approach 1) turns out to be a dead end, inspecting the internal of how CAAnimations work told me absolutely nothing...and 2) is flawed as the "model layer tree is updated immediately and tapping into the presentation tree is difficult as the presentation layer is nil when you start.
I am pretty desperate, I was thinking that hooking into CADisplayLink will give me a tick and then I simply check something that is affected by the animation but there is nothing to tap to.
Do you think going the NSTimer way that is launched in the same scope as the animation method is ok? If I know animation duration then I can generate the progress myself.
If all you want is the time value, then you can do math on the CACurrentMediaTime() minus the animation start time. I have a sample project on Github called KeyframeViewAnimations that does exactly that.
That project supports pausing and resuming and scrubbing both UIView and CAAnimation based animations. In both cases it digs into the underlying CAAnimations.
I have another project that uses the values of the animated layer's presentationLayer in order to do hit testing so you can tap on an in-flight view and start/pause the animation. That one can be found here:
iOS-CAAnimation-group-demo
My code uses an NSTimer to update the progress of the animation. It would be better to use a CADisplayLink timer, as you mentioned.
I am also looking at the new UIViewPropertyAnimator class that was added to iOS 10. That makes pausing, reversing, and scrubbing UIView animations easy without having to dig into the underlying CAAnimations. See this thread I just posted:
Is there a way to observe changes to fractionComplete in UIViewPropertyAnimator

Limit effect of UIPercentDrivenInteractiveTransition to topmost view

Is there a way to limit the effect of UIPercentDrivenInteractiveTransition to only the topmost view in a view hierarchy?
Specifically: as explained here and here the interactive transition sets the container view layer's speed to 0 and then manipulates the timeOffset to scrub through the transition.
If for example I have an activity indicator in that containing view, the interactive transition also scrubs through the activity indicator's spin animation. It stops spinning and appears to "roll" forward and back with the interactive transition.
Is there a way to localize the effect of setting speed and timeOffset and prevent them from propagating through to any or all subviews?
So far, I can think of two possible approaches:
Create a "barrier" layer: subclass CALayer and override setTimeOffset: to prevent or selectively prevent changes
Subclass or replace UIPercentDrivenInteractiveTransition with something that traverses the subview hierarchy and selectively hits only certain views
Any other ideas would be welcome.
You should tell the activity animator to stop animating, but keep it visible, during the transition. This is consistent with how Apple handles this in its apps. For example, in the Mail app, do pull-to-refresh to get new messages. While the indicator is spinning, use the interactive pop gesture recognizer to about halfway. Notice that the activity indicator stops during the interactive transition.
Why don't you just hide the activity indicator for the duration of the animation? Surely it has no purpose being there, has it? After all, it cannot mean "animation is in progress" - the user does not sense this as an animation, or even as a time-consuming transition, but as a gesture being driven by a finger. You can easily show it again when the animation is over, by using the animation's completion handler.
The answer to this question is no. According to Apple, the CALayer class adopts CAMediaTiming "allowing a layer to define a timespace relative to its superlayer". In other words, whatever values of timeOffset or beginTime are specified in a child layer are summed with the layers above. As the docs state, "This concept of a layer-tree timespace provides a scalable timeline that starts at the root layer, through its descendants."
The UIPercentDrivenInteractiveTransition acts by scrubbing through the timeOffset of [transitionContext containerView].layer. (transitionContext is an id<UIViewControllerContextTransitioning>).
Therefore it is applying a time offset that is then automatically adopted by all child layers down the sublayer chain.
There is no such thing as a "barrier" or "selectively hitting only certain layers" because this time propagation is handled by Core Animation internally.

iOS - How to make an animation track touches

What is the best way to implement a smooth reversing animation which tracks touches? I am referring to those animations in which, for example, if the user executes a swipe gesture some elements smoothly animate on screen, and others off, but if the user instead slowly drags a pan gesture back and forth the same objects will move forward/backward as a percent in accordance with the touch position. This is seen in many app intros and also in transitions. I have found
One tutorial which discusses the built-in facility for this but it is only between view controller transitions, not providing the full granular control I see in many apps (http://www.doubleencore.com/2013/09/ios-7-custom-transitions/)
Jazzhands, which is a kit by IFTTT, but this is a packaged solution that might not cover how the solution is best implemented at a lower level (https://github.com/IFTTT/JazzHands)
A question here for which one answer shows how you might execute an animation after a gesture ends (iOS Touch, Gestures, Animation)
What I don't grasp - and I'm comfortable using CAAnimations and gestures - is how something can be both animated and interactive.
Typically, when I create an animation, I commit the animation and it goes from start to finish. While I could interrupt the animation as touches continue, that seems like it would be stilted.
On the other hand, moving things in response to user input is easy, but that is not animated.
How is the effect achieved where something can change according to an animation, but also have that exact same animation occur tied to touches, and yet still also have it so that although the animation reaches completion it doesn't really "finish" (become irreversible) unless the user releases touch, while at any point during interaction if the user releases panning then the animation either reverts backwards to its starting position or animates to completion depending on the last touch location and velocity. These requirements are baffling.
The glimpses of this technique I see all involve keyframe animations, but what I don't understand is where the touch events intersect with an animation to create these smooth effects I see.
Any tips, examples, or tutorials are most welcome.
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.

Is it possible to control a CAAnimation's timeline?

I have a complex animation that usually runs just by itself, driven by a certain CAMediaTimingFunction. This works fine.
Now, I want to control that same animation's time(line) using an external value, for example from a slider or a gesture recognizer. In other words, I don't want to have the "clock" drive the timeline, but a slider, so one can scrub back and forth with it and "freeze" the animation by putting the slider to a certain value.
Is this possible? If so, how?
It's possible (and quite easy), but I only tried this as an experiment (for a complex animation driven by a pinch gesture recognizer), so I'd love to hear if this solution is sufficient:
You need to set the speed of your animation to 0 and the time offset to the point in time you want to jump to, e.g.
CABasicAnimation* animation = [CABasicAnimation ...];
animation.speed = 0;
animation.duration = 1;
animation.timeOffset = 0.5;
will make the animation jump to the point it would be after half a second.
Now, you can't manipulate CAAnimation objects after they've been added to the layer, so you'll need to add a new animation every time the offset changes (and remove the old one, don't forget ;).

Resources