仿半糖下拉刷新 -- PDPullToRefresh

周末仿写了半糖的下拉刷新,学了两句法语

  • C'est La Vie
  • La Vie est belle

C'est La Vie 通常是用在较为消极的事情发生时,用于自我安慰或自我解嘲,鼓励自己或他人即使遇到了再大的难处,也要坦然笑对生活。哈哈,鸡汤一下开始正文。


仿半糖下拉刷新 -- PDPullToRefresh_第1张图片

示例

  • 半糖


    半糖
  • PDPullToRefresh


    PDPullToRefresh

思路

PDPullToRefresh是给UIScrollView加的分类,包括PDHeaderRefreshView和PDFooterRefreshView ,整个刷新过程可分为两部分

  • 下拉时 - C'est La Vie 动画
  • 刷新时 - La Vie est belle 动画

C'est La Vie 动画

首先得拿到C'est La Vie的字形,这里用到了CoreText,拿到字形后添加到layer.path上显示

CGMutablePathRef letters = CGPathCreateMutable();
    
    CTFontRef font = CTFontCreateWithName(CFSTR("HelveticaNeue-UltraLight"), pFontSize, NULL);
    NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
                           (__bridge id)font, kCTFontAttributeName,
                           nil];
    NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:animationString
                                                                     attributes:attrs];
    CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
    CFArrayRef runArray = CTLineGetGlyphRuns(line);
    
    // for each RUN
    for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
    {
        // Get FONT for this run
        CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
        CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
        // for each GLYPH in run
        for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++)
        {
            // get Glyph & Glyph-data
            CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
            CGGlyph glyph;
            CGPoint position;
            CTRunGetGlyphs(run, thisGlyphRange, &glyph);
            CTRunGetPositions(run, thisGlyphRange, &position);
            // Get PATH of outline
            {
                CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
                CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
                CGPathAddPath(letters, &t, letter);
                CGPathRelease(letter);
            }
        }
    }
    CFRelease(line);
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointZero];
    [path appendPath:[UIBezierPath bezierPathWithCGPath:letters]];
    
    CGPathRelease(letters);
    CFRelease(font);
    
    CAShapeLayer *pathLayer = [CAShapeLayer layer];
    pathLayer.frame = self.animationLayer.bounds;
    pathLayer.bounds = CGPathGetBoundingBox(path.CGPath);
    pathLayer.geometryFlipped = YES;
    pathLayer.path = path.CGPath;
    pathLayer.strokeColor = [UIColor colorWithRed:234.0/255 green:84.0/255 blue:87.0/255 alpha:1].CGColor;
    pathLayer.fillColor = nil;
    pathLayer.lineWidth = 1.0f;
    pathLayer.lineJoin = kCALineJoinBevel;

然后用KVO监听ScrollView的contentOffset属性,与pathLayer的strokeEnd关联起来,C'est La Vie就可以随着下拉做动画啦。

La Vie est belle 动画

La Vie est belle与C'est La Vie的动画不同,它是一直闪动着的,还是先拿到La Vie est belle的字形,这里用CAGradientLayer可以方便的处理颜色渐变。

CAGradientLayer *gradientLayer = (CAGradientLayer *)self.gradientLayer;
    if([gradientLayer animationForKey:kAnimationKey] == nil)
    {
        // 通过不断改变渐变的起止范围,来实现光晕效果
        CABasicAnimation *startPointAnimation = [CABasicAnimation animationWithKeyPath:gradientStartPointKey];
        startPointAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(1.0, 0)];
        startPointAnimation.timingFunction = [CAMediaTimingFunction functionWithName:_animationPacing];
        
        CABasicAnimation *endPointAnimation = [CABasicAnimation animationWithKeyPath:gradientEndPointKey];
        endPointAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(1 + pHaloWidth, 0)];
        endPointAnimation.timingFunction = [CAMediaTimingFunction functionWithName:_animationPacing];
        
        CAAnimationGroup *group = [CAAnimationGroup animation];
        group.animations = @[startPointAnimation, endPointAnimation];
        group.duration = pHaloDuration;
        group.timingFunction = [CAMediaTimingFunction functionWithName:_animationPacing];
        group.repeatCount = HUGE_VALF;
        
        [gradientLayer addAnimation:group forKey:kAnimationKey];
    }

使用

安装

  • 添加 pod 'PDPullToRefresh' 到你的 Podfile ,然后pod install
  • 手动添加到你的Xcode项目中,#import "PDPullToRefresh.h"

添加下拉刷新

[tableView pd_addHeaderRefreshWithNavigationBar:YES andActionHandler:^{
    // prepend data to dataSource, insert cells at top of table view
    // call [tableView.pdHeaderRefreshView stopRefreshing] when done
 }];

添加上拉刷新

[tableView pd_addFooterRefreshWithNavigationBar:YES andActionHandler:^{
    // prepend data to dataSource, insert cells at top of table view
    // call [tableView.pdFooterRefreshView stopRefreshing] when done
 }];

立即刷新

[tableView.pdHeaderRefreshView startRefreshing];

自定义

目前仅支持下拉距离自定义,默认高度为80

@property (nonatomic, assign) CGFloat pdHeaderRefreshViewHeight;
@property (nonatomic, assign) CGFloat pdFooterRefreshViewHeight;

最后

附上Github地址,外加感谢半塘、SVPullToRefresh对我的启发。


Blog地址

你可能感兴趣的:(仿半糖下拉刷新 -- PDPullToRefresh)