iOS - 支持水平/垂直显示自动滚动的跑马灯控件 --- SKAutoScrollLabel的使用和实现

简述

SKAutoScrollLabel是一个同时支持水平/垂直两种类型的“跑马灯”效果的自动滚动UILabel。在滚动的边缘使用了梯度褪色来解决滚动边缘生硬的效果问题,总体效果呈现出混然天成的感觉,并且使用简单方便。如果你觉得还不错,请star支持一下吧~


效果图

如何开始

1.从GitHub上Clone-->SKAutoScrollLabel,然后查看Demo

2.直接将目录下的SKAutoScrollLabel拷贝到工程中,或在podfile文件夹中添加 pod 'SKAutoScrollLabel'

使用方法

初始化

SKAutoScrollLabel * leftLabel = [[SKAutoScrollLabel alloc] initWithFrame:CGRectMake(50, 50, [UIScreen mainScreen].bounds.size.width - 100, 50)];
[self.view addSubview:leftLabel];

基本设置

leftLabel.backgroundColor = [UIColor blackColor];// 背景色
leftLabel.textColor = [UIColor whiteColor];// 字体颜色
leftLabel.font = [UIFont systemFontOfSize:16];// 字体大小
leftLabel.direction = SK_AUTOSCROLL_DIRECTION_LEFT;// 滚动方向,这是向左滚动
leftLabel.text = text;// 显示内容

实现

※ 这里仅列出关键步骤,具体方法请查阅源码

  1. 我们需要创建一个数组,来存放我们滚动时所需要展示的label,这里我们只创建两个label存放到数组中即可(滚动只需要两个即可达成持续跑马灯的效果)
// 创建label数组
    NSMutableSet * labelSet = [[NSMutableSet alloc] init];
    for (NSInteger i = 0; i < kLabelCount; i++) {
        UILabel * label = [UILabel new];
        label.backgroundColor = [UIColor clearColor];
        label.autoresizingMask = self.autoresizingMask;
        label.frame = CGRectMake(0, 0, 50, 50);
        [self.scrollView addSubview:label];
        [labelSet addObject:label];
    }
    self.labels = [labelSet.allObjects copy];

2.区分水平和垂直的UILabel

static void each_object(NSArray *objects, void (^block)(UILabel * label)) {
    for (UILabel * label in objects) {
        block(label);
    }
}

__block float offset = 0;
     // 遍历label数组中的元素
    each_object(self.labels, ^(UILabel *label) {
        [label sizeToFit];
        
        CGRect frame = label.frame;
        // 垂直
        if (self.direction != SK_AUTOSCROLL_DIRECTION_RIGHT && self.direction != SK_AUTOSCROLL_DIRECTION_LEFT) {
            // 动态获取长度和高度, 以此达到将label垂直的目的
            NSDictionary * attribute = [NSDictionary dictionaryWithObjectsAndKeys:self.font,NSFontAttributeName, nil];
            CGSize sizeWord = [@"一" boundingRectWithSize:self.bounds.size options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:attribute context:nil].size;
            CGFloat wordWidth = sizeWord.width;
            CGSize sizeStr = [self.text boundingRectWithSize:CGSizeMake(wordWidth, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:attribute context:nil].size;
            CGFloat wordHeight = sizeStr.height;
            
            frame.origin = CGPointMake((CGRectGetWidth(self.frame) / 2) - (wordWidth / 2), offset);
            frame.size = CGSizeMake(wordWidth, wordHeight);
            label.frame = frame;
            label.numberOfLines = 0;
            offset += CGRectGetHeight(label.bounds) + self.labelSpacing;
            
        } else {// 水平
            frame.origin = CGPointMake(offset, 0);
            frame.size.height = CGRectGetHeight(self.bounds);
            label.frame = frame;
            offset += CGRectGetWidth(label.bounds) + self.labelSpacing;
            // 重新定位label在scrollview中的位置
            label.center = CGPointMake(label.center.x, roundf(self.center.y - CGRectGetMinY(self.frame)));
        }
        
    });

3.判断滚动条件,是否允许滚动

// (水平方向) 判断当前scrollLabel是否大于self的宽度,如果是就开始滚动
    if (CGRectGetWidth(self.scrollLabel.bounds) > CGRectGetWidth(self.bounds)) {
        CGSize size = CGSizeMake(CGRectGetWidth(self.scrollLabel.bounds) + CGRectGetWidth(self.bounds) + self.labelSpacing,  CGRectGetHeight(self.bounds));
        self.scrollView.contentSize = size;
        
        EACH_LABEL(hidden, NO);
        
        [self applyGradientMaskForFadeLength:kDefaultFadeLength enableFade:self.scrolling];
        [self scrollLabelIfNeeded];
        
    } else if (CGRectGetHeight(self.scrollLabel.bounds) > CGRectGetHeight(self.bounds)) {// (垂直方向) 判断当前scrollLabel是否大于self的高度,如果是就开始滚动
        CGSize size = CGSizeMake(CGRectGetWidth(self.bounds), CGRectGetHeight(self.scrollLabel.bounds) + self.labelSpacing);
        self.scrollView.contentSize = size;
        
        EACH_LABEL(hidden, NO);
        
        [self applyGradientMaskForFadeLength:kDefaultFadeLength enableFade:self.scrolling];
        [self scrollLabelIfNeeded];
        
    } else {
        // 隐藏其他label
        EACH_LABEL(hidden, self.scrollLabel != label);
        
        // 调整scrollView和scrollLabel
        self.scrollView.contentSize = self.bounds.size;
        self.scrollLabel.frame = self.bounds;
        self.scrollLabel.hidden = NO;
        self.scrollLabel.textAlignment = NSTextAlignmentCenter;
        
        [self.scrollView.layer removeAllAnimations];
        
        [self applyGradientMaskForFadeLength:0 enableFade:NO];
    }

4.自动滚动动画

switch (self.direction) {// 滚动方向
        case SK_AUTOSCROLL_DIRECTION_LEFT:
            self.scrollView.contentOffset = CGPointZero;
            break;
        case SK_AUTOSCROLL_DIRECTION_RIGHT:
            self.scrollView.contentOffset = CGPointMake(width + self.labelSpacing, 0);
            break;
        case SK_AUTOSCROLL_DIRECTION_TOP:
            self.scrollView.contentOffset = CGPointZero;
            break;
        case SK_AUTOSCROLL_DIRECTION_BOTTOM:
            self.scrollView.contentOffset = CGPointMake(0, height - 10 - CGRectGetHeight(self.bounds));
            break;
    }
        
    // 滚动动画, UIViewAnimationOptionRepeat一直重复动画
    [UIView animateWithDuration:duration delay:self.autoScrollInterval options:UIViewAnimationOptionRepeat animations:^{

        switch (self.direction) {
            case SK_AUTOSCROLL_DIRECTION_LEFT:
                self.scrollView.contentOffset = CGPointMake(width + self.labelSpacing, 0);
                break;
            case SK_AUTOSCROLL_DIRECTION_RIGHT:
                self.scrollView.contentOffset = CGPointZero;
                break;
            case SK_AUTOSCROLL_DIRECTION_TOP:
                self.scrollView.contentOffset = CGPointMake(0, CGRectGetHeight(self.scrollLabel.bounds) + self.labelSpacing);
                break;
            case SK_AUTOSCROLL_DIRECTION_BOTTOM:
                self.scrollView.contentOffset = CGPointZero;

                break;
        }

    } completion:^(BOOL finished) {
        _scrolling = NO;
        // 移除阴影
        [self applyGradientMaskForFadeLength:kDefaultFadeLength enableFade:NO];
        // 完成后循调用
        if (finished) {
            [self performSelector:@selector(scrollLabelIfNeeded)];
        }
    }];

5.使用CAGradientLayer在滚动边缘的制造梯度褪色效果

        // 创建梯度mask和消失长度
        CAGradientLayer * gradientMask = [CAGradientLayer layer];
        gradientMask.bounds = self.layer.bounds;
        gradientMask.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
        
        gradientMask.shouldRasterize = YES;
        gradientMask.rasterizationScale = [UIScreen mainScreen].scale;
        
        CGFloat fadePoint = 0;
        // 垂直方向的梯度褪色
        if (self.direction != SK_AUTOSCROLL_DIRECTION_RIGHT && self.direction != SK_AUTOSCROLL_DIRECTION_LEFT) {
            gradientMask.startPoint = CGPointMake(CGRectGetMidX(self.frame), 0);
            gradientMask.endPoint = CGPointMake(CGRectGetMidX(self.frame), 1);
            fadePoint = fadeLength / CGRectGetHeight(self.bounds);
        // 水平方向的梯度褪色
        } else {
            gradientMask.startPoint = CGPointMake(0, CGRectGetMidY(self.frame));
            gradientMask.endPoint = CGPointMake(1, CGRectGetMidY(self.frame));
            fadePoint = fadeLength / CGRectGetWidth(self.bounds);
        }
        
        // 设置渐变mask颜色和位置
        id transparent = (id)[UIColor clearColor].CGColor;
        id opaque = (id)[UIColor blackColor].CGColor;
        gradientMask.colors = @[transparent, opaque, opaque, transparent];
        
        // 计算褪色
        NSNumber * leftFadePoint = @(fadePoint);
        NSNumber * rightFadePoint = @(1 - fadePoint);
        if (!fade) {
            switch (self.direction) {
                case SK_AUTOSCROLL_DIRECTION_LEFT:
                    leftFadePoint = @0;
                    break;
                case SK_AUTOSCROLL_DIRECTION_RIGHT:
                    leftFadePoint = @0;
                    rightFadePoint = @1;
                    break;
                case SK_AUTOSCROLL_DIRECTION_TOP:
                    leftFadePoint = @0;
                    break;
                case SK_AUTOSCROLL_DIRECTION_BOTTOM:
                    leftFadePoint = @0;
                    rightFadePoint = @1;
                    break;

            }
        }
        
        // 将计算结果交给mask
        gradientMask.locations = @[@0, leftFadePoint, rightFadePoint, @1];
        
        [CATransaction begin];
        [CATransaction setDisableActions:YES];
        self.layer.mask = gradientMask;
        [CATransaction commit];

感谢你花时间阅读以上内容, 如果这个项目能够帮助到你,记得告诉我

Email: shevakuilin@gmail.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容