仿半糖下拉刷新 -- PDPullToRefresh

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

  • C'est La Vie
  • La Vie est belle

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


示例

  • 半糖


    半糖
  • 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地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 15,033评论 4 61
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 178,725评论 25 709
  • 目标 决定自己的优先级并于其上集中 对付时间浪费的策略 建立时间管理计划 成为更好的时间管理者 Quotation...
    青衫磊落_H阅读 353评论 0 0
  • 时间 真的是这个世界上最好的跨度 让惨痛变得苍白 让执着的人选择离开 然后历经沧桑人来人往 你会明白 万般皆是命 ...
    大佐先森阅读 575评论 0 1
  • 1、WOW前60级MOD的内容 说白了就是有些高级玩家对MMORPG网游WOW不满,觉得暴雪做的任务与剧情不符合自...
    westwind1985阅读 807评论 0 0

友情链接更多精彩内容