TXScrollLabelView.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. //
  2. // TXScrollLabelView.m
  3. //
  4. // Created by tingxins on 2/23/16.
  5. // Copyright © 2016 tingxins. All rights reserved.
  6. // 如果在使用 TXScrollLabelView 的过程中出现bug,请及时联系,我会尽快进行修复。如果有更好的点子,直接 Open an issue 或者 submit a pr。
  7. /**
  8. Blog : https://tingxins.com
  9. 简书 :http://www.jianshu.com/u/5141561e4d59
  10. GitHub : https://github.com/tingxins
  11. Weibo : http://weibo.com/tingxins
  12. Twitter : http://twitter.com/tingxins
  13. */
  14. #define TXScrollLabelFont [UIFont systemFontOfSize:14]
  15. #import "TXScrollLabelView.h"
  16. #import <CoreText/CoreText.h>
  17. static const NSInteger TXScrollDefaultTimeInterval = 2.0;//滚动默认时间
  18. typedef NS_ENUM(NSInteger, TXScrollLabelType) {
  19. TXScrollLabelTypeUp = 0,
  20. TXScrollLabelTypeDown
  21. };
  22. #pragma mark - NSTimer+TXTimerTarget
  23. @interface NSTimer (TXTimerTarget)
  24. + (NSTimer *)tx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeat:(BOOL)yesOrNo block:(void(^)(NSTimer *timer))block;
  25. @end
  26. @implementation NSTimer (TXTimerTarget)
  27. + (NSTimer *)tx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeat:(BOOL)yesOrNo block:(void (^)(NSTimer *))block{
  28. return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(startTimer:) userInfo:[block copy] repeats:yesOrNo];
  29. }
  30. + (void)startTimer:(NSTimer *)timer {
  31. void (^block)(NSTimer *timer) = timer.userInfo;
  32. if (block) {
  33. block(timer);
  34. }
  35. }
  36. @end
  37. #pragma mark - UILabel+TXLabel
  38. @interface TXScrollLabel : UILabel
  39. @property (assign, nonatomic) UIEdgeInsets contentInset;
  40. @end
  41. @implementation TXScrollLabel
  42. - (instancetype)init {
  43. if (self = [super init]) {
  44. _contentInset = UIEdgeInsetsZero;
  45. }
  46. return self;
  47. }
  48. - (instancetype)initWithFrame:(CGRect)frame {
  49. if (self = [super initWithFrame:frame]) {
  50. _contentInset = UIEdgeInsetsZero;
  51. }
  52. return self;
  53. }
  54. - (void)drawTextInRect:(CGRect)rect {
  55. [super drawTextInRect:UIEdgeInsetsInsetRect(rect, _contentInset)];
  56. }
  57. @end
  58. @interface TXScrollLabel (TXLabel)
  59. + (instancetype)tx_label;
  60. @end
  61. @implementation TXScrollLabel (TXLabel)
  62. + (instancetype)tx_label {
  63. TXScrollLabel *label = [[TXScrollLabel alloc]init];
  64. label.numberOfLines = 0;
  65. label.font = TXScrollLabelFont;
  66. label.textColor = [UIColor whiteColor];
  67. label.lineBreakMode = NSLineBreakByWordWrapping;
  68. label.textAlignment = NSTextAlignmentCenter;
  69. return label;
  70. }
  71. @end
  72. #pragma mark - TXScrollLabelView
  73. @interface TXScrollLabelView ()
  74. @property (assign, nonatomic) UIViewAnimationOptions options;
  75. @property (weak, nonatomic) TXScrollLabel *upLabel;
  76. @property (weak, nonatomic) TXScrollLabel *downLabel;
  77. //定时器
  78. @property (strong, nonatomic) NSTimer *scrollTimer;
  79. //文本行分割数组
  80. @property (strong, nonatomic) NSArray *scrollArray;
  81. @property (strong, nonatomic) NSArray *scrollTexts;
  82. //当前滚动行
  83. @property (assign, nonatomic) NSInteger currentSentence;
  84. //是否第一次开始计时
  85. @property (assign, nonatomic, getter=isFirstTime) BOOL firstTime;
  86. //传入参数是否为数组
  87. @property (assign, nonatomic) BOOL isArray;
  88. @end
  89. @implementation TXScrollLabelView
  90. @synthesize scrollSpace = _scrollSpace;
  91. @synthesize font = _font;
  92. #pragma mark - Preference Methods
  93. - (void)setSomePreference {
  94. /** Default preference. */
  95. self.backgroundColor = [UIColor blackColor];
  96. self.scrollEnabled = NO;
  97. }
  98. - (void)setSomeSubviews {
  99. TXScrollLabel *upLabel = [TXScrollLabel tx_label];
  100. self.upLabel = upLabel;
  101. [self addSubview:upLabel];
  102. TXScrollLabel *downLabel = [TXScrollLabel tx_label];
  103. self.downLabel = downLabel;
  104. [self addSubview:downLabel];
  105. [upLabel addTapGesture:self sel:@selector(didTap:)];
  106. [downLabel addTapGesture:self sel:@selector(didTap:)];
  107. }
  108. #pragma mark - UITapGestureRecognizer Methods
  109. - (void)didTap:(UITapGestureRecognizer *)tapGesture {
  110. UILabel *label = (UILabel *)tapGesture.view;
  111. if (!label || ![label isKindOfClass:[UILabel class]]) return;
  112. NSInteger index = 0;
  113. if (self.scrollArray.count) index = [self.scrollArray indexOfObject:label.text];
  114. if ([self.scrollLabelViewDelegate respondsToSelector:@selector(scrollLabelView:didClickWithText:atIndex:)]) {
  115. [self.scrollLabelViewDelegate scrollLabelView:self didClickWithText:label.text atIndex:index];
  116. }
  117. }
  118. #pragma mark - Instance Methods
  119. /** Terminating app due to uncaught exception 'Warning TXScrollLabelView -[TXScrollLabelView init] unimplemented!', reason: 'unimplemented, use - scrollWithTitle:scrollType:scrollVelocity:options:'*/
  120. - (instancetype)init {
  121. @throw [NSException exceptionWithName:[NSString stringWithFormat:@"Warning %@ %s unimplemented!", self.class, __func__] reason:@"unimplemented, please use - scrollWithTitle:scrollType:scrollVelocity:options:" userInfo:nil];
  122. }
  123. - (instancetype)initWithFrame:(CGRect)frame {
  124. if (self = [super initWithFrame:frame]) {
  125. [self setSomePreference];
  126. [self setSomeSubviews];
  127. }
  128. return self;
  129. }
  130. - (instancetype)initWithTitle:(NSString *)scrollTitle
  131. type:(TXScrollLabelViewType)scrollType
  132. velocity:(NSTimeInterval)scrollVelocity
  133. options:(UIViewAnimationOptions)options
  134. inset:(UIEdgeInsets)inset {
  135. if (self = [super init]) {
  136. _scrollTitle = scrollTitle;
  137. _scrollType = scrollType;
  138. self.scrollVelocity = scrollVelocity;
  139. _options = options;
  140. _scrollInset = inset;
  141. }
  142. return self;
  143. }
  144. #pragma mark - Factory Methods
  145. + (instancetype)scrollWithTitle:(NSString *)scrollTitle {
  146. return [self scrollWithTitle:scrollTitle
  147. type:TXScrollLabelViewTypeLeftRight];
  148. }
  149. + (instancetype)scrollWithTitle:(NSString *)scrollTitle
  150. type:(TXScrollLabelViewType)scrollType {
  151. return [self scrollWithTitle:scrollTitle
  152. type:scrollType
  153. velocity:TXScrollDefaultTimeInterval];
  154. }
  155. + (instancetype)scrollWithTitle:(NSString *)scrollTitle
  156. type:(TXScrollLabelViewType)scrollType
  157. velocity:(NSTimeInterval)scrollVelocity {
  158. return [self scrollWithTitle:scrollTitle
  159. type:scrollType
  160. velocity:scrollVelocity
  161. options:UIViewAnimationOptionCurveEaseInOut];
  162. }
  163. + (instancetype)scrollWithTitle:(NSString *)scrollTitle
  164. type:(TXScrollLabelViewType)scrollType
  165. velocity:(NSTimeInterval)scrollVelocity
  166. options:(UIViewAnimationOptions)options {
  167. return [self scrollWithTitle:scrollTitle
  168. type:scrollType
  169. velocity:scrollVelocity
  170. options:options
  171. inset:UIEdgeInsetsMake(0, 5, 0, 5)];
  172. }
  173. + (instancetype)scrollWithTitle:(NSString *)scrollTitle
  174. type:(TXScrollLabelViewType)scrollType
  175. velocity:(NSTimeInterval)scrollVelocity
  176. options:(UIViewAnimationOptions)options
  177. inset:(UIEdgeInsets)inset {
  178. return [[self alloc] initWithTitle:scrollTitle
  179. type:scrollType
  180. velocity:scrollVelocity
  181. options:options
  182. inset:inset];
  183. }
  184. #pragma mark - Deprecated Getter & Setter Methods
  185. /*************WILL BE REMOVED IN THE FUTURE.****************************/
  186. - (void)setTx_scrollTitle:(NSString *)tx_scrollTitle {
  187. self.scrollTitle = tx_scrollTitle;
  188. }
  189. - (void)setTx_scrollType:(TXScrollLabelViewType)tx_scrollType {
  190. self.scrollType = tx_scrollType;
  191. }
  192. - (void)setTx_scrollVelocity:(NSTimeInterval)tx_scrollVelocity {
  193. self.scrollVelocity = tx_scrollVelocity;
  194. }
  195. - (void)setTx_scrollContentSize:(CGRect)tx_scrollContentSize{
  196. _tx_scrollContentSize = tx_scrollContentSize;
  197. self.frame = _tx_scrollContentSize;
  198. }
  199. - (void)setTx_scrollTitleColor:(UIColor *)tx_scrollTitleColor {
  200. self.scrollTitleColor = tx_scrollTitleColor;
  201. }
  202. /*************ALL ABOVE.*******************************************/
  203. #pragma mark - Getter & Setter Methods
  204. - (void)setScrollTitle:(NSString *)scrollTitle {
  205. _scrollTitle = scrollTitle;
  206. // self.scrollArray = nil;
  207. [self resetScrollLabelView];
  208. }
  209. - (void)setScrollType:(TXScrollLabelViewType)scrollType {
  210. if (_scrollType == scrollType) return;
  211. _scrollType = scrollType;
  212. self.scrollVelocity = _scrollVelocity;
  213. [self resetScrollLabelView];
  214. }
  215. - (void)setScrollVelocity:(NSTimeInterval)scrollVelocity {
  216. CGFloat velocity = scrollVelocity;
  217. if (scrollVelocity < 0.1) {
  218. velocity = 0.1;
  219. }else if (scrollVelocity > 10) {
  220. velocity = 10;
  221. }
  222. if (_scrollType == TXScrollLabelViewTypeLeftRight || _scrollType == TXScrollLabelViewTypeUpDown) {
  223. _scrollVelocity = velocity / 30.0;
  224. }else {
  225. _scrollVelocity = velocity;
  226. }
  227. }
  228. - (UIViewAnimationOptions)options {
  229. if (_options) return _options;
  230. return _options = UIViewAnimationOptionCurveEaseInOut;
  231. }
  232. - (void)setScrollTitleColor:(UIColor *)scrollTitleColor {
  233. _scrollTitleColor = scrollTitleColor;
  234. [self setupTextColor:scrollTitleColor];
  235. }
  236. - (void)setScrollInset:(UIEdgeInsets)scrollInset {
  237. _scrollInset = scrollInset;
  238. [self setupSubviewsLayout];
  239. }
  240. - (void)setScrollSpace:(CGFloat)scrollSpace {
  241. _scrollSpace = scrollSpace;
  242. [self setupSubviewsLayout];
  243. }
  244. - (CGFloat)scrollSpace {
  245. if (_scrollSpace) return _scrollSpace;
  246. return 0.f;
  247. }
  248. - (NSArray *)scrollArray {
  249. if (_scrollArray) return _scrollArray;
  250. if (_scrollTexts.count) {
  251. return _scrollArray = _scrollTexts;
  252. }
  253. return _scrollArray = [self getSeparatedLinesFromLabel];
  254. }
  255. - (void)setFrame:(CGRect)frame {
  256. [super setFrame:frame];
  257. [self setupSubviewsLayout];
  258. }
  259. - (void)setTextAlignment:(NSTextAlignment)textAlignment {
  260. _textAlignment = textAlignment;
  261. self.upLabel.textAlignment = textAlignment;
  262. self.downLabel.textAlignment = textAlignment;
  263. }
  264. - (void)setFont:(UIFont *)font {
  265. _font = font;
  266. self.upLabel.font = font;
  267. self.downLabel.font = font;
  268. [self setupSubviewsLayout];
  269. }
  270. - (UIFont *)font {
  271. if (_font) return _font;
  272. return TXScrollLabelFont;
  273. }
  274. #pragma mark - Custom Methods
  275. // Component initial
  276. - (void)setupInitial {
  277. switch (_scrollType) {
  278. case TXScrollLabelViewTypeLeftRight:
  279. [self updateTextForScrollViewWithSEL:@selector(updateLeftRightScrollLabelLayoutWithText:labelType:)];
  280. break;
  281. case TXScrollLabelViewTypeUpDown:
  282. [self updateTextForScrollViewWithSEL:@selector(updateUpDownScrollLabelLayoutWithText:labelType:)];
  283. break;
  284. case TXScrollLabelViewTypeFlipRepeat:
  285. case TXScrollLabelViewTypeFlipNoRepeat:
  286. // TODO
  287. break;
  288. default:
  289. @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"TXScrollLabelViewType unrecognized in -[TXScrollLabelView setupInitial]" userInfo:nil];
  290. break;
  291. }
  292. }
  293. /** 重置滚动视图 */
  294. - (void)resetScrollLabelView {
  295. [self endup];//停止滚动
  296. [self setupSubviewsLayout];//重新布局
  297. [self startup];//开始滚动
  298. }
  299. - (void)setupTextColor:(UIColor *)color {
  300. self.upLabel.textColor = color;
  301. self.downLabel.textColor = color;
  302. }
  303. - (void)setupTitle:(NSString *)title {
  304. // self.upLabel.text = title;
  305. // self.downLabel.text = title;
  306. }
  307. - (void)setupAttributeTitle:(NSAttributedString *)attributeTitle {
  308. _scrollTitle = attributeTitle.string;
  309. [self setupSubviewsLayout];
  310. self.upLabel.attributedText = attributeTitle;
  311. self.downLabel.attributedText = attributeTitle;
  312. }
  313. #pragma mark - SubviewsLayout Methods
  314. - (void)setupSubviewsLayout {
  315. switch (_scrollType) {
  316. case TXScrollLabelViewTypeLeftRight:
  317. if (self.isArray) {
  318. [self setupInitial];
  319. }else {
  320. [self setupSubviewsLayout_LeftRight];
  321. }
  322. break;
  323. case TXScrollLabelViewTypeUpDown:
  324. if (self.isArray) {
  325. [self setupInitial];
  326. }else {
  327. [self setupSubviewsLayout_UpDown];
  328. }
  329. break;
  330. case TXScrollLabelViewTypeFlipRepeat: {
  331. [self setupSubviewsLayout_Flip];
  332. [self setupTitle:_scrollTitle];
  333. }
  334. break;
  335. case TXScrollLabelViewTypeFlipNoRepeat:
  336. [self setupSubviewsLayout_Flip];
  337. break;
  338. default:
  339. break;
  340. }
  341. }
  342. - (void)setupSubviewsLayout_LeftRight {
  343. CGFloat labelMaxH = self.tx_height;//最大高度
  344. CGFloat labelMaxW = 0;//无限宽
  345. CGFloat labelH = labelMaxH;//label实际高度
  346. __block CGFloat labelW = 0;//label宽度,有待计算
  347. self.contentOffset = CGPointZero;
  348. [self setupLRUDTypeLayoutWithMaxSize:CGSizeMake(labelMaxW, labelMaxH) width:labelW height:labelH completedHandler:^(CGSize size) {
  349. labelW = MAX(size.width, self.tx_width);
  350. //开始布局
  351. self.upLabel.frame = CGRectMake(_scrollInset.left, 0, labelW, labelH);
  352. //由于 TXScrollLabelViewTypeLeftRight\UpDown 类型 X\Y 值均不一样,此处不再block中处理!
  353. self.downLabel.frame = CGRectMake(CGRectGetMaxX(self.upLabel.frame) + self.scrollSpace, 0, labelW, labelH);
  354. }];
  355. }
  356. - (void)setupSubviewsLayout_UpDown {
  357. CGFloat labelMaxH = 0;
  358. CGFloat labelMaxW = self.tx_width - _scrollInset.left - _scrollInset.right;
  359. CGFloat labelW = labelMaxW;
  360. __block CGFloat labelH = 0;
  361. [self setupLRUDTypeLayoutWithMaxSize:CGSizeMake(labelMaxW, labelMaxH) width:labelW height:labelH completedHandler:^(CGSize size) {
  362. labelH = MAX(size.height, self.tx_height);
  363. self.upLabel.frame = CGRectMake(_scrollInset.left, 0, labelW, labelH);
  364. self.downLabel.frame = CGRectMake(_scrollInset.left, CGRectGetMaxY(self.upLabel.frame) + self.scrollSpace, labelW, labelH);
  365. }];
  366. }
  367. - (void)setupSubviewsLayout_Flip {
  368. CGFloat labelW = self.tx_width - _scrollInset.left - _scrollInset.right;
  369. CGFloat labelX = _scrollInset.left;
  370. self.upLabel.frame = CGRectMake(labelX, 0, labelW, self.tx_height);
  371. self.downLabel.frame = CGRectMake(labelX, CGRectGetMaxY(self.upLabel.frame), labelW, self.tx_height);
  372. }
  373. - (void)setupLRUDTypeLayoutWithMaxSize:(CGSize)size
  374. width:(CGFloat)width
  375. height:(CGFloat)height
  376. completedHandler:(void(^)(CGSize size))completedHandler {
  377. CGSize scrollLabelS = [_scrollTitle boundingRectWithSize:size
  378. options:NSStringDrawingUsesLineFragmentOrigin
  379. attributes:@{NSFontAttributeName: self.font} context:nil].size;
  380. //回调获取布局数据
  381. completedHandler(scrollLabelS);
  382. if (!self.isArray) {
  383. [self setupTitle:_scrollTitle];
  384. }
  385. }
  386. - (void)setupLRUDTypeLayoutWithTitle:(NSString *)title
  387. maxSize:(CGSize)size
  388. width:(CGFloat)width
  389. height:(CGFloat)height
  390. completedHandler:(void(^)(CGSize size))completedHandler {
  391. CGSize scrollLabelS = [title boundingRectWithSize:size
  392. options:NSStringDrawingUsesLineFragmentOrigin
  393. attributes:@{NSFontAttributeName: self.font} context:nil].size;
  394. //回调获取布局数据
  395. completedHandler(scrollLabelS);
  396. }
  397. /**
  398. update the frame of scrollLabel. Just layout
  399. @param text scrollText
  400. @param type scrollLabel type
  401. */
  402. - (void)updateLeftRightScrollLabelLayoutWithText:(NSString *)text labelType:(TXScrollLabelType)type {
  403. CGFloat labelMaxH = self.tx_height;//最大高度
  404. CGFloat labelMaxW = 0;//无限宽
  405. CGFloat labelH = labelMaxH;//label实际高度
  406. __block CGFloat labelW = 0;//label宽度,有待计算
  407. [self setupLRUDTypeLayoutWithTitle:text maxSize:CGSizeMake(labelMaxW, labelMaxH) width:labelW height:labelH completedHandler:^(CGSize size) {
  408. labelW = MAX(size.width, self.tx_width);
  409. //开始布局
  410. if (type == TXScrollLabelTypeUp) {
  411. self.upLabel.frame = CGRectMake(_scrollInset.left, 0, labelW, labelH);
  412. }else if (type == TXScrollLabelTypeDown) {
  413. self.downLabel.frame = CGRectMake(CGRectGetMaxX(self.upLabel.frame) + self.scrollSpace, 0, labelW, labelH);
  414. }
  415. }];
  416. }
  417. /**
  418. The same as "-updateLeftRightScrollLabelLayoutWithText:labelType:"
  419. */
  420. - (void)updateUpDownScrollLabelLayoutWithText:(NSString *)text labelType:(TXScrollLabelType)type {
  421. CGFloat labelMaxH = 0;
  422. CGFloat labelMaxW = self.tx_width - _scrollInset.left - _scrollInset.right;
  423. CGFloat labelW = labelMaxW;
  424. __block CGFloat labelH = 0;
  425. [self setupLRUDTypeLayoutWithTitle:text maxSize:CGSizeMake(labelMaxW, labelMaxH) width:labelW height:labelH completedHandler:^(CGSize size) {
  426. labelH = MAX(size.height, self.tx_height);
  427. if (type == TXScrollLabelTypeUp) {
  428. self.upLabel.frame = CGRectMake(_scrollInset.left, 0, labelW, labelH);
  429. }else if (type == TXScrollLabelTypeDown) {
  430. self.downLabel.frame = CGRectMake(_scrollInset.left, CGRectGetMaxY(self.upLabel.frame) + self.scrollSpace, labelW, labelH);
  431. }
  432. }];
  433. }
  434. #pragma mark - Scrolling Operation Methods -- Public
  435. - (void)beginScrolling {
  436. self.currentSentence = 0;
  437. if (self.isArray) {
  438. [self setupInitial];
  439. }
  440. [self startup];
  441. }
  442. - (void)endScrolling {
  443. [self endup];
  444. }
  445. - (void)pauseScrolling {
  446. [self endup];
  447. }
  448. #pragma mark - Scrolling Operation Methods -- Private
  449. - (void)endup {
  450. [self.scrollTimer invalidate];
  451. self.scrollTimer = nil;
  452. self.scrollArray = nil;
  453. }
  454. - (void)startup {
  455. if (!self.scrollTitle.length && !self.scrollArray.count) return;
  456. [self endup];
  457. if (_scrollType == TXScrollLabelViewTypeFlipRepeat || _scrollType == TXScrollLabelViewTypeFlipNoRepeat) {
  458. _firstTime = YES;
  459. if (_scrollType == TXScrollLabelViewTypeFlipNoRepeat) {
  460. [self setupTitle:[self.scrollArray firstObject]];//初次显示
  461. }
  462. [self startWithVelocity:1];
  463. }else {
  464. [self startWithVelocity:self.scrollVelocity];
  465. }
  466. }
  467. //开始计时
  468. - (void)startWithVelocity:(NSTimeInterval)velocity {
  469. // if (!self.scrollTitle.length) return;
  470. if (!self.scrollTitle.length && self.scrollArray.count) return;
  471. __weak typeof(self) weakSelf = self;
  472. self.scrollTimer = [NSTimer tx_scheduledTimerWithTimeInterval:velocity repeat:YES block:^(NSTimer *timer) {
  473. TXScrollLabelView *strongSelf = weakSelf;
  474. if (strongSelf) {
  475. [strongSelf updateScrolling];
  476. }
  477. }];
  478. [[NSRunLoop mainRunLoop] addTimer:self.scrollTimer forMode:NSRunLoopCommonModes];
  479. }
  480. #pragma mark - Scrolling Animation Methods
  481. - (void)updateScrolling {
  482. switch (self.scrollType) {
  483. case TXScrollLabelViewTypeLeftRight:
  484. [self updateScrollingType_LeftRight];
  485. break;
  486. case TXScrollLabelViewTypeUpDown:
  487. [self updateScrollingType_UpDown];
  488. break;
  489. case TXScrollLabelViewTypeFlipRepeat:
  490. [self updateScrollingType_FlipRepeat];
  491. break;
  492. case TXScrollLabelViewTypeFlipNoRepeat:
  493. [self updateScrollingType_FlipNoRepeat];
  494. break;
  495. default:
  496. break;
  497. }
  498. }
  499. #pragma mark - ScrollLabelView + Methods
  500. - (void)updateScrollingType_LeftRight {
  501. if (self.contentOffset.x >= (_scrollInset.left + self.upLabel.tx_width + self.scrollSpace)) {
  502. /** 更新 Label.text */
  503. if ((self.contentOffset.x > (_scrollInset.left + self.upLabel.tx_width) - self.tx_width) &&
  504. self.isArray) {
  505. [self updateTextForScrollViewWithSEL:@selector(updateLeftRightScrollLabelLayoutWithText:labelType:)];
  506. }
  507. [self endup];
  508. self.contentOffset = CGPointMake(_scrollInset.left + 1, 0);//x增加偏移量,防止卡顿
  509. [self startup];
  510. }else {
  511. self.contentOffset = CGPointMake(self.contentOffset.x + 1, self.contentOffset.y);
  512. }
  513. }
  514. - (void)updateScrollingType_UpDown {
  515. if (self.contentOffset.y >= (self.upLabel.tx_height + self.scrollSpace)) {
  516. /** 更新 Label.text */
  517. if ((self.contentOffset.y >= (self.upLabel.tx_height)) &&
  518. self.isArray) {
  519. [self updateTextForScrollViewWithSEL:@selector(updateUpDownScrollLabelLayoutWithText:labelType:)];
  520. }
  521. [self endup];
  522. self.contentOffset = CGPointMake(0, 2);//y增加偏移量,防止卡顿
  523. [self startup];
  524. }else {
  525. self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + 1);
  526. }
  527. }
  528. - (void)updateScrollingType_FlipRepeat {
  529. [self updateRepeatTypeWithOperation:^(NSTimeInterval velocity) {
  530. [self flipAnimationWithDelay:velocity];
  531. }];
  532. }
  533. - (void)updateScrollingType_FlipNoRepeat {
  534. [self updateRepeatTypeWithOperation:^(NSTimeInterval velocity) {
  535. [self flipNoCleAnimationWithDelay:velocity];
  536. }];
  537. }
  538. - (void)updateRepeatTypeWithOperation:(void(^)(NSTimeInterval))operation {
  539. NSTimeInterval velocity = self.scrollVelocity;
  540. if (self.isFirstTime) {
  541. _firstTime = NO;
  542. [self endup];
  543. [self startWithVelocity:velocity];
  544. }
  545. operation(velocity);
  546. }
  547. - (void)flipAnimationWithDelay:(NSTimeInterval)delay {
  548. [UIView transitionWithView:self.upLabel duration:delay * 0.5 options:self.options animations:^{
  549. self.upLabel.tx_bottom = 0;
  550. [UIView transitionWithView:self.upLabel duration:delay * 0.5 options:self.options animations:^{
  551. self.downLabel.tx_y = 0;
  552. } completion:^(BOOL finished) {
  553. self.upLabel.tx_y = self.tx_height;
  554. TXScrollLabel *tempLabel = self.upLabel;
  555. self.upLabel = self.downLabel;
  556. self.downLabel = tempLabel;
  557. }];
  558. } completion:nil];
  559. }
  560. /**
  561. Execute flip animation.
  562. @param delay animation duration.
  563. */
  564. - (void)flipNoCleAnimationWithDelay:(NSTimeInterval)delay {
  565. if (!self.scrollArray.count) return;
  566. /** 更新文本 */
  567. [self updateScrollText];
  568. /** 执行翻滚动画 */
  569. [self flipAnimationWithDelay:delay];
  570. }
  571. #pragma mark - Params For Array
  572. void (*setter)(id, SEL, NSString *, TXScrollLabelType);
  573. - (void)updateTextForScrollViewWithSEL:(SEL)sel {
  574. if (!self.scrollArray.count) return;
  575. /** 更新文本 */
  576. [self updateScrollText];
  577. /** 执行 SEL */
  578. setter = (void (*)(id, SEL, NSString *, TXScrollLabelType))[self methodForSelector:sel];
  579. setter(self, sel, self.upLabel.text, TXScrollLabelTypeUp);
  580. setter(self, sel, self.downLabel.text, TXScrollLabelTypeDown);
  581. }
  582. - (void)updateScrollText {
  583. NSInteger currentSentence = self.currentSentence;
  584. if (currentSentence >= self.scrollArray.count) currentSentence = 0;
  585. self.upLabel.text = self.scrollArray[currentSentence];
  586. currentSentence ++;
  587. if (currentSentence >= self.scrollArray.count) currentSentence = 0;
  588. self.downLabel.text = self.scrollArray[currentSentence];
  589. self.currentSentence = currentSentence;
  590. }
  591. #pragma mark - Text-Separator
  592. -(NSArray *)getSeparatedLinesFromLabel {
  593. if (!_scrollTitle.length) return nil;
  594. NSString *text = _scrollTitle;
  595. UIFont *font = self.font;
  596. CTFontRef myFont = CTFontCreateWithName((__bridge CFStringRef)([font fontName]), [font pointSize], NULL);
  597. NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
  598. [attStr addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)myFont range:NSMakeRange(0, attStr.length)];
  599. CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attStr);
  600. CGMutablePathRef path = CGPathCreateMutable();
  601. CGPathAddRect(path, NULL, CGRectMake(0,0,self.upLabel.tx_width,100000));
  602. CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
  603. NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
  604. NSMutableArray *linesArray = [[NSMutableArray alloc]init];
  605. for (id line in lines) {
  606. CTLineRef lineRef = (__bridge CTLineRef )line;
  607. CFRange lineRange = CTLineGetStringRange(lineRef);
  608. NSRange range = NSMakeRange(lineRange.location, lineRange.length);
  609. NSString *lineString = [text substringWithRange:range];
  610. [linesArray addObject:lineString];
  611. }
  612. return (NSArray *)linesArray;
  613. }
  614. - (void)dealloc {
  615. [self endup];
  616. }
  617. @end
  618. @implementation TXScrollLabelView (TXArray)
  619. #pragma mark - Array Methods
  620. - (instancetype)initWithTextArray:(NSArray *)scrollTexts
  621. type:(TXScrollLabelViewType)scrollType
  622. velocity:(NSTimeInterval)scrollVelocity
  623. options:(UIViewAnimationOptions)options
  624. inset:(UIEdgeInsets)inset {
  625. if (self = [super init]) {
  626. self.isArray = YES;
  627. _scrollTexts = [scrollTexts copy];
  628. _scrollTitle = [_scrollTexts firstObject];
  629. _scrollType = scrollType;
  630. self.scrollVelocity = scrollVelocity;
  631. _options = options;
  632. _scrollInset = inset;
  633. }
  634. return self;
  635. }
  636. + (instancetype)scrollWithTextArray:(NSArray *)scrollTexts
  637. type:(TXScrollLabelViewType)scrollType
  638. velocity:(NSTimeInterval)scrollVelocity
  639. options:(UIViewAnimationOptions)options
  640. inset:(UIEdgeInsets)inset {
  641. return [[self alloc] initWithTextArray:scrollTexts
  642. type:scrollType
  643. velocity:scrollVelocity
  644. options:options
  645. inset:inset];
  646. }
  647. @end
  648. @implementation TXScrollLabelView (TXScrollLabelViewDeprecated)
  649. + (instancetype)tx_setScrollTitle:(NSString *)scrollTitle {
  650. return [self scrollWithTitle:scrollTitle];
  651. }
  652. + (instancetype)tx_setScrollTitle:(NSString *)scrollTitle
  653. scrollType:(TXScrollLabelViewType)scrollType {
  654. return [self scrollWithTitle:scrollTitle
  655. type:scrollType];
  656. }
  657. + (instancetype)tx_setScrollTitle:(NSString *)scrollTitle
  658. scrollType:(TXScrollLabelViewType)scrollType
  659. scrollVelocity:(NSTimeInterval)scrollVelocity {
  660. return [self scrollWithTitle:scrollTitle
  661. type:scrollType
  662. velocity:scrollVelocity];
  663. }
  664. + (instancetype)tx_setScrollTitle:(NSString *)scrollTitle
  665. scrollType:(TXScrollLabelViewType)scrollType
  666. scrollVelocity:(NSTimeInterval)scrollVelocity
  667. options:(UIViewAnimationOptions)options {
  668. return [self scrollWithTitle:scrollTitle
  669. type:scrollType
  670. velocity:scrollVelocity
  671. options:options];
  672. }
  673. + (instancetype)tx_setScrollTitle:(NSString *)scrollTitle
  674. scrollType:(TXScrollLabelViewType)scrollType
  675. scrollVelocity:(NSTimeInterval)scrollVelocity
  676. options:(UIViewAnimationOptions)options
  677. inset:(UIEdgeInsets)inset {
  678. return [self scrollWithTitle:scrollTitle
  679. type:scrollType
  680. velocity:scrollVelocity
  681. options:options
  682. inset:inset];
  683. }
  684. @end
  685. @implementation UIView (TXAdditions)
  686. - (void)addTapGesture:(id)target sel:(SEL)selector {
  687. self.userInteractionEnabled = YES;
  688. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:target action:selector];
  689. [self addGestureRecognizer:tap];
  690. }
  691. @end