需求

项目中需要用到跑马灯来仅展示一条消息,长度合适则不滚动,过长则循环滚动。

虽然不是我写的,但看了看代码,是在一个UIView里面放入两个UILabel,

在前一个快结束的时候,另一个显示。然而点击处理的 确是UIView的点击事件。

然而看到比如地铁、公交里面的跑马灯是分了很多段显示的。虽然说可以将多段合并为一段来显示,

但是如果各个需要点击事件又该如何处理呢?于是我来自己实现可点击的多段跑马灯。

所以这篇随笔我要实现的跑马灯包含下面这种效果:(图中有5段   点击不同文本可触发相应的事件)

弯路

还记得上一篇随笔【IOS】将字体大小不同的文字底部对齐 么?

虽然不能够做到多个UILabel的底部对齐,但是我们可以通过继承UILabel来改变文本竖直方向的位置。

所以呢,我最初的想法是继承UILabel,可以保持其继承性, 通过NSTimer来直接慢慢移动UILable里面的文本。

这里出现了两个问题:(以@"这是自定义跑马灯里面要移动的文本"为例)

1.移动是可以移动,但是在文本左移至快要看不见(只剩下"移动的文本")的时候, 如何让@"这是.."开始从右侧出现呢?

2.文本过长的时候,看不见的部分将被截断,所以在移动的时候,只有部分文本了。

第一种好像没有办法,UILabel只存在一个文本的bounds, 不可能让他一部分在左边, 一部分在右边。

第二种就因为存在默认的属性NSLineBreakMode:NSLineBreakByWordWrapping,就算不截断文本也只会变为省略号。

所以这种方法作罢。。。。

实现

首先要明确的是本跑马灯继承了UIView且需要两个UILabel、定时器NSTimer。

在初始化时,传入字符串数组,并计算各个字符串的自适应大小

CGRect textRect = [((NSString *)_textArray[i]) boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:kFont} context:nil];
[_textRectArray addObject:[NSValue valueWithCGRect:textRect]];

如果传入的字符串数组个数为1且自适应宽度<UIView宽度,则不会滚动。重新写一个UILabel用于显示就行了

其他情况下,就是可以滚动的, 在此实例化两个UILabel,并打开定时器了:

定时器相关:

_timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
//为什么要将定时器起放入LOOP中呢?
//如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。
//也就是说,如果你将跑马灯放入scrollview上,当滑动scrollview的时候,定时器就不会动了 //相关方法:
//[_timer setFireDate:[NSDate date]];   开始
//[_timer setFireDate:[NSDate distantFuture]]; 暂停
//取消定时器
//[_timer invalidate];
//_timer = nil;  //防止野指针

//定时器执行事件:

-(void)timerAction:(NSTimer *)timer{}

现在我们要做的  就是在每次进入该方法的时候来设置两个UILabel。

好了,现在假设传入了4个字符串@[@"这个是第0个字符串",@"这个是第1个字符串",@"这个是第2个字符串",@"这个是第3个字符串"];

只有两个Label, 不管向右滚动还是向左滚动,我们将最初显示的定为Labels[0], 后来显示的定位Labels[1]

定义一个变量,实时的存放前一个UILabel的origin.X值,从0开始

1.每次前一个UILabel暂未完全隐藏前,后一个UIlabel就已经出现 (两者间有一个固定的距离  internalWidth)

还需要根据speed的值更改Labels[0]的大小的增减来控制Labels[0]的位置(更改offsetX值)。

通过这个距离和前一个的位置则可以实时的计算后一个UILabel的位置(origin.X值)。

2.每次前一个UILabel完全隐藏时就需要重新设置一个值, 此时刻在每次前一个UILabel完全看不到后只进入一次

同时将左右两个UILabel变换一下位置。 往左滚动: A<--B,A消失后,A跑到B右边去了;   成为B<--A,B消失后,又要到A右边去。

所以只需要设置offsetX = _labels[1].frame.origin.x;//A消失后,将后面的B位置作为下一个将消失的label的位置,A变为后面一个,

                               //其位置根据B的位置实时计算出来。每次前一个消失后,如此循环的更换。

但是这样只更改了位置,文本以及大小却没有变换,见3.

3.对于只有一个文本来说,AB的内容都是一样的。但是对于传入的四个字符串而言,每次重新设置值的时候,需要更改AB内容。

同时,对于长度不等的字符串,需要根据不同的文本大小来设置相应AB的Frame。

所以需将四个字符串文本大小,文本内容在之前保存为一个数组。定义一个始终记录当前正准备消失的(前一个)UIlabel的位置:_currentIndex

在步骤1中: 从两个数组中分别获取用于显示在A.B里的文本数组:labelTextArray  frame数组:labelArray(从中取得宽和高)

每次AB位置交换的时候,需将currentIndex+1 : 即_currentIndex = (_currentIndex + 1)  % _textArray.count;以供交换后使用。

之后分别取得当前以及下一个的Text和frame   分别保存到长度为2的数组   以便使用

上面太多、太乱。。。。。。我不想看我不想看我不想看。。。。。。

这里有图: 看完上面还完全不懂的请看这个吧。

再次解释

<===============左移==================

================右移=================>

现在只看颜色 从图中看可以到 无论左滚右滚   绿色始终是Labels[0](将要消失的Label)  红色始终是Label[1]

正常滚动情况下:

  绿色的offsetX值随着speed而变 : self.offsetX =  self.offsetX - sign * self.speed;

  红色的X值会随着绿色的offsetX和固定间距的关系而变 : CGFloat nextOffX = self.offsetX + sign * (((self.orientation == RollingOrientationLeft)? firstRect.size.width : lastRect.size.width) +   self.internalWidth);

  通过_currentIndex值从保存到的数据中获取到红色、绿色的内容和大小后赋值:

当绿色消失的一瞬间:

  本该是在右边的红色一下子吓绿了 : self.offsetX = _labels[1].frame.origin.x;

  消失的绿色又将会按照正常滚动的情况下变为红色

   _currentIndex指得始终是绿色内容的索引: _currentIndex = (_currentIndex + 1)  % _textArray.count;

  通过这个值又将会获取按照正常滚动的情况下  红色、绿色的大小和文本内容

  (两个又将会进行的流程将会在"正常滚动情况下"的蓝色部分操作)

}

好了  解释到此结束   看着好累。。。。   慢着   还有点击事件没写完

点击事件:在给UILabel添加了Tap手势后进行处理

-(void)labelTap:(UITapGestureRecognizer *)gesture{
NSInteger tag = ((UILabel *)[gesture view]).tag - ;
NSInteger index;
if(tag == ){ //如果是(Labels[0])绿色
index = _currentIndex;
}else if (tag == ){ //如果是(Labels[1])红色 就是当前点击的后一个
index = (_currentIndex + ) % _textArray.count;
}else{
index = _currentIndex;
}
if(self.labelClickBlock){
self.labelClickBlock(index);
}
}

终点

代码见GitHub: ====>   YFRollingLabel       PS:源码以及GitHub文档都是用蹩脚的英语写的,也不知道会不会有人看。。

另外说明记录存在的问题:

对于放入的文本数组  长度不能太短   因为里面只有两个UILabel  如果长度太短的话   并且间距也小的情况下

在绿色刚消失后, 又会立马变为红色,出现在目前的绿色右边,而不是慢慢的移动出现。

文本太长太长的话(几百个中文,正常情况下不会设置这么多吧), 会导致获取的文本宽度过长,UILabel宽度过长,文本直接就不显示了,但点击事件还是有的  说明了还存在。。。这就搞不懂。。。

好了,终于写完了,Windows 10 Mobile 万岁!!!

PS:新的知识点:CADisplayLink

CADisplayLink是一个能让我们以和屏幕刷新率相同的频率(每秒60次)将内容画到屏幕上的定时器。

但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。

//创建
CADisplayLink displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doSomeThing:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; //设置是否停止
displayLink.Pause = NO/YES; //释放
[displayLink invalidate];
displayLink = nil;

【IOS】自定义可点击的多文本跑马灯YFRollingLabel的更多相关文章

  1. Android文字跑马灯控件(文本自动滚动控件)

    最近在开发一个应用,需要用到文本的跑马灯效果,图省事,在网上找,但老半天都找不到,后来自己写了一个,很简单,代码如下: import android.content.Context; import a ...

  2. 【iOS自定义键盘及键盘切换】详解

    [iOS自定义键盘]详解 实现效果展示: 一.实现的协议方法代码 #import <UIKit/UIKit.h> //创建自定义键盘协议 @protocol XFG_KeyBoardDel ...

  3. iOS自定义的UISwitch按钮

    UISwitch开关控件 开关代替了点选框.开关是到目前为止用起来最简单的控件,不过仍然可以作一定程度的定制化. 一.创建 UISwitch* mySwitch = [[ UISwitchalloc] ...

  4. iOS 自定义转场动画浅谈

    代码地址如下:http://www.demodashi.com/demo/11612.html 路漫漫其修远兮,吾将上下而求索 前记 想研究自定义转场动画很久了,时间就像海绵,挤一挤还是有的,花了差不 ...

  5. iOS自定义转场动画实战讲解

    iOS自定义转场动画实战讲解   转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerA ...

  6. 自定义可点击的ImageSpan并在TextView中内置“View“

    有的时候可能想在TextView中添加一些图片,比如下图,发短信输入联系人时,要把联系人号码换成一个图片,但这个图片无法用固定的某张图,而是根据内容进行定制的,这更像一个view. 当然,如果你不是v ...

  7. 给iOS开发新手送点福利,简述文本属性Attributes的用法

    给iOS开发新手送点福利,简述文本属性Attributes的用法   文本属性Attributes 1.NSKernAttributeName: @10 调整字句 kerning 字句调整 2.NSF ...

  8. 如何实现 iOS 自定义状态栏

    给大家介绍如何实现 iOS 自定义状态栏 Sample Code: 01 UIWindow * statusWindow = [[UIWindow alloc] initWithFrame:[UIAp ...

  9. ios的300ms点击延时问题

    一.什么是ios的300ms点击延时问题 ios的移动端页面对点击事件有300ms延时. 二.为什么存在这个问题 这要追溯至 2007 年初.苹果公司在发布首款 iPhone 前夕,遇到一个问题 —— ...

随机推荐

  1. Geeklink引领智慧新生活!

    煤油灯成为古董,管道天然气进入厨房,电脑挤进生活,手机代替书信成为通讯的主要工具-这些变化无不提醒我们,时代在变迁,科技在发展.而最近朋友圈和电视又在播报智能家居的生活方式-智能家电能实现怎样的情景功 ...

  2. 三维网格精简算法(Quadric Error Metrics)附源码

    在计算机图形应用中,为了尽可能真实呈现虚拟物体,往往需要高精度的三维模型.然而,模型的复杂性直接关系到它的计算成本,因此高精度的模型在几何运算时并不是必须的,取而代之的是一个相对简化的三维模型,那么如 ...

  3. 【repost】document.write的用处

    document.write的用处 document.write是JavaScript中对document.open所开启的文档流(document stream操作的API方法,它能够直接在文档流中 ...

  4. poj 2352 Stars 数星星 详解

    题目: poj 2352 Stars 数星星 题意:已知n个星星的坐标.每个星星都有一个等级,数值等于坐标系内纵坐标和横坐标皆不大于它的星星的个数.星星的坐标按照纵坐标从小到大的顺序给出,纵坐标相同时 ...

  5. go 常用包

    标准的 Go 代码库中包含了大量的包,并且在安装 Go 的时候多数会伴随一起安 装.浏览 $GOROOT/src/pkg 目录并且查看那些包会非常有启发. fmt:包 fmt 实现了格式化的 I/O ...

  6. 我要成为前端工程师!给 JavaScript 新手的建议与学习资源整理

    来源于:http://blog.miniasp.com/post/2016/02/02/JavaScript-novice-advice-and-learning-resources.aspx 今年有 ...

  7. [转]Java中导入、导出Excel

    原文地址:http://blog.csdn.net/jerehedu/article/details/45195359 一.介绍 当前B/S模式已成为应用开发的主流,而在企业办公系统中,常常有客户这样 ...

  8. mysql 基本操作

    创建数据库 create database studentdb charset utf8; #创建数据库 create database studentdb charset utf8; #查看数据库的 ...

  9. Android Studio导入项目慢的问题

    在Github下载的项目,导入studio时非常慢,原因是下载的项目中的gradle与当前gradle不匹配,需要更新包. 解决办法:修改下载包中的文件 1. xxx-project/.idea/gr ...

  10. FFT 快速傅里叶变换浅析

    终于补完坑了哈哈哈 这个东西很神奇,看了半天网上的解释和课件,研究了很长时间,算是大概明白了它的原理. 话不多说先上图. 我们要求的h(x)=f(x)*g(x),f(x)=Σai*x^i,g(x)=Σ ...