I recently saw a cool drop-down refresh effect on the web (iostuts.io/2015/10/17/… [4]). I tried to realize the jelly rebound effect.


Review images


  • Because the writing style is not very good -.-, it is recommended to download the demo, and then combined with the following analysis, it will be better to understand the point. Address github.com/Resory/RYCu… [5]


  • Below p1, the blue part is a CAShapeLayer whose shape is composed of the path of UIBezierPath.

  • This path is defined by the red dots R1, R2, R3, R4, and R5. R1, R2, R3 and R4 are fixed points, and the only point that can move is R5.

  • According to the dynamic diagram above, the shape of CAShapeLayer changes with the movement of r5 red dot, so as long as the coordinate changes of R5 can be obtained, the corresponding path can be made with UIBezierPath, and then the corresponding shape can be formed.

Review images


  • Initialize CAShapeLayer
- (void)configShapeLayer
{ 
    _shapeLayer = [CAShapeLayer layer];
    _shapeLayer.fillColor = [UIColor colorWithRed:57/255.0 green:67/255.0 blue:89/255.0 alpha:1.0].CGColor;
    [self.layer addSublayer:_shapeLayer];
}Copy the code
  • Initialize r5
(void)configCurveView {// _curveView = SYS_DEVICE_WIDTH/2.0; _curveY = MIN_HEIGHT; _curveView = [[UIView alloc] initWithFrame:CGRectMake(_curveX, _curveY, 3, 3)]; _curveView.backgroundColor = [UIColor redColor]; [self addSubview:_curveView]; }Copy the code
  • Added mobile gestures &CADisplayLink
- (void)configAction { _mHeight = 100; _isAnimating = NO; UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanAction:)]; self.userInteractionEnabled = YES; [self addGestureRecognizer:pan]; // calculatePath is to calculate the coordinates of _curveView at runtime, To determine the shape of the _shapeLayer _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @ the selector (calculatePath)]; [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; // The calculatePath method is called only at the end of the gesture, so it is initially paused _displayLink. Paused = YES; }Copy the code
    • When the gesture moves, the r5 red dot moves with the gesture, and _shapeLayer expands its area based on the coordinates of R5

    • At the end of the gesture, the r5 red dot changes the coordinates of R5 through UIView’s animation method, and _shapeLayer shrinks its area according to the coordinates of R5 and finally returns to its original shape.

- (void)handlePanAction:(UIPanGestureRecognizer *)pan { if(! _isAnimating) {if (pan) state = = UIGestureRecognizerStateChanged) {/ / gestures while moving, CGPoint point = [pan translationInView:self]; _mHeight = point.y*0.7 + MIN_HEIGHT; _curveX = SYS_DEVICE_WIDTH/2.0 + point.x; _curveY = _mHeight > MIN_HEIGHT ? _mHeight : MIN_HEIGHT; _curveView.frame = CGRectMake(_curveX, _curveY, _curveView.frame.size.width, _curveView.frame.size.height); // Update _shapeLayer shape [self updateShapeLayerPath] according to r5; } else if (pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateEnded || pan.state == At the end of the UIGestureRecognizerStateFailed) {/ / gestures, _shapeLayer return to original state and spring dynamic effect _isAnimating = YES; _displayLink.paused = NO; / / open the displaylink method performs calculatePath. / / spring dynamic effect [UIView animateWithDuration: 1.0 delay: usingSpringWithDamping 0.0:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ // The curve point (R5) is a view, so there is spring effect in the block, and then calculate the shape of the elastic graph in calculatePath according to its dynamic path _curveView.frame = CGRectMake(SYS_DEVICE_WIDTH/2.0, MIN_HEIGHT, 3, 3); } completion:^(BOOL finished) { if(finished) { _displayLink.paused = YES; _isAnimating = NO; } }]; }}}Copy the code
  • Update _shapeLayer shape based on position of R5
UIBezierPath *tPath = [UIBezierPath bezierPath]; - (void)updateShapeLayerPath {// Update _shapeLayer UIBezierPath *tPath = [UIBezierPath bezierPath]; [tPath moveToPoint:CGPointMake(0, 0)]; / / r1 points [tPath addLineToPoint: CGPointMake (SYS_DEVICE_WIDTH, 0)]; / / r2 points [tPath addLineToPoint: CGPointMake (SYS_DEVICE_WIDTH MIN_HEIGHT)]; / / r4 points [tPath addQuadCurveToPoint: CGPointMake (0, MIN_HEIGHT) controlPoint: CGPointMake (_curveX _curveY)]; // r3,r4,r5 determine an arc [tPath closePath]; _shapeLayer.path = tPath.CGPath; }Copy the code
  • Calculate the spring effect coordinates
- (void)calculatePath {// Since at the end of the gesture, R5 performs a spring animation of UIView, writes down the coordinates of the process and draws the corresponding _shapeLayer shape CALayer *layer = _curveView.layer.presentationLayer; _curveX = layer.position.x; _curveY = layer.position.y; [self updateShapeLayerPath]; }Copy the code

  • The role of R5 points is very important, because it is not easy to implement dynamic effects directly on CAShapeLayer. Therefore, the spring dynamic effect of R5 point is realized by recording the coordinates of R5 point, forming a path with UIBezierPath, and finally giving CAShapeLayer, indirectly completing the spring dynamic effect of CAShapeLayer.

  • If you have any questions or find any errors please leave a message to me. 3Q~~