SCNTransaction AnimationOptions - ios

I have an animation in my SceneKit project. It animates the node that is pressed. Now, the animation works with this code:
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
SCNTransaction.setCompletionBlock {
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
result.node!.position.y -= 1
SCNTransaction.commit()
}
result.node!.position.y += 1
SCNTransaction.commit()
}
I want to make the node seem like it is jumping, therefore I would like to use some animation options, such as you can use with a UIView: CurveEaseIn etc.. (I want it to start slowly, end abrupt. The second should be abrupt first, then slowly.)
Is there a way to use these for a SCNTransaction? Or is there maybe a better way to make it 'bounce'?
Thanks in advance :)

Changing the timing function
Yes, you can change the timing function with SCNTransaction.
You do that by creating a CAMediaTimingFunction object and assigning it using setAnimationTimingFunction(). There are a couple of named timing functions (for example "ease in"), or you can create one using two set of control points.
let easeIn = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
SCNTransaction.setAnimationTimingFunction(easeIn)
Using a key-frame animation
Another alternative is to create a more custom key-frame animation for the node's y-position. I did a bounce looking key-frame animation in the sample code for Chapter 5 of my book about Scene Kit:
var jump = CAKeyframeAnimation(keyPath: "position.y")
let easeIn = CAMediaTimingFunction(controlPoints: 0.35, 0.0, 1.0, 1.0)
let easeOut = CAMediaTimingFunction(controlPoints: 0.0, 1.0, 0.65, 1.0)
jump.values = [0.000000, 0.433333, 0.000000, 0.124444, 0.000000, 0.035111, 0.000000];
jump.keyTimes = [0.000000, 0.255319, 0.531915, 0.680851, 0.829788, 0.914894, 1.000000];
jump.timingFunctions = [easeOut, easeIn, easeOut, easeIn, easeOut, easeIn ];
jump.duration = 0.783333;
yourNodeThatWasClicked.addAnimation(jump, forKey: "jump and bounce")
If you go down this path, I would recommend finding somewhere that you can experiment, tweak, and play with the key times and values of the key-frame animation. Perhaps a playground or a small custom app with sliders where you can get fast iterations.

you can use a CABasicAnimation with byValue of 1 and set autoreverses to YES. Finally set its timingFunction to kCAMediaTimingFunctionEaseIn (or, more specifically, a timing function initialized with this curve name).

Related

Animate several properties of several views with Core Animation

I want to animate several properties of several subviews using Core Animations in Swift. I'm looking for a way to group the animations together and handle them as one, mainly to be able to synchronise them altogether and pause and resume all of them together, but also in order to avoid code duplication and make the code cleaner.
I tried to use other kind of animations, such as UIView.animate or UIViewPropertyAnimator, but neither of them fit my needs because they don't allow the customization needed for my case.
For now, I'm using CATransaction to ensure that the animation changes are committed to Core Animation at the same time:
CATransaction.begin()
CATransaction.setAnimationDuration(1.5)
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(controlPoints: 0.85, 0, 0.15, 1))
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue = 0
opacityAnimation.toValue = 1.0
opacityAnimation.repeatCount = .infinity
opacityAnimation.autoreverses = true
self.lable.layer.add(beforeOpacityAnimation, forKey: "opacityAninmation")
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = 1.5
scaleAnimation.toValue = 1.0
scaleAnimation.repeatCount = .infinity
scaleAnimation.autoreverses = true
self.imageView.layer.add(targetImageScaleAnimation, forKey: "scaleAnimator")
CATransaction.commit()
So far so good. But now I want to be able to pause and resume all animations together. From what I see here, in order to achieve this one would need to iterate over all animated layers (e.g. self.lable.layer, self.imageView.layer) to modify their speed, timeOffset and beginTime properties (this is kinda dirty and nonatomic).
Is there a way to group these animations or refer to them as one in order to pause and resume them altogether?
If not, should I at least put these iterations over layers inside a CATransaction as well?

Animation to Remove Sprite

I am currently making a Sprite and I want it to animate before it disappears.
For example: I want it to animate it in the sense that it disappears from the top of the block until the bottom. To put it another way, I want to the size to decrease slowly until there is nothing left. But I want to give it the appearance that it is disappearing rather than scaling to nothing.
let hand = SKSpriteNode(imageNamed: "hand")
hand.size = CGSize(width: size.width/10, height: size.height/30)
hand.position = CGPoint(x: CGFloat(posX-2)*size.width/10+offsetX, y:CGFloat(posY)*size.height/30+offsetY)
addChild(hand)
tl;dr is it possible to make this sort of effect using SpriteKit in Swift.
Ideal Animation: https://ibb.co/sPsffmK
SKAction is an animation that is executed by a node in the scene. Actions are used to change a node in some way (like move its position over time), but you can also use actions to change the scene, like doing a fadeout.
In your case, you can apply multiple animations:
let move_action = SKAction.moveBy(x: -100, y: 0, duration: 1.0)
let scale_action = SKAction.scale(to: 0.0, duration: 1.0)
let fade_action = SKAction.fadeAlpha(to: 0.0, duration: 1.0)
hand.run(move_action)
hand.run(scale_action)
//hand.run(fade_action)
In the previous example, the hand runs the move and scale animation at the same time. But you can also make the hand to move to the position, and after it reaches, to scale it.
let sequence = SKAction.sequence([move_animation,scale_animation])
hand.run(sequence)
There are lots of animations that SKAction has, here you can find the complete list.

Animate a UIView along a part of a bezier path

I'm trying animate a UIView along a portion of a bezier path. I found a way to move the the view to any part of the path using this code:
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = trackPath.cgPath
animation.rotationMode = kCAAnimationRotateAuto
animation.speed = 0
animation.timeOffset = offset
animation.duration = 1
animation.calculationMode = kCAAnimationPaced
square.layer.add(animation, forKey: "animate position along path")
However, this just moves the view to the desired point and doesn't animate it. How do you animate a view along over a portion of a bezier path?
Thanks
You can accomplish this by modifying the timing of the "complete" animation and a animation group that wraps it.
To illustrate how the timing of such an animation works, imagine – instead of animating a position along a path – that a color is being animated. Then, the complete animation from one color to another can be illustrated as this, where a value further to the right is at a later time — so that the very left is the start value and the very right is the end value:
Note that the "timing" of this animation is linear. This is intentional since the "timing" of the end result will be configured later.
In this animation, imagine that we're looking to animate only the middle third, this part of the animation:
There are a few steps to animating only this part of the animation.
First, configure the "complete" animation to have linear timing (or in the case of an animation along a path; to have a paced calculation mode) and to have a the "complete" duration.
For example: if you're looking to animate a third of the animation an have that take 1 second, configure the complete animation to take 3 seconds.
let relativeStart = 1.0/3.0
let relativeEnd = 2.0/3.0
let duration = 1.0
let innerDuration = duration / (relativeEnd - relativeStart) // 3 seconds
// configure the "complete" animation
animation.duration = innerDuration
This means that the animation currently is illustrated like this (the full animation):
Next, so that the animation "starts" a third of the way into the full animation, we "offset" its time by a third of the duration:
animation.timeOffset = relativeStart * innerDuration
Now the animation is illustrated like this, offset and wrapping from its end value to its start value:
Next, so that we only display part of this animation, we create an animation group with the wanted duration and add only the "complete" animation to it.
let group = CAAnimationGroup()
group.animations = [animation]
group.duration = duration
Even though this group contains an animation that is 3 seconds long it will end after just 1 second, meaning that the 2 seconds of the offset "complete" animation will never be shown.
Now the group animation is illustrated like this, ending after a third of the "complete" animation:
If you look closely you'll see that this (the part that isn't faded out) is the middle third of the "complete" animation.
Now that this group animates animates between the wanted values, it (the group) can be configured further and then added to a layer. For example, if you wanted this "partial" animation to reverse, repeat, and have a timing curve you would configure these things on the group:
group.repeatCount = HUGE
group.autoreverses = true
group.timingFunction = CAMediaTimingFunction(name: "easeInEaseOut")
With this additional configuration, the animation group would be illustrated like this:
As a more concrete example, this is an animation that I created using this technique (in fact all the code is from that example) that moves a layer back and forth like a pendulum. In this case the "complete" animation was a "position" animation along a path that was a full circle
I've done a similar animation just a few days ago:
// I want the animation to go along the circular path of the oval shape
let flightAnimation = CAKeyframeAnimation(keyPath: "position")
flightAnimation.path = ovalShapeLayer.path
// I set this one to make the animation go smoothly along the path
flightAnimation.calculationMode = kCAAnimationPaced
flightAnimation.duration = 1.5
flightAnimation.rotationMode = kCAAnimationRotateAuto
airplaneLayer.add(flightAnimation, forKey: nil)
I see that you set speed to zero and a time offset. Why do you need them?
I would suggest to try the animation using just the parameters in the above code and then try to tune it from them.

Can UISpringTimingParameters have an inverse spring effect?

After dabbling with UIViewPropertyAnimator, I've found that .isReversed simply plays the animation backwards like a movie in reverse. Is it possible to apply "springiness" to the reverse animation through a custom Cubic Timing Parameter that only applies to the animation if it's reversed?
When you want to reverse the animation and go back with spring effect, you can simply use the following code snippet:
// prepare new timing parameters with the desired springiness
let newTimingParameters
= UISpringTimingParameters(dampingRatio: 0.5, initialVelocity: CGVector(dx: 2, dy: 0))
// pause the animator
propertyAnimator.pauseAnimation()
// reverse it
propertyAnimator.isReversed = true
// and restart it using new timing parameters with springiness
propertyAnimator.continueAnimation(withTimingParameters: newTimingParameters, durationFactor: propertyAnimator.fractionComplete)

Animate View with multiple arguments

I am trying to animate the opacity of a CALayer but the values are more than one value to begin with and one to end with.
For example: I want it to animate throw these values: 0.0, 0.7, 0.3, 1.0, 0.5, 0.0
I also want the animation to repeat with auto reverse.
This is what I have for now:
let redLineAnimation = CABasicAnimation(keyPath: "opacity")
redLineAnimation.duration = 0.4
redLineAnimation.autoreverses = true
redLineAnimation.fromValue = 0.0
redLineAnimation.toValue = 1.0
redLineAnimation.repeatCount = Float.infinity
movingRedLineLayer.addAnimation(redLineAnimation, forKey: nil)
I am new to iOS development. What can I do? Thanks!
Take a look at CAKeyframeAnimation. That will give you what you want.
And if what you are animating is a view's layer, you could use the easier-to-use UIView keyframe based animation methods. (Core Animation is pretty tricky, and the docs are spotty)

Resources