If you need subtle control over the animation, you can use CADisplayLink to execute any custom layout or drawing before updating the screen pixels. The start cycle tries to draw 60 frames per second, so with this in mind, you can change your code to simulate touch events.
You need to add some properties:
var displayLink:CADisplayLink? // let us tap into the drawing run loop var startTime:NSDate? // while let us keep track of how long we've been animating var deltaX:CGFloat = 0.0 // how much we should update in the x direction between frames var deltaY:CGFloat = 0.0 // same but in the y direction var startValue = CGPointMake(70.0, 50.0) // where we want our touch simulation to start var currentPoint = CGPoint(x:0.0, y:0.0) let endValue = CGPointMake(90.0, 150.0) // where we want our touch simulation to end
Then, when we want to start the animation, we can call:
func animate() { let duration:CGFloat = 2.0 self.currentPoint = self.startValue self.deltaX = (endValue.x - startValue.x) / (duration * 60.0)
This will establish a link to the image and determine how much we will simulate the touch simulator between frames and start calling the function that will be called before each performAnimation screen performAnimation :
func performAnimation(){ if self.startTime?.timeIntervalSinceNow > -2 { self.updateViewsFor(self.currentPoint, translation: CGPoint(x:self.currentPoint.x - self.startValue.x, y: self.currentPoint.y - self.startValue.y)) self.currentPoint.x += self.deltaX self.currentPoint.y += self.deltaY } else { self.displayLink?.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode) self.currentPoint = self.startValue } }
Here we check if we are within the duration of the animation. Then call updateViewsFor(point:CGPoint,translation:CGPoint) , which was in your target gesture, and then update our touch simulation if we are in it, otherwise we just reset our properties.
Finally,
func updateViewsFor(point:CGPoint,translation:CGPoint) { let additionalHeight = max(translation.y, 0) let waveHeight = min(additionalHeight * 0.6, maxWaveHeight) let baseHeight = minimalHeight + additionalHeight - waveHeight let locationX = point.x layoutControlPoints(baseHeight: baseHeight, waveHeight: waveHeight, locationX: locationX) updateShapeLayer() }
You can also change your panGestureDidMove to:
@objc func panGestureDidMove(gesture: UIPanGestureRecognizer) { if gesture.state == .Ended || gesture.state == .Failed || gesture.state == .Cancelled { } else { self.updateViewsFor(gesture.locationInView(gesture.view), translation: gesture.translationInView(view)) } }
Edit
There is an easier way to do this using keyframe animation. But I'm not sure if it will revive your updateShapeLayer() . But for animating views, we can write a function such as:
func animate(fromPoint:CGPoint, toPoint:CGPoint, duration:NSTimeInterval) {
This will move the views into place, and then create a keyframe in which the views will be displayed and animate everything in between. We could call it that:
override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) self.animate(CGPointMake(70.0, 50.0), toPoint: CGPointMake(90.0, 150.0), duration: 2.0) }