CABasicAnimation on UIView, Child button is not clickable - ios

I have view which is rotating like this :
if view.layer.animation(forKey: kRotationAnimationKey) == nil {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = Float(M_PI * 2.0)
rotationAnimation.duration = duration
rotationAnimation.repeatCount = Float.infinity
view.layer.add(rotationAnimation, forKey: kRotationAnimationKey)
}
there are multiple small button on this view.
While parent view is rotating and trying to tap on buttons , i am unable to tap and corresponding action method is not getting called.
Please help on this issue.

When you animate a view or layer, the actual view does not move through the intermediate locations of the animation. Instead, the view object jumps to it's final location at the beginning of the animation. (The animation actually takes place on a "presentation layer" which is laid on top of the view's normal layer hierarchy.)
If you want to be able to tap on a button that's "in flight" in an animation you will have to do hit testing on the view's presentation layer, and you can't have a button respond to touches using target/action.
I have a sample project on Github that demonstrates detecting taps on an in-flight view if you're interested. Take a look at https://github.com/DuncanMC/iOS-CAAnimation-group-demo on Github. It's written in Objective-C, but the idea is the same

Related

Rotation animation with spring effect

I want to make rotation of CAShapeLayer with spring effect (like in UIView.animateWithDuration(_:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:)) but on layer not on a view.
When the button is tapped its sublayer of main layer should rotate to 3*PI/4 and spring should bounce to 2*PI/3. Then, when button is tapped again, layer rotation should be done in reversed order than before: first bounce to 2*PI/3, then rotation to the initial position (before first rotation).
How I could do that? I cannot achieve it by UIView.animateWithDuration(_:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:) because layer's transform property is animatable by default.
I've tried changing CATransaction but it rotates only by one angle (without taking into consideration other rotation):
let rotation1 = CGAffineTransformRotate(CGAffineTransformIdentity, angle1)
let rotation2 = CGAffineTransformRotate(CGAffineTransformIdentity, angle2)
let transform = CGAffineTransformConcat(rotation1, rotation2)
CATransaction.begin()
CATransaction.setAnimationDuration(0.6)
self.plusLayer.setAffineTransform(transform)
CATransaction.commit()
Update
According to Duncan C post I try to use CASpringAnimation and I achive animation in one direction:
myLayer.setAffineTransform(CGAffineTransformMakeRotation(angle))
let spring = CASpringAnimation(keyPath: "transform.rotation")
spring.damping = 12.0
spring.fromValue = 2.0 * CGFloat(M_PI)
spring.toValue = 3.0 * CGFloat(M_PI_4)
spring.duration = 0.5
myLayer.addAnimation(spring, forKey: "rotation")
But how to reverse that animation on button tapped?
Thanks in advance for your help.
UIView block animation is for animating view properties. You could animate the button's transform (of type CGAffineTransform, a 2D transform) using UIView.animateWithDuration(_:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:
If you need to animate layer properties, though, you'll need to use Core Animation.
It seems Apple added (or made public) a spring CAAnimation in iOS 9. It doesn't seem to be in the Xcode docs however.
Check out this thread:
SpringWithDamping for CALayer animations?

How to move a buttons target/gesture recogniser when animating the button in swift (iOS)? [duplicate]

This question already has answers here:
UIButton not interacting during animation
(5 answers)
Closed 6 years ago.
Im animating some buttons in swift to move randomly across the view forever, however the buttons 'target' does not move. To click the button, you have to click the original position that it is created at, even though it might be the other side of the screen.
Does anyone have simple instructions for how to move the buttons target with the button?
func circlePath(view: UIButton, pathDuration: Double){
//create an animation to follow a circular path
let pathAnimation: CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "position")
//interpolate the movement to be more smooth
pathAnimation.calculationMode = kCAAnimationPaced
pathAnimation.repeatCount = .infinity
//no ease in/out to have the same speed along the path
pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
//the paths circular animation is proportional its size
pathAnimation.duration = pathDuration
//The circle to follow will be inside the circleContainer frame.
//it should be a frame around the center of your view to animate.
//do not make it to large, a width/height of 3-4 will be enough.
let curvedPath: CGMutablePathRef = CGPathCreateMutable()
let circleContainer: CGRect = CGRectInset(view.frame, 23, 23)
CGPathAddEllipseInRect(curvedPath, nil, circleContainer)
//add the path to the animation
pathAnimation.path = curvedPath
//add animation to the view's layer
view.layer.addAnimation(pathAnimation, forKey: "myCircleAnimation")
}
Don't use the term "target" to mean the area that taps respond to. "Target" has a specific meaning with regard to buttons, and it's not that.
Let's call it the "hit rectangle".
Core Animation does not actually animate the objects across the screen. It creates a "presentation layer" that gives the appearance of the objects moving, but the objects themselves do not animate.
Thus you can't have buttons respond to taps while "mid-flight" in an animation.
Instead, what you have to do is to put a tap gesture recognizer on the superview (or the layer that contains the layer you're animating, if you're doing CAAnimation.) Then you can use the hitTest method of the animated layer's presentation layer to test if the tap was inside the bounds of the layer that's being animated.
I have a project on github that show this technique. It's called iOS-CAAnimation-group-demo (link.) It's written in Objective-C, but the technique is the same regardless of language so it should give you an idea of how to proceed.

CABasicAnimation change startingpoint to go forward and backwards

I'm trying to make a small ui controlled animation (as described in this answer) and continue this animation after rotation. Due to autolayout issues I have to remove and add the whole layer on autorotate.
This works so far. My problem is, as I want continue the animation from the same position it was left off, it won't go anywhere prior to the state it was the moment it rotated.
e.g. the slider is at 0.5. The animation was added again (due to removal) and I've set the timeOffset corresponding to 0.5. The animation will go forward, but not backward.
I create my animation via:
let animatePhase = CABasicAnimation(keyPath: "lineDashPhase")
animatePhase.byValue = phaseLength
animatePhase.duration = 1.0
animatePhase.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animatePhase.repeatCount = Float.infinity
lineLayer.addAnimation(animatePhase, forKey: "marching ants")
lineLayer.speed = 0.0
lineLayer.timeOffset = 0.0;
lineLayer.beginTime = 0.0;
lineLayer is a CAShapeLayer.
On `layoutSubviews' I remove and recreate the layer.
What am I doing wrong?
Thanks

iOS, Swift: animation controlled by gesture

I trying to find a way for connecting user interaction (pan gesture) with animation. I want to control animation progress by dragging view (or just by finger panning on the screen).
I think I can make this by using Core Animation, but I didn't find any similar to my needs example.
I have a CGPath, that I want to draw.
I know that there are strokeStart and strokeEnd property that is animatable and perfectly acceptable for my needs in the "drawing" point of view.
Using layer with given path property I can animate this path with CABasicAnimation
layer.path = myPath
//... layer configuration
let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
pathAnimation.duration = 2
pathAnimation.fromValue = 0
pathAnimation.toValue = 1
layer.addAnimation(pathAnimation, forKey: "strokeEndAnimation")
Here we can see animation from the first path point to the last consequentially.
Okay, but I need to somehow control this animation with gesture.
Let's say, if we pan finger on the screen to the right, our path animation draws straight ahead (e.g strokeStart = 0 and strokeEnd = 1) and if we pan from right to left we will see backward animation (like rewind) (e.g. strokeStart = 1 and strokeEnd = 0).
I know, how to get gesture direction, translationInView, velocityInView and I want to animate this in changed state, not end. (UIGestureRecognizerState.Changed)
But I don't know how I can combine control from gesture to animation because of this troubles:
duration of animation. (I don't know how fast or how slow gesture would be)
how I need to implement animation because calls are very frequent in changed state
I looked to
animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:
but duration question rises again.
Hope somebody can help me, ask me if you misunderstood something and I'll try to clarify.
Let's say, if we pan finger on the screen to the right, our path animation draws straight ahead (e.g strokeStart = 0 and strokeEnd = 1) and if we pan from right to left we will see backward animation (like rewind) (e.g. strokeStart = 1 and strokeEnd = 0).
If I understand you correctly, the solution is to freeze the animation by setting the layer's speed to 0 and then setting the "frame" of the animation by setting the layer's timeOffset.

Continue animation from where it was interupted - Core animation

I need a a little help with my animation.
I got two buttons, the first button calls an animation and the second one calls another animation.
Now when I press the first button a code like this one down below will be called.
The Problem is that when I press the second button the animation kinda teleports to it's starting position (second animations position), is there a way to make the animations flow together from where it was interupted?
CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
anim1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim1.fromValue = [NSNumber numberWithFloat:((-10*M_PI)/180)];
anim1.toValue = [NSNumber numberWithFloat:((10*M_PI)/180)];
anim1.repeatCount = HUGE_VALF;
anim1.autoreverses = YES;
anim1.duration = .5;
[head addAnimation:anim1 forKey:#"transform"];
Virtual picture:
http://i47.tinypic.com/2yopif6.jpg
The black line is the first animation, the red is the second animation, and the green is the one I'm trying to figure out. According to the picture it was interupted in the middle of the black.
Why not using the animation method instead of the UIView class - just make sure you use the UIViewAnimationOptionBeginFromCurrentState option?
Check the reference doc here: http://developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/uiview/uiview.html
EDIT
I think this tutorial will help you better: http://wangling.me/2011/06/core-animation-101-from-and-to/. What they are trying to achieve there is to bounce a ball after triggering the animation through a button. When the button is pressed a second time the ball should bounce back from the current position. There is a section where they mention they rely on 2 delegates method animationDidStart and animationDidStop to implement this desired effect.

Resources