How to replace panGestureDidMove with fixed start and end positions

I use UIPanGestureRecognizer for touch recognition, but I want to replace it with fixed start and end positions for my animation. See code below:

panGestureDidMove:

func panGestureDidMove(gesture: UIPanGestureRecognizer) { if gesture.state == .Ended || gesture.state == .Failed || gesture.state == .Cancelled { } else { let additionalHeight = max(gesture.translationInView(view).y, 0) let waveHeight = min(additionalHeight * 0.6, maxWaveHeight) let baseHeight = minimalHeight + additionalHeight - waveHeight let locationX = gesture.locationInView(gesture.view).x layoutControlPoints(baseHeight: baseHeight, waveHeight: waveHeight, locationX: locationX) updateShapeLayer() } } 

layoutControlPoints:

 private func layoutControlPoints(baseHeight baseHeight: CGFloat, waveHeight: CGFloat, locationX: CGFloat) { let width = view.bounds.width let minLeftX = min((locationX - width / 2.0) * 0.28, 0.0) let maxRightX = max(width + (locationX - width / 2.0) * 0.28, width) let leftPartWidth = locationX - minLeftX let rightPartWidth = maxRightX - locationX l3ControlPointView.center = CGPoint(x: minLeftX, y: baseHeight) l2ControlPointView.center = CGPoint(x: minLeftX + leftPartWidth * 0.44, y: baseHeight) l1ControlPointView.center = CGPoint(x: minLeftX + leftPartWidth * 0.71, y: baseHeight + waveHeight * 0.64) cControlPointView.center = CGPoint(x: locationX , y: baseHeight + waveHeight * 1.36) r1ControlPointView.center = CGPoint(x: maxRightX - rightPartWidth * 0.71, y: baseHeight + waveHeight * 0.64) r2ControlPointView.center = CGPoint(x: maxRightX - (rightPartWidth * 0.44), y: baseHeight) r3ControlPointView.center = CGPoint(x: maxRightX, y: baseHeight) } 

I am trying to replace panGestureDidMove with CABasicAnimation in order to animate the start and end position, something like the code below:

 let startValue = CGPointMake(70.0, 50.0) let endValue = CGPointMake(90.0, 150.0) CATransaction.setDisableActions(true) //Not necessary view.layer.bounds.size.height = endValue let positionAnimation = CABasicAnimation(keyPath:"bounds.size.height") positionAnimation.fromValue = startValue positionAnimation.toValue = endValue positionAnimation.duration = 2.0 view.layer.addAnimation(positionAnimation, forKey: "bounds") 

Many factors influence me, as the situation changes, how can I achieve this?

+7
swift uiview uigesturerecognizer cabasicanimation
source share
1 answer

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) // 60 frames per second so getting the difference then dividing by the duration in seconds times 60 self.deltaY = (endValue.y - startValue.y) / (duration * 60.0) self.startTime = NSDate() self.displayLink = CADisplayLink(target: self, selector: #selector(self.performAnimation)) self.displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode) } 

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) { // Essentually simulates the beginning of a touch event at our start point and the touch hasn't moved so tranlation is zero self.updateViewsFor(fromPoint, translation: CGPointZero) // Create our keyframe animation using UIView animation blocks UIView.animateKeyframesWithDuration(duration, delay: 0.0, options: .CalculationModeLinear, animations: { // We really only have one keyframe which is the end. We want everything in between animated. // So start time zero and relativeDuration 1.0 because we want it to be 100% of the animation UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 1.0, animations: { // we want our "touch" to move to the endValue and the translation will just be the difference between end point and start point in the x and y direction. self.updateViewsFor(toPoint, translation: CGPoint(x: toPoint.x - fromPoint.x, y: toPoint.y - fromPoint.y)) }) }, completion: { _ in // do anything you need done after the animation }) } 

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) } 
0
source share

All Articles