【IOS】自定义可点击的多文本跑马灯YFRollingLabel
需求
项目中需要用到跑马灯来仅展示一条消息,长度合适则不滚动,过长则循环滚动。
虽然不是我写的,但看了看代码,是在一个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的更多相关文章
- Android文字跑马灯控件(文本自动滚动控件)
最近在开发一个应用,需要用到文本的跑马灯效果,图省事,在网上找,但老半天都找不到,后来自己写了一个,很简单,代码如下: import android.content.Context; import a ...
- 【iOS自定义键盘及键盘切换】详解
[iOS自定义键盘]详解 实现效果展示: 一.实现的协议方法代码 #import <UIKit/UIKit.h> //创建自定义键盘协议 @protocol XFG_KeyBoardDel ...
- iOS自定义的UISwitch按钮
UISwitch开关控件 开关代替了点选框.开关是到目前为止用起来最简单的控件,不过仍然可以作一定程度的定制化. 一.创建 UISwitch* mySwitch = [[ UISwitchalloc] ...
- iOS 自定义转场动画浅谈
代码地址如下:http://www.demodashi.com/demo/11612.html 路漫漫其修远兮,吾将上下而求索 前记 想研究自定义转场动画很久了,时间就像海绵,挤一挤还是有的,花了差不 ...
- iOS自定义转场动画实战讲解
iOS自定义转场动画实战讲解 转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerA ...
- 自定义可点击的ImageSpan并在TextView中内置“View“
有的时候可能想在TextView中添加一些图片,比如下图,发短信输入联系人时,要把联系人号码换成一个图片,但这个图片无法用固定的某张图,而是根据内容进行定制的,这更像一个view. 当然,如果你不是v ...
- 给iOS开发新手送点福利,简述文本属性Attributes的用法
给iOS开发新手送点福利,简述文本属性Attributes的用法 文本属性Attributes 1.NSKernAttributeName: @10 调整字句 kerning 字句调整 2.NSF ...
- 如何实现 iOS 自定义状态栏
给大家介绍如何实现 iOS 自定义状态栏 Sample Code: 01 UIWindow * statusWindow = [[UIWindow alloc] initWithFrame:[UIAp ...
- ios的300ms点击延时问题
一.什么是ios的300ms点击延时问题 ios的移动端页面对点击事件有300ms延时. 二.为什么存在这个问题 这要追溯至 2007 年初.苹果公司在发布首款 iPhone 前夕,遇到一个问题 —— ...
随机推荐
- DevExpress ChartControl 样式设置
第三方控件,设置ChartControl的样式,仅供参考 Demo: <Grid> <Grid.Resources> <SolidColorBrush x:Key=&qu ...
- Redis与KV存储(RocksDB)融合之编码方式
Redis与KV存储(RocksDB)融合之编码方式 简介 Redis 是目前 NoSQL 领域的当红炸子鸡,它象一把瑞士军刀,小巧.锋利.实用,特别适合解决一些使用传统关系数据库难以解决的问题.Re ...
- 20145215&20145307《信息安全系统设计基础》实验五 网络通信
小组成员:20145215卢肖明.20145307陈俊达 实验报告链接:信息安全系统设计基础--实验五实验报告
- FineUI(专业版)公测版发布(这速度,真TM快!)
经过近一年的筹备.编码和测试,FineUI(专业版)公测版终于和大家见面了!现在就来体验一下专业版飞一般的速度吧:http://fineui.com/demo_pro/FineUI(专业版)首页:ht ...
- CF719E(线段树+矩阵快速幂)
题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这 ...
- jQuery之回调对象
1. jQuery 1.7 版本中新增的 jQuery.Callbacks() 函数返回一个全能的对象,此对象对管理回调列表提供了强大的方式.它能够增加.删除.触发.禁用回调函数. 2. callba ...
- poj3581
Sequence Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 6893 Accepted: 1534 Case Tim ...
- maven
maven常见问题问答 1.前言 Maven,发音是[`meivin],"专家"的意思.它是一个很好的项目管理工具,很早就进入了我的必备工具行列,但是这次为了把project1项目 ...
- Podfile使用说明
什么是Podfile ? CocoaPods是用ruby实现的,因此Podfile文件的语法就是ruby的语法.podfile是一个说明文件,用以描述管理一个或者多个Xcode project的tar ...
- Junit的使用
Junit是用于编写单元测试的框架.对于已经写好的函数,可以使用Junit生成单元测试代码. 自己的环境是:Linux Java环境是:JDK1.7 IDE:Eclipse Java EE IDE f ...