LMJHorizontalScrollText.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. //
  2. // LMJHorizontalScrollText.m
  3. // LMJHorizontalScrollTextExample
  4. //
  5. // Created by LiMingjie on 2019/8/22.
  6. // Copyright © 2019 LMJ. All rights reserved.
  7. //
  8. #import "LMJHorizontalScrollText.h"
  9. @interface LMJHorizontalScrollText()
  10. @property (nonatomic, strong) UILabel * contentLabel1;
  11. @property (nonatomic, strong) UILabel * contentLabel2;
  12. @property (nonatomic, assign) CGFloat textWidth;
  13. @property (nonatomic, assign) int wanderingOffset;
  14. @property (nonatomic, strong) NSTimer * timer;
  15. @end
  16. @implementation LMJHorizontalScrollText
  17. {
  18. CGFloat _selfWidth;
  19. CGFloat _selfHeight;
  20. }
  21. #pragma mark - Init
  22. - (id)init{
  23. self = [super init];
  24. if (self) {
  25. self.frame = CGRectMake(0, 0, 100, 20); // 设置一个初始的frame
  26. }
  27. return self;
  28. }
  29. - (id)initWithFrame:(CGRect)frame{
  30. self = [super initWithFrame:frame];
  31. if (self) {
  32. [self setInitialSettings];
  33. }
  34. return self;
  35. }
  36. - (void)awakeFromNib{
  37. [super awakeFromNib];
  38. [self setInitialSettings];
  39. }
  40. - (void)layoutSubviews{
  41. _selfWidth = self.frame.size.width;
  42. _selfHeight = self.frame.size.height;
  43. }
  44. - (void)setInitialSettings {
  45. self.clipsToBounds = YES;
  46. _contentLabel1 = nil;
  47. _contentLabel2 = nil;
  48. _text = @"";
  49. _textFont = [UIFont systemFontOfSize:12];
  50. _textColor = [UIColor blackColor];
  51. _speed = 0.03;
  52. _textWidth = 0;
  53. _wanderingOffset = -1;
  54. _moveMode = LMJTextScrollWandering;
  55. _moveDirection = LMJTextScrollMoveLeft;
  56. _timer = nil;
  57. _selfWidth = self.frame.size.width;
  58. _selfHeight = self.frame.size.height;
  59. }
  60. #pragma mark - Set
  61. - (void)setText:(NSString *)text{
  62. if ([_text isEqualToString: text]) return;
  63. _text = text;
  64. [self updateTextWidth];
  65. [self updateLabelsFrame];
  66. if (_contentLabel1 != nil) {
  67. _contentLabel1.text = _text;
  68. }
  69. if (_contentLabel2 != nil) {
  70. _contentLabel2.text = _text;
  71. }
  72. }
  73. - (void)setTextFont:(UIFont *)textFont{
  74. if (_textFont == textFont) return;
  75. _textFont = textFont;
  76. [self updateTextWidth];
  77. [self updateLabelsFrame];
  78. if (_contentLabel1 != nil) {
  79. _contentLabel1.font = _textFont;
  80. }
  81. if (_contentLabel2 != nil) {
  82. _contentLabel2.font = _textFont;
  83. }
  84. }
  85. - (void)setTextColor:(UIColor *)textColor{
  86. _textColor = textColor;
  87. if (_contentLabel1 != nil) {
  88. _contentLabel1.textColor = _textColor;
  89. }
  90. if (_contentLabel2 != nil) {
  91. _contentLabel2.textColor = _textColor;
  92. }
  93. }
  94. - (void)setSpeed:(CGFloat)speed{
  95. _speed = speed;
  96. [self move];
  97. }
  98. - (void)setMoveMode:(LMJTextScrollMode)moveMode {
  99. _moveMode = moveMode;
  100. // if (self.text.length == 0) {//如果字符串长度为0,直接返回
  101. // return;
  102. // }
  103. if (_timer) {
  104. [_timer invalidate];
  105. _timer = nil;
  106. }
  107. [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  108. [obj removeFromSuperview];
  109. obj = nil;
  110. }];
  111. switch (self.moveMode) {
  112. case LMJTextScrollContinuous:
  113. {
  114. if (self.moveDirection == LMJTextScrollMoveLeft) {
  115. [self creatLabel1WithFrame1:CGRectMake(0, 0, _textWidth, _selfHeight) andLabel2WithFrame2:CGRectMake(_textWidth, 0, _textWidth, _selfHeight)];
  116. }else{
  117. [self creatLabel1WithFrame1:CGRectMake(_selfWidth -_textWidth, 0, _textWidth, _selfHeight) andLabel2WithFrame2:CGRectMake(_selfWidth -_textWidth -_textWidth, 0, _textWidth, _selfHeight)];
  118. }
  119. }break;
  120. case LMJTextScrollIntermittent:
  121. {
  122. if (self.moveDirection == LMJTextScrollMoveLeft) {
  123. [self creatLabel1WithFrame:CGRectMake(0, 0, _textWidth, _selfHeight)];
  124. }else{
  125. [self creatLabel1WithFrame:CGRectMake(_selfWidth -_textWidth, 0, _textWidth, _selfHeight)];
  126. }
  127. }break;
  128. case LMJTextScrollFromOutside:
  129. {
  130. if (self.moveDirection == LMJTextScrollMoveLeft) {
  131. [self creatLabel1WithFrame:CGRectMake(_selfWidth, 0, _textWidth, _selfHeight)];
  132. }else{
  133. [self creatLabel1WithFrame:CGRectMake(_textWidth, 0, _textWidth, _selfHeight)];
  134. }
  135. }break;
  136. case LMJTextScrollWandering:
  137. {
  138. [self creatLabel1WithFrame:CGRectMake(0, 0, _textWidth, _selfHeight)];
  139. }break;
  140. default:
  141. break;
  142. }
  143. if (!_timer) {
  144. [self move];
  145. }
  146. }
  147. #pragma mark - Create Labels
  148. -(void)creatLabel1WithFrame:(CGRect)frame{
  149. _contentLabel1 = [[UILabel alloc] initWithFrame:frame];
  150. _contentLabel1.text = self.text;
  151. _contentLabel1.font = self.textFont;
  152. _contentLabel1.textColor = self.textColor;
  153. _contentLabel1.tag = 1001;
  154. _contentLabel1.backgroundColor = [UIColor clearColor];
  155. [self addSubview:_contentLabel1];
  156. if (_contentLabel2) {
  157. [_contentLabel2 removeFromSuperview];
  158. _contentLabel2 = nil;
  159. }
  160. }
  161. -(void)creatLabel1WithFrame1:(CGRect)frame1 andLabel2WithFrame2:(CGRect)frame2{
  162. [self creatLabel1WithFrame:frame1];
  163. _contentLabel2 = [[UILabel alloc] initWithFrame:frame2];
  164. _contentLabel2.text = self.text;
  165. _contentLabel2.font = self.textFont;
  166. _contentLabel2.textColor = self.textColor;
  167. _contentLabel2.tag = 1002;
  168. _contentLabel2.backgroundColor = [UIColor clearColor];
  169. [self addSubview:_contentLabel2];
  170. }
  171. #pragma mark - Util
  172. - (void)updateTextWidth {
  173. _textWidth = [self.text sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:self.textFont, NSFontAttributeName, nil]].width;
  174. }
  175. - (void)updateLabelsFrame {
  176. CGFloat label1_x = _contentLabel1.frame.origin.x;
  177. CGFloat label2_x = _contentLabel2.frame.origin.x;
  178. if (_contentLabel1 && _contentLabel2) {
  179. if (label1_x < label2_x) {
  180. _contentLabel1.frame = CGRectMake(label1_x, 0, _textWidth, _selfHeight);
  181. _contentLabel2.frame = CGRectMake(label1_x + _textWidth, 0, _textWidth, _selfHeight);
  182. } else {
  183. _contentLabel2.frame = CGRectMake(label2_x, 0, _textWidth, _selfHeight);
  184. _contentLabel1.frame = CGRectMake(label2_x + _textWidth, 0, _textWidth, _selfHeight);
  185. }
  186. } else {
  187. if (_contentLabel1) {
  188. _contentLabel1.frame = CGRectMake(label1_x, 0, _textWidth, _selfHeight);
  189. }
  190. if (_contentLabel2) {
  191. _contentLabel2.frame = CGRectMake(label2_x, 0, _textWidth, _selfHeight);
  192. }
  193. }
  194. }
  195. - (void)resetLabelsPosition {
  196. switch (self.moveMode) {
  197. case LMJTextScrollContinuous:
  198. {
  199. if (self.moveDirection == LMJTextScrollMoveLeft) {
  200. _contentLabel1.frame = CGRectMake(0, 0, _textWidth, _selfHeight);
  201. _contentLabel2.frame = CGRectMake(_textWidth, 0, _textWidth, _selfHeight);
  202. }else{
  203. _contentLabel1.frame = CGRectMake(_selfWidth -_textWidth, 0, _textWidth, _selfHeight);
  204. _contentLabel2.frame = CGRectMake(_selfWidth -_textWidth -_textWidth, 0, _textWidth, _selfHeight);
  205. }
  206. }break;
  207. case LMJTextScrollIntermittent:
  208. {
  209. if (self.moveDirection == LMJTextScrollMoveLeft) {
  210. _contentLabel1.frame = CGRectMake(0, 0, _textWidth, _selfHeight);
  211. }else{
  212. _contentLabel2.frame = CGRectMake(_selfWidth -_textWidth, 0, _textWidth, _selfHeight);
  213. }
  214. }break;
  215. case LMJTextScrollFromOutside:
  216. {
  217. if (self.moveDirection == LMJTextScrollMoveLeft) {
  218. _contentLabel1.frame = CGRectMake(_selfWidth, 0, _textWidth, _selfHeight);
  219. }else{
  220. _contentLabel1.frame = CGRectMake(_textWidth, 0, _textWidth, _selfHeight);
  221. }
  222. }break;
  223. case LMJTextScrollWandering:
  224. {
  225. _contentLabel1.frame = CGRectMake(0, 0, _textWidth, _selfHeight);
  226. }break;
  227. default:
  228. break;
  229. }
  230. }
  231. #pragma mark - Move Action
  232. // LMJTextScrollContinuous
  233. -(void)moveContinuous{
  234. if (self.moveDirection == LMJTextScrollMoveLeft) {
  235. _contentLabel1.frame = CGRectMake(_contentLabel1.frame.origin.x -1, 0, _textWidth, _selfHeight);
  236. _contentLabel2.frame = CGRectMake(_contentLabel2.frame.origin.x -1, 0, _textWidth, _selfHeight);
  237. if (_contentLabel1.frame.origin.x < -_textWidth) {
  238. _contentLabel1.frame = CGRectMake(_contentLabel2.frame.origin.x + _textWidth, 0, _textWidth, _selfHeight);
  239. }
  240. if (_contentLabel2.frame.origin.x < -_textWidth) {
  241. _contentLabel2.frame = CGRectMake(_contentLabel1.frame.origin.x + _textWidth, 0, _textWidth, _selfHeight);
  242. }
  243. }else{
  244. _contentLabel1.frame = CGRectMake(_contentLabel1.frame.origin.x +1, 0, _textWidth, _selfHeight);
  245. _contentLabel2.frame = CGRectMake(_contentLabel2.frame.origin.x +1, 0, _textWidth, _selfHeight);
  246. if (_contentLabel1.frame.origin.x > _selfWidth) {
  247. _contentLabel1.frame = CGRectMake(_contentLabel2.frame.origin.x - _textWidth, 0, _textWidth, _selfHeight);
  248. }
  249. if (_contentLabel2.frame.origin.x > _selfWidth) {
  250. _contentLabel2.frame = CGRectMake(_contentLabel1.frame.origin.x - _textWidth, 0, _textWidth, _selfHeight);
  251. }
  252. }
  253. }
  254. // LMJTextScrollIntermittent
  255. -(void)moveIntermittent{
  256. if (self.moveDirection == LMJTextScrollMoveLeft) {
  257. _contentLabel1.frame = CGRectMake(_contentLabel1.frame.origin.x -1, 0, _textWidth, _selfHeight);
  258. if (_contentLabel1.frame.origin.x < -_textWidth) {
  259. _contentLabel1.frame = CGRectMake(0, 0, _textWidth, _selfHeight);
  260. }
  261. }else{
  262. _contentLabel1.frame = CGRectMake(_contentLabel1.frame.origin.x +1, 0, _textWidth, _selfHeight);
  263. if (_contentLabel1.frame.origin.x > _selfWidth) {
  264. _contentLabel1.frame = CGRectMake(_selfWidth -_textWidth, 0, _textWidth, _selfHeight);
  265. }
  266. }
  267. }
  268. // LMJTextScrollFromOutside
  269. -(void)moveFromOutside{
  270. if (self.moveDirection == LMJTextScrollMoveLeft) {
  271. _contentLabel1.frame = CGRectMake(_contentLabel1.frame.origin.x -1, 0, _textWidth, _selfHeight);
  272. if (_contentLabel1.frame.origin.x < -_textWidth) {
  273. _contentLabel1.frame = CGRectMake(_selfWidth, 0, _textWidth, _selfHeight);
  274. }
  275. }else{
  276. _contentLabel1.frame = CGRectMake(_contentLabel1.frame.origin.x +1, 0, _textWidth, _selfHeight);
  277. if (_contentLabel1.frame.origin.x > _selfWidth) {
  278. _contentLabel1.frame = CGRectMake(-_textWidth, 0, _textWidth, _selfHeight);
  279. }
  280. }
  281. }
  282. // LMJTextScrollWandering
  283. -(void)moveWandering{
  284. if (_textWidth > _selfWidth) {
  285. _contentLabel1.frame = CGRectMake(_contentLabel1.frame.origin.x + _wanderingOffset, 0, _textWidth, _selfHeight);
  286. if (_contentLabel1.frame.origin.x < -(_textWidth -_selfWidth +2)) {
  287. _wanderingOffset = 1;
  288. }
  289. if (_contentLabel1.frame.origin.x > 2) {
  290. _wanderingOffset = -1;
  291. }
  292. }else if (_textWidth < _selfWidth){
  293. _contentLabel1.frame = CGRectMake(_contentLabel1.frame.origin.x + _wanderingOffset, 0, _textWidth, _selfHeight);
  294. if (_contentLabel1.frame.origin.x < 0) {
  295. _wanderingOffset = 1;
  296. }
  297. if (_contentLabel1.frame.origin.x > _selfWidth - _textWidth) {
  298. _wanderingOffset = -1;
  299. }
  300. }
  301. }
  302. #pragma mark - API Methods
  303. - (void)move {
  304. if (_timer != nil) {
  305. [_timer invalidate];
  306. _timer = nil;
  307. }
  308. switch (self.moveMode) {
  309. case LMJTextScrollContinuous:
  310. {
  311. _timer = [NSTimer scheduledTimerWithTimeInterval:self.speed target:self selector:@selector(moveContinuous) userInfo:nil repeats:YES];
  312. }
  313. break;
  314. case LMJTextScrollIntermittent:
  315. {
  316. _timer = [NSTimer scheduledTimerWithTimeInterval:self.speed target:self selector:@selector(moveIntermittent) userInfo:nil repeats:YES];
  317. }
  318. break;
  319. case LMJTextScrollFromOutside:
  320. {
  321. _timer = [NSTimer scheduledTimerWithTimeInterval:self.speed target:self selector:@selector(moveFromOutside) userInfo:nil repeats:YES];
  322. }
  323. break;
  324. case LMJTextScrollWandering:
  325. {
  326. _timer = [NSTimer scheduledTimerWithTimeInterval:self.speed target:self selector:@selector(moveWandering) userInfo:nil repeats:YES];
  327. }
  328. break;
  329. default:
  330. break;
  331. }
  332. [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
  333. }
  334. - (void)stop {
  335. [_timer invalidate];
  336. _timer = nil;
  337. [self resetLabelsPosition];
  338. }
  339. - (void)dealloc {
  340. [_timer invalidate];
  341. _timer = nil;
  342. }
  343. @end