0

I want to animate a UIView in a figure 8 motion. I am doing this in Swift using BezierPath but I want to add a delay to the animation.

let center: CGPoint = CGPoint(x: 60, y: 45)
let cent: CGPoint = CGPoint(x: 90, y: 45)
let radius: CGFloat = 15.0
let start: CGFloat = CGFloat(M_PI)
let end: CGFloat = 0.0
let path1: UIBezierPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: start, endAngle: end, clockwise: true)
let path2: UIBezierPath = UIBezierPath(arcCenter: cent, radius: radius, startAngle: -start, endAngle: end, clockwise: false)
let path3: UIBezierPath = UIBezierPath(arcCenter: cent, radius: radius, startAngle: end, endAngle: -start, clockwise: false)
let path4: UIBezierPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: end, endAngle: start, clockwise: true)
let paths: UIBezierPath = UIBezierPath()
paths.appendPath(path1)
paths.appendPath(path2)
paths.appendPath(path3)
paths.appendPath(path4)

let anim: CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "position")
anim.path = paths.CGPath
anim.repeatCount = 2.0
anim.duration = 3.0

view.layer.addAnimation(anim, forKey: "animate position along path")

The figure 8 works just fine, but I have no idea how to add a delay. I have tried using methods like animateWithDuration(delay:options:animation:completion:) but that didn't help. If I am way off base and there is an easier way to animate a UIView in a 'Figure 8'/'Inifinity Loop' motion that would be fine. I just need the motion coupled with the delay.

Cody Weaver
  • 4,756
  • 11
  • 33
  • 51

2 Answers2

4

Animation objects have a start time. Set that to the future by whatever amount you want.

anim.beginTime = CACurrentMediaTime() + 1.0

This example will delay the start by 1 second.

Using a dispatch_after block to kick off the entire animation method may achieve the same result in the short run, but could inhibit the ability to adjust or manipulate additional timing functions on the animation relative to it's start time in the future. Adjusting the time on the animation itself keeps your animation object's timing state consistent.

isaac
  • 4,867
  • 1
  • 21
  • 31
  • 1
    You seem to know a lot about this, so I would really appreciate if you could answer some questions I have. If he defines the animation in the completion block will it still inhibit the ability to adjust it later? Can you give an example of these inhibitions you mention? Is using `dispatch_after` for UI things a bad idea in general? And lastly, where would I learn about this kind of stuff (how things would work in a short run but later inhibit abilities)? Thanks :) (even if you don't answer these lol) – Ike10 Jun 24 '16 at 00:47
  • Not necessarily - in practice it may have no effect, or it might. Using dispatch_after isn't a bad idea when you need something delayed on the UI, the issue here is basically mixing timing models. An important part of animation state is timing, so it's really just a matter of consistency, such that the animation object know's it own characteristics and can respond/adjust accordingly as circumstances change during execution. I would start by reading Apple's Guide to Core Animation, it's very insightful. – isaac Jun 24 '16 at 15:22
  • That makes a lot of sense. Thank you so much for taking the time to answer my questions, and an extra thank you for a place to continue learning. I will definitely read the guide when I have the time! :D – Ike10 Jun 24 '16 at 15:53
  • @CodyWeaver you should look at this solution, as it is safer for explained reasons. – Ike10 Jun 24 '16 at 15:54
1

If you want the animation to trigger after a delay you can try triggering the animation in a function like this:

func executeWithDelay(_ delay: NSTimeInterval, block: dispatch_block_t) {
    let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
    dispatch_after(delay, dispatch_get_main_queue(), block)
}

and use it like:

executeWithDelay(1.0) {
   //trigger animation
}

The above example will trigger the block after 1.0 second.

Ike10
  • 1,585
  • 12
  • 14
  • 1
    This will work but Issac's answer using the animation's beginTime property is better, for the reasons he outlines. – Duncan C Jun 24 '16 at 00:34