How to specify SCNAction to use scene time base - ios

I'm trying to control playback of a scene by using SCNRenderer render(atTime:viewport:commandBuffer:passDescriptor:) method, according to Apple document:
By default, the playback timing of actions and animations in a scene
is based on the system time, not the scene time. Before using this
method to control the playback of animations, set the
uses​Scene​Time​Base property of each animation to YES, or specify the
SCNScene​Source​Animation​Import​Policy​Play​Using​Scene​Time​Base
option when loading a scene file that contains animations.
I can only specify the property uses​Scene​Time​Base of CoreAnimation animation. Is SCNAction have any property to change playback to scene time?

Related

How to animate character on Demand SceneKit Xcode Swift

hey I'm trying to figure out how to animate a .scn character i can get him in the scene but i can't lets say get him to play his animation when i press a button. Really i just need him to play his animation. back about a month ago i used to import the .scn file then the animation would play automatically as soon as he hit the screen but thats not what i need. I need it to play on demand and not just only when the app launches.
Code:
let GuyScene = SCNScene(named: "art.scnassets/Guy.scn")
let characterTopLevelNode: SCNNode = GuyScene!.rootNode.childNodeWithName("Guy", recursively: true)!
node.addChildNode(characterTopLevelNode)
Check these two Apple sample projects. They both have characters animated on demand:
Fox
Badger
The steps are:
Load a scn (or dae) file that contain the character and an animation (or just an animation and the skeleton of the character).
Trigger the animation with addAnimation:forKey: on the character's
root node.
I believe you should be able to use the paused property to achieve what you want.
Both SCNScene and SCNNode have this property. You should be able to pause the SCNNode and then unpause when you want the animation to run.
characterTopLevelNode.paused = true
// Or
GuyScene.paused = true
// Then to unpause just assign to false instead

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.

custom begining keyframe of CAAnimation / changing animation speed during animation

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.

How to share Background SpriteNode among Multiple Scenes?

Well, the title says it all. If I am currently in a presented Scene of which I have initialized a Background SpriteNode to be a class constant, then how do I use the same Background SpriteNode in my next presented scene without having to create another SpriteNode that behaves and looks exactly the same as in the previous scene.
Will creating another SpriteNode be costly, assuming the background object loads its texture from a texture atlas?
And, I am using Swift.
Something like this should work.
backgroundSprite.removeFromParent()
nextScene.addChild(backgroundSprite)

Resources