Stop old animation and start new one, once I change segments, swift - swift

Is there a method or function that can stop previous animation, when I switch segment controls?
If I don't remove animation, image that was fading on first segment, starts rotating on the second and then moves on the third segment.
While I switch segments, it works more like appending different animations all the time.
I tried:
- removeAllAnimations(), but it doesn't work properly.
- self.view.layer.removeAnimation(forKey: "moveAnimation"). Tried that one at the end of the code for each segment choice.
Maybe I am not putting it in the right spot, but I can't get it working properly
Here is my code:
import UIKit
class MoveViewController: UIViewController {
#IBOutlet var sgAction : UISegmentedControl!
var moveLayer : CALayer?
#IBAction func segmentDidChange(sender : UISegmentedControl){
updateAction()
}
func updateAction(){
let action = sgAction.selectedSegmentIndex
if action == 0 {
//fade
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
self.view.layer.removeAnimation(forKey: "fadeAnimation")
fadeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
fadeAnimation.fromValue = NSNumber.init(value: 1.0)
fadeAnimation.toValue = NSNumber.init(value: 0.0)
fadeAnimation.isRemovedOnCompletion = false
fadeAnimation.duration = 3.0
fadeAnimation.beginTime = 1.0
fadeAnimation.isAdditive = false
fadeAnimation.fillMode = CAMediaTimingFillMode.both
fadeAnimation.repeatCount = Float.infinity
moveLayer?.add(fadeAnimation, forKey: nil)
} else if action == 1 {
//2. rotate
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
self.view.layer.removeAnimation(forKey: "rotateAnimation")
rotateAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
rotateAnimation.fromValue = 0
rotateAnimation.toValue = 2 * Double.pi
rotateAnimation.isRemovedOnCompletion = false
rotateAnimation.duration = 1.0
rotateAnimation.repeatCount = Float.infinity
moveLayer?.add(rotateAnimation, forKey:nil)
} else {
//3. move
let moveAnimation = CABasicAnimation(keyPath: "position")
self.view.layer.removeAnimation(forKey: "moveAnimation")
moveAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
moveAnimation.fromValue = NSValue.init(cgPoint: CGPoint(x: 0, y: 0))
moveAnimation.toValue = NSValue.init(cgPoint: CGPoint(x: 700, y: 500))
moveAnimation.isRemovedOnCompletion = false
moveAnimation.duration = 3.0
moveAnimation.repeatCount = Float.infinity
moveLayer?.add(moveAnimation, forKey: nil)
}
}
// moveLayer?.removeAllAnimations()
func createImg(){
let displayImage = UIImage(named: "balloon.png")
moveLayer = CALayer.init()
moveLayer?.contents = displayImage?.cgImage
moveLayer?.bounds = CGRect(x: 0, y: 0, width: 150, height: 150)
moveLayer?.position = CGPoint(x: 300, y: 200)
self.view.layer.addSublayer(moveLayer!)
}
override func viewDidLoad() {
super.viewDidLoad()
createImg()
// moveLayer?.removeAllAnimations()
updateAction()
}
}

When you add moveLayer?.removeAllAnimations() and self.moveLayer.layoutIfNeed() to the segmentDidChange(sender : UISegmentedControl). Also the other key item that needs to get changed is moveLayer?.add(fadeAnimation, forKey: "nil") to moveLayer?.add(fadeAnimation, forKey: "fadeAnimation").
import UIKit
class MoveViewController: UIViewController {
#IBOutlet var sgAction : UISegmentedControl!
var moveLayer : CALayer?
override func viewDidLoad() {
super.viewDidLoad()
createImg()
updateAction()
}
#IBAction func segmentDidChange(sender : UISegmentedControl){
moveLayer?.removeAllAnimations()
self.moveLayer?.layoutIfNeeded()
updateAction()
}
func updateAction(){
switch sgAction.selectedSegmentIndex {
case 0:
//fade
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
fadeAnimation.fromValue = NSNumber.init(value: 1.0)
fadeAnimation.toValue = NSNumber.init(value: 0.0)
fadeAnimation.isRemovedOnCompletion = false
fadeAnimation.duration = 3.0
fadeAnimation.beginTime = 1.0
fadeAnimation.isAdditive = false
fadeAnimation.fillMode = CAMediaTimingFillMode.both
fadeAnimation.repeatCount = Float.infinity
moveLayer?.add(fadeAnimation, forKey: "fadeAnimation")
case 1:
//2. rotate
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
rotateAnimation.fromValue = 0
rotateAnimation.toValue = 2 * Double.pi
rotateAnimation.isRemovedOnCompletion = false
rotateAnimation.duration = 1.0
rotateAnimation.repeatCount = Float.infinity
moveLayer?.add(rotateAnimation, forKey: "rotateAnimation")
case 2:
//3. move
let moveAnimation = CABasicAnimation(keyPath: "position")
moveAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
moveAnimation.fromValue = NSValue.init(cgPoint: CGPoint(x: 0, y: 0))
moveAnimation.toValue = NSValue.init(cgPoint: CGPoint(x: 700, y: 500))
moveAnimation.isRemovedOnCompletion = false
moveAnimation.duration = 3.0
moveAnimation.repeatCount = Float.infinity
moveLayer?.add(moveAnimation, forKey: "position")
default: break
}
}
func createImg(){
let displayImage = UIImage(named: "balloon.png")
moveLayer = CALayer.init()
moveLayer?.contents = displayImage?.cgImage
moveLayer?.bounds = CGRect(x: 0, y: 0, width: 150, height: 150)
moveLayer?.position = CGPoint(x: 300, y: 200)
self.view.layer.addSublayer(moveLayer!)
}
}

Related

Swift pulse animation non circle

I'm trying to create pulse animation rectangular shape
I have code:
func createPulse() {
for _ in 0...3 {
let circularPath = UIBezierPath(arcCenter: .zero, radius: view.bounds.size.height/2, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
let pulseLayer = CAShapeLayer()
pulseLayer.path = circularPath.cgPath
pulseLayer.lineWidth = 2
pulseLayer.fillColor = UIColor.clear.cgColor
pulseLayer.lineCap = CAShapeLayerLineCap.round
pulseLayer.position = CGPoint(x: binauralBackView.frame.size.width/2, y: binauralBackView.frame.size.height/2)
pulseLayer.cornerRadius = binauralBackView.frame.width/2
binauralBackView.layer.insertSublayer(pulseLayer, at: 0)
pulseLayers.append(pulseLayer)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.animatePulse(index: 0)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.animatePulse(index: 1)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.animatePulse(index: 2)
}
}
}
}
func animatePulse(index: Int) {
pulseLayers[index].strokeColor = #colorLiteral(red: 0.7215686275, green: 0.5137254902, blue: 0.7647058824, alpha: 1).cgColor
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.duration = 2
scaleAnimation.fromValue = 0.0
scaleAnimation.toValue = 0.9
scaleAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
scaleAnimation.repeatCount = .greatestFiniteMagnitude
pulseLayers[index].add(scaleAnimation, forKey: "scale")
let opacityAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
opacityAnimation.duration = 2
opacityAnimation.fromValue = 0.9
opacityAnimation.toValue = 0.0
opacityAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
opacityAnimation.repeatCount = .greatestFiniteMagnitude
pulseLayers[index].add(opacityAnimation, forKey: "opacity")
}
and have result
circle result
I need to create rectangular like this (like border of image):
rectangular
How can change circle to rectangular?
Output:
Note: You can add CABasicAnimation for opacity too in animationGroup if this solution works for you.
extension UIView {
/// animate the border width
func animateBorderWidth(toValue: CGFloat, duration: Double) -> CABasicAnimation {
let widthAnimation = CABasicAnimation(keyPath: "borderWidth")
widthAnimation.fromValue = layer.borderWidth
widthAnimation.toValue = toValue
widthAnimation.duration = duration
return widthAnimation
}
/// animate the scale
func animateScale(toValue: CGFloat, duration: Double) -> CABasicAnimation {
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = 1.0
scaleAnimation.toValue = toValue
scaleAnimation.duration = duration
scaleAnimation.repeatCount = .infinity
scaleAnimation.isRemovedOnCompletion = false
scaleAnimation.timingFunction = CAMediaTimingFunction(name: .easeOut)
return scaleAnimation
}
func pulsate(animationDuration: CGFloat) {
var animationGroup = CAAnimationGroup()
animationGroup = CAAnimationGroup()
animationGroup.duration = animationDuration
animationGroup.repeatCount = Float.infinity
let newLayer = CALayer()
newLayer.bounds = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
newLayer.cornerRadius = self.frame.width/2
newLayer.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
newLayer.cornerRadius = self.frame.width/2
animationGroup.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
animationGroup.animations = [animateScale(toValue: 6.0, duration: 2.0),
animateBorderWidth(toValue: 1.2, duration: animationDuration)]
newLayer.add(animationGroup, forKey: "pulse")
self.layer.cornerRadius = self.frame.width/2
self.layer.insertSublayer(newLayer, at: 0)
}
}
Usage:
class ViewController: UIViewController {
#IBOutlet weak var pulseView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
pulseView.pulsate(animationDuration: 1.0)
}
}

How do I set an endpoint for a CAKeyFrameAnimation?

I am using an arc for a progress bar.
There is a track layer that the progress indicator moves along and I also have a circle that moves with the progress bar for added emphasis. Both the progress bar and the circle move correctly along the path but when the progress is complete, instead of stopping at the end of the path, the circle jumps back to the beginning of the progress path.
screen recording of behavior
The code:
class ViewController: UIViewController {
let shapeLayer = CAShapeLayer()
let trackLayer = CAShapeLayer()
let dotLayer: CALayer = CALayer()
let seconds = 10.0
// MARK: - Properties
private var progressBarView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
title = "Making Progress"
view.backgroundColor = .systemTeal
// determine the value of Center
let center = view.center
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: 2.7, endAngle: 0.45, clockwise: true) // these values give us a complete circle
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.lineWidth = 10
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineCap = .round
view.layer.addSublayer(trackLayer)
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 10
shapeLayer.strokeEnd = 0
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = .round
view.layer.addSublayer(shapeLayer)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap )))
}
#objc private func handleTap() {
print("Attempting to animate stroke")
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = 1
basicAnimation.duration = seconds
basicAnimation.fillMode = CAMediaTimingFillMode.forwards
basicAnimation.isRemovedOnCompletion = false
shapeLayer.add(basicAnimation, forKey: "anyKeyWillDo")
// circle image
let circleNobImage = UIImage(named: "whiteCircle.png")!
dotLayer.contents = circleNobImage.cgImage
dotLayer.bounds = CGRect(x: 0.0, y: 0.0, width: circleNobImage.size.width, height: circleNobImage.size.height)
view.layer.addSublayer(dotLayer)
dotLayer.position = CGPoint(x: 105, y: 460)
dotLayer.opacity = 1
dotAnimation()
}
func dotAnimation() {
let dotAnimation = CAKeyframeAnimation(keyPath: "position")
dotAnimation.path = trackLayer.path
dotAnimation.calculationMode = .paced
let dotAnimationGroup = CAAnimationGroup()
dotAnimationGroup.duration = seconds
dotAnimation.autoreverses = false
dotAnimationGroup.animations = [dotAnimation]
dotLayer.add(dotAnimationGroup, forKey: nil)
}
}
I need to get the circle to stop at the end of the arc with the progress bar. How is this done?
Also set dotAnimationGroup.fillMode and dotAnimationGroup.isRemovedOnCompletion
dotAnimationGroup.fillMode = .forwards
dotAnimationGroup.isRemovedOnCompletion = false

For Loop Overriding Animations

I'm trying to create a new animated line every second on the screen. Each second I do get a new line, however it overrides the old one. I don't know why but it's probably something idiotic I'm overlooking. Here's my code:
func repeatThis() {
for x in 1...10 {
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), Int64(x) * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
var topLinePatha: UIBezierPath {
return UIBezierPath(rect: CGRect(x: 0, y: 0 + (x * 10), width: 1, height: 10))
}
var topLinePathb: UIBezierPath {
return UIBezierPath(rect: CGRect(x: 0, y: 0 + (x * 10), width: Int(UIScreen.mainScreen().bounds.width), height: 10))
}
let expAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
expAnimation.fromValue = topLinePatha.CGPath
expAnimation.toValue = topLinePathb.CGPath
expAnimation.duration = self.animationTime
expAnimation.fillMode = kCAFillModeForwards
expAnimation.removedOnCompletion = false
self.addAnimation(expAnimation, forKey: nil)
print(x)
}
}
}
Thanks for the help
Edit 1:
Here's an issue I'm having with the animation timing where basically the animations override each other:
func repeatThis() {
var runningPath = UIBezierPath()
for x in 0...10 {
delay(Double(x) / 10) {
let topLineStartPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: 1, height: 10))
let topLineEndPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: Int(self.bounds.width), height: 10))
let fullStartPath = runningPath.copy() as! UIBezierPath
fullStartPath.appendPath(topLineStartPath)
let fullEndPath = runningPath.copy() as! UIBezierPath
fullEndPath.appendPath(topLineEndPath)
let expAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
expAnimation.fromValue = fullStartPath.CGPath
expAnimation.toValue = fullEndPath.CGPath
expAnimation.duration = self.animationTime
expAnimation.fillMode = kCAFillModeForwards
expAnimation.removedOnCompletion = false
self.addAnimation(expAnimation, forKey: nil)
print(x)
runningPath = fullEndPath
}
}
}
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
Your code is replacing the CAShapeLayer's path each time you do an animation, so for each "line" that you animate in, you lose the past lines.
To show multiple lines, you could either:
Add multiple subpaths to the CAShapeLayer's path, one for each line, using the method UIBezierPath.appendPath.
Use multiple CAShapeLayers, one for each line.
Here's alternative #1, which is a smaller change from your current code. This is a self-contained example that you can add into a new iOS project in a view controller called ViewController.
import UIKit
class MyShapeLayer: CAShapeLayer {
var animationTime: NSTimeInterval = 0.75
func repeatThis() {
// Keep track of the path so far
var runningPath = UIBezierPath()
for x in 1...10 {
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), Int64(x) * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
// We will add a rectangular subpath onto runningPath.
// It will be animated starting with:
let topLineStartPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: 1, height: 10))
// and ending with:
let topLineEndPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: Int(self.bounds.width), height: 10))
// Copy the running path, and add the starting and ending subpaths onto it
let fullStartPath = runningPath.copy() as! UIBezierPath
fullStartPath.appendPath(topLineStartPath)
let fullEndPath = runningPath.copy() as! UIBezierPath
fullEndPath.appendPath(topLineEndPath)
// Animate from fullStartPath to fullEndPath
let expAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
expAnimation.fromValue = fullStartPath.CGPath
expAnimation.toValue = fullEndPath.CGPath
expAnimation.duration = self.animationTime
expAnimation.fillMode = kCAFillModeForwards
expAnimation.removedOnCompletion = false
self.addAnimation(expAnimation, forKey: nil)
print(x)
// The next time through the loop, add on to this iteration's ending path
runningPath = fullEndPath
}
}
}
}
class MyView: UIView {
override class func layerClass() -> AnyClass {
return MyShapeLayer.self
}
}
class ViewController: UIViewController {
override func loadView() {
self.view = MyView()
self.view.backgroundColor = UIColor.whiteColor()
}
override func viewDidLoad() {
if let myShapeLayer = self.view.layer as? MyShapeLayer {
myShapeLayer.repeatThis()
}
}
}
And the result:
Here's a way to do alternative #2. I made the animationTime longer so you can see that the animations for each line can overlap.
class LineAtATimeView: UIView {
var animationTime = 1.25 // longer duration so line animations overlap
func repeatAddingLines() {
for x in 1...10 {
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), Int64(x) * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
let newLayer = CAShapeLayer()
newLayer.frame = self.bounds
self.layer.addSublayer(newLayer)
let topLineStartPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: 1, height: 10))
let topLineEndPath = UIBezierPath(rect: CGRect(x: 0, y: x * 10, width: Int(self.bounds.width), height: 10))
let expAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
expAnimation.fromValue = topLineStartPath.CGPath
expAnimation.toValue = topLineEndPath.CGPath
expAnimation.duration = self.animationTime
expAnimation.fillMode = kCAFillModeForwards
expAnimation.removedOnCompletion = false
newLayer.addAnimation(expAnimation, forKey: nil)
}
}
}
}
class ViewController2: UIViewController {
override func loadView() {
self.view = LineAtATimeView()
self.view.backgroundColor = UIColor.whiteColor()
}
override func viewDidLoad() {
if let v = self.view as? LineAtATimeView {
v.repeatAddingLines()
}
}
}

Swift: How to draw a circle step by step?

I build a project to draw an arc initially, this arc is 1/8 of a circle. Then i put a button on the viewController, whenever I click this button, it will draw another 1/8 of a circle seamless on it .
But I got a problem: when i click the button, it almost draws the arc rapidly(0.25s), not the duration i set before(1s). How to reach that no matter when I click the button, it consumes the same time as the duration i set before?
import UIKit
let π = CGFloat(M_PI)
class ViewController: UIViewController {
var i: CGFloat = 1
var maxStep: CGFloat = 8
var circleLayer: CAShapeLayer?
override func viewDidLoad() {
super.viewDidLoad()
let startAngle = CGFloat(0)
let endAngle = 2*π
let ovalRect = CGRectMake(100, 100, 100, 100)
let ovalPath = UIBezierPath(arcCenter: CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect)), radius: CGRectGetWidth(ovalRect), startAngle: startAngle, endAngle: endAngle, clockwise: true)
let circleLayer = CAShapeLayer()
circleLayer.path = ovalPath.CGPath
circleLayer.strokeColor = UIColor.blueColor().CGColor
circleLayer.fillColor = UIColor.clearColor().CGColor
circleLayer.lineWidth = 10.0
circleLayer.lineCap = kCALineCapRound
self.circleLayer = circleLayer
self.view.layer.addSublayer(self.circleLayer)
let anim = CABasicAnimation(keyPath: "strokeEnd")
// here i set the duration
anim.duration = 1.0
anim.fromValue = 0.0
anim.toValue = self.i/self.maxStep
self.circleLayer!.strokeStart = 0.0
self.circleLayer!.strokeEnd = self.i/self.maxStep
self.circleLayer!.addAnimation(anim, forKey: "arc animation")
self.i++
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// when this action be triggered, it almost draws the arc rapidly, why?
#IBAction func buttonAnimate(sender: UIButton) {
if self.i<=self.maxStep {
self.circleLayer!.strokeStart = 0.0
self.circleLayer!.strokeEnd = self.i/self.maxStep
self.i++
}
}
}
Get your animation and reset properties. See code below:
#IBAction func buttonAnimate(sender: UIButton) {
if self.i<=self.maxStep {
self.circleLayer!.strokeStart = 0.0
self.circleLayer!.strokeEnd = self.i/self.maxStep
self.circleLayer?.animationKeys()
let anim = self.circleLayer?.animationForKey("arc animation") as? CABasicAnimation
if anim == nil {
let anim = CABasicAnimation(keyPath: "strokeEnd")
// here i set the duration
anim.duration = 1
anim.fromValue = (self.i - 1)/self.maxStep
anim.toValue = self.i/self.maxStep
self.circleLayer!.addAnimation(anim, forKey: "arc animation")
}
self.i++
}
}
Hope this helps!
Here is the correct solution:
import UIKit
import UIKit
let π = CGFloat(M_PI)
class ViewController: UIViewController {
var i: CGFloat = 1
var maxStep: CGFloat = 8
var prevValue : CGFloat = 0.0
var circleLayer: CAShapeLayer?
override func viewDidLoad() {
super.viewDidLoad()
let startAngle = CGFloat(0)
let endAngle = 2*π
let ovalRect = CGRectMake(100, 100, 100, 100)
let ovalPath = UIBezierPath(arcCenter: CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect)), radius: CGRectGetWidth(ovalRect), startAngle: startAngle, endAngle: endAngle, clockwise: true)
let circleLayer = CAShapeLayer()
circleLayer.path = ovalPath.CGPath
circleLayer.strokeColor = UIColor.blueColor().CGColor
circleLayer.fillColor = nil //UIColor.clearColor().CGColor
circleLayer.lineWidth = 10.0
circleLayer.lineCap = kCALineCapRound
self.circleLayer = circleLayer
self.view.layer.addSublayer(self.circleLayer!)
self.circleLayer!.strokeStart = 0.0
//Initial stroke-
setStrokeEndForLayer(self.circleLayer!, from: 0.0, to: self.i / self.maxStep, animated: true)
self.i++
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonAnimate(sender: UIButton) {
if self.i <= self.maxStep {
setStrokeEndForLayer(self.circleLayer!, from: (self.i - 1 ) / self.maxStep, to: self.i / self.maxStep, animated: true)
self.i++
}
}
func setStrokeEndForLayer(layer: CALayer, var from:CGFloat, to: CGFloat, animated: Bool)
{
self.circleLayer!.strokeEnd = to
if animated
{
//Check if there is any existing animation is in progress, if so override, the from value
if let circlePresentationLayer = self.circleLayer!.presentationLayer()
{
from = circlePresentationLayer.strokeEnd
}
//Remove any on going animation
if (self.circleLayer?.animationForKey("arc animation") as? CABasicAnimation != nil)
{
//Remove the current animation
self.circleLayer!.removeAnimationForKey("arc animation")
}
let anim = CABasicAnimation(keyPath: "strokeEnd")
// here i set the duration
anim.duration = 1
anim.fromValue = from
anim.toValue = to
self.circleLayer!.addAnimation(anim, forKey: "arc animation")
}
}
}

Swift NSValue.valueWithCGRect

I'm trying to animate the bounds of a UIButton using Facebook pop POPSpringAnimation, but I can't find what I should use in swift in replace of NSValue.valueWithCGRect
This is what I'm trying to do:
#IBAction func buttonTapped(sender : UIButton) {
var springAnimation = POPSpringAnimation()
springAnimation.property = POPAnimatableProperty.propertyWithName(kPOPLayerBounds) as POPAnimatableProperty
springAnimation.springBounciness = 12.0
springAnimation.springSpeed = 10.0
springAnimation.toValue = NSValue.valueWithCGRect(newBounds)
shutterButton.pop_addAnimation(springAnimation, forKey: "size")
}
This will compile:
let rect = CGRect(x: 0,y: 0,width: 1,height: 1)
let val = NSValue(CGRect: rect)
This is "initializer syntax"
Here's my solution:
func scaleView(view: UIView, magnitude: CGFloat) {
var springAnimation = POPSpringAnimation()
springAnimation.property = POPAnimatableProperty.propertyWithName(kPOPViewScaleXY) as POPAnimatableProperty
springAnimation.springBounciness = 12.0
springAnimation.springSpeed = 10.0
let newRect = CGRect(x: (view.bounds.origin.x + (view.bounds.size.width / 2.0)) - ((view.bounds.size.width / 2.0) * magnitude), y: (view.bounds.origin.y + (view.bounds.size.height / 2.0)) - ((view.bounds.size.height / 2.0) * magnitude), width: view.bounds.size.width * magnitude, height: view.bounds.size.height * magnitude)
springAnimation.fromValue = NSValue(CGRect: view.bounds)
springAnimation.toValue = NSValue(CGRect: newRect)
view.pop_addAnimation(springAnimation, forKey: "size")
}
In Swift:
let hover = CABasicAnimation(keyPath: "position")
hover.additive = true
hover.fromValue = NSValue(CGPoint: CGPointZero)
hover.toValue = NSValue(CGPoint: CGPointMake(0.0, -10.0))
hover.autoreverses = true
hover.duration = 0.2
hover.repeatCount = Float(Int.max)
button.layer.addAnimation(hover, forKey: "myHoverAnimation")