OCBarrageRenderView.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. //
  2. // OCBarrageContentView.m
  3. // TestApp
  4. //
  5. // Created by QMTV on 2017/8/22.
  6. // Copyright © 2017年 LFC. All rights reserved.
  7. //
  8. #define kNextAvailableTimeKey(identifier, index) [NSString stringWithFormat:@"%@_%d", identifier, index]
  9. #import "OCBarrageRenderView.h"
  10. #import "OCBarrageTrackInfo.h"
  11. @implementation OCBarrageRenderView
  12. - (void)dealloc {
  13. NSLog(@"%s", __func__);
  14. }
  15. - (instancetype)init {
  16. self = [super init];
  17. if (self) {
  18. _animatingCellsLock = dispatch_semaphore_create(1);
  19. _idleCellsLock = dispatch_semaphore_create(1);
  20. _trackInfoLock = dispatch_semaphore_create(1);
  21. _lowPositionView = [[UIView alloc] init];
  22. [self addSubview:_lowPositionView];
  23. _middlePositionView = [[UIView alloc] init];
  24. [self addSubview:_middlePositionView];
  25. _highPositionView = [[UIView alloc] init];
  26. [self addSubview:_highPositionView];
  27. _veryHighPositionView = [[UIView alloc] init];
  28. [self addSubview:_veryHighPositionView];
  29. self.layer.masksToBounds = YES;
  30. _trackNextAvailableTime = [NSMutableDictionary dictionary];
  31. }
  32. return self;
  33. }
  34. - (nullable OCBarrageCell *)dequeueReusableCellWithClass:(Class)barrageCellClass {
  35. OCBarrageCell *barrageCell = nil;
  36. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  37. for (OCBarrageCell *cell in self.idleCells) {
  38. if ([NSStringFromClass([cell class]) isEqualToString:NSStringFromClass(barrageCellClass)]) {
  39. barrageCell = cell;
  40. break;
  41. }
  42. }
  43. if (barrageCell) {
  44. [self.idleCells removeObject:barrageCell];
  45. barrageCell.idleTime = 0.0;
  46. } else {
  47. barrageCell = [self newCellWithClass:barrageCellClass];
  48. }
  49. dispatch_semaphore_signal(_idleCellsLock);
  50. if (![barrageCell isKindOfClass:[OCBarrageCell class]]) {
  51. return nil;
  52. }
  53. return barrageCell;
  54. }
  55. - (OCBarrageCell *)newCellWithClass:(Class)barrageCellClass {
  56. OCBarrageCell *barrageCell = [[barrageCellClass alloc] init];
  57. if (![barrageCell isKindOfClass:[OCBarrageCell class]]) {
  58. return nil;
  59. }
  60. return barrageCell;
  61. }
  62. - (void)start {
  63. switch (self.renderStatus) {
  64. case OCBarrageRenderStarted: {
  65. return;
  66. }
  67. break;
  68. case OCBarrageRenderPaused: {
  69. [self resume];
  70. return;
  71. }
  72. break;
  73. default: {
  74. _renderStatus = OCBarrageRenderStarted;
  75. }
  76. break;
  77. }
  78. }
  79. - (void)pause {
  80. switch (self.renderStatus) {
  81. case OCBarrageRenderStarted: {
  82. _renderStatus = OCBarrageRenderPaused;
  83. }
  84. break;
  85. case OCBarrageRenderPaused: {
  86. return;
  87. }
  88. break;
  89. default: {
  90. return;
  91. }
  92. break;
  93. }
  94. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  95. NSEnumerator *enumerator = [self.animatingCells reverseObjectEnumerator];
  96. OCBarrageCell *cell = nil;
  97. while (cell = [enumerator nextObject]){
  98. CFTimeInterval pausedTime = [cell.layer convertTime:CACurrentMediaTime() fromLayer:nil];
  99. cell.layer.speed = 0.0;
  100. cell.layer.timeOffset = pausedTime;
  101. }
  102. dispatch_semaphore_signal(_animatingCellsLock);
  103. }
  104. - (void)resume {
  105. switch (self.renderStatus) {
  106. case OCBarrageRenderStarted: {
  107. return;
  108. }
  109. break;
  110. case OCBarrageRenderPaused: {
  111. _renderStatus = OCBarrageRenderStarted;
  112. }
  113. break;
  114. default: {
  115. return;
  116. }
  117. break;
  118. }
  119. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  120. NSEnumerator *enumerator = [self.animatingCells reverseObjectEnumerator];
  121. OCBarrageCell *cell = nil;
  122. while (cell = [enumerator nextObject]){
  123. CFTimeInterval pausedTime = cell.layer.timeOffset;
  124. cell.layer.speed = 1.0;
  125. cell.layer.timeOffset = 0.0;
  126. cell.layer.beginTime = 0.0;
  127. CFTimeInterval timeSincePause = [cell.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
  128. cell.layer.beginTime = timeSincePause;
  129. }
  130. dispatch_semaphore_signal(_animatingCellsLock);
  131. }
  132. - (void)stop {
  133. switch (self.renderStatus) {
  134. case OCBarrageRenderStarted: {
  135. _renderStatus = OCBarrageRenderStoped;
  136. }
  137. break;
  138. case OCBarrageRenderPaused: {
  139. _renderStatus = OCBarrageRenderStoped;
  140. }
  141. break;
  142. default: {
  143. return;
  144. }
  145. break;
  146. }
  147. if (_autoClear) {
  148. [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(clearIdleCells) object:nil];
  149. }
  150. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  151. NSEnumerator *animatingEnumerator = [self.animatingCells reverseObjectEnumerator];
  152. OCBarrageCell *animatingCell = nil;
  153. while (animatingCell = [animatingEnumerator nextObject]){
  154. CFTimeInterval pausedTime = [animatingCell.layer convertTime:CACurrentMediaTime() fromLayer:nil];
  155. animatingCell.layer.speed = 0.0;
  156. animatingCell.layer.timeOffset = pausedTime;
  157. [animatingCell.layer removeAllAnimations];
  158. [animatingCell removeFromSuperview];
  159. }
  160. [self.animatingCells removeAllObjects];
  161. dispatch_semaphore_signal(_animatingCellsLock);
  162. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  163. [self.idleCells removeAllObjects];
  164. dispatch_semaphore_signal(_idleCellsLock);
  165. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  166. [_trackNextAvailableTime removeAllObjects];
  167. dispatch_semaphore_signal(_trackInfoLock);
  168. }
  169. - (void)fireBarrageCell:(OCBarrageCell *)barrageCell {
  170. switch (self.renderStatus) {
  171. case OCBarrageRenderStarted: {
  172. }
  173. break;
  174. case OCBarrageRenderPaused: {
  175. return;
  176. }
  177. break;
  178. default:
  179. return;
  180. break;
  181. }
  182. if (!barrageCell) {
  183. return;
  184. }
  185. if (![barrageCell isKindOfClass:[OCBarrageCell class]]) {
  186. return;
  187. }
  188. [barrageCell clearContents];
  189. [barrageCell updateSubviewsData];
  190. [barrageCell layoutContentSubviews];
  191. [barrageCell convertContentToImage];
  192. [barrageCell sizeToFit];
  193. [barrageCell removeSubViewsAndSublayers];
  194. [barrageCell addBorderAttributes];
  195. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  196. _lastestCell = [self.animatingCells lastObject];
  197. [self.animatingCells addObject:barrageCell];
  198. barrageCell.idle = NO;
  199. dispatch_semaphore_signal(_animatingCellsLock);
  200. [self addBarrageCell:barrageCell WithPositionPriority:barrageCell.barrageDescriptor.positionPriority];
  201. CGRect cellFrame = [self calculationBarrageCellFrame:barrageCell];
  202. barrageCell.frame = cellFrame;
  203. [barrageCell addBarrageAnimationWithDelegate:self];
  204. [self recordTrackInfoWithBarrageCell:barrageCell];
  205. _lastestCell = barrageCell;
  206. }
  207. - (void)addBarrageCell:(OCBarrageCell *)barrageCell WithPositionPriority:(OCBarragePositionPriority)positionPriority {
  208. switch (positionPriority) {
  209. case OCBarragePositionMiddle: {
  210. [self insertSubview:barrageCell aboveSubview:_middlePositionView];
  211. }
  212. break;
  213. case OCBarragePositionHigh: {
  214. [self insertSubview:barrageCell belowSubview:_highPositionView];
  215. }
  216. break;
  217. case OCBarragePositionVeryHigh: {
  218. [self insertSubview:barrageCell belowSubview:_veryHighPositionView];
  219. }
  220. break;
  221. default: {
  222. [self insertSubview:barrageCell belowSubview:_lowPositionView];
  223. }
  224. break;
  225. }
  226. }
  227. - (CGRect)calculationBarrageCellFrame:(OCBarrageCell *)barrageCell {
  228. CGRect cellFrame = barrageCell.bounds;
  229. cellFrame.origin.x = CGRectGetMaxX(self.frame);
  230. if (![[NSValue valueWithRange:barrageCell.barrageDescriptor.renderRange] isEqualToValue:[NSValue valueWithRange:NSMakeRange(0, 0)]]) {
  231. CGFloat cellHeight = CGRectGetHeight(barrageCell.bounds);
  232. CGFloat minOriginY = barrageCell.barrageDescriptor.renderRange.location;
  233. CGFloat maxOriginY = barrageCell.barrageDescriptor.renderRange.length;
  234. if (maxOriginY > CGRectGetHeight(self.bounds)) {
  235. maxOriginY = CGRectGetHeight(self.bounds);
  236. }
  237. if (minOriginY < 0) {
  238. minOriginY = 0;
  239. }
  240. CGFloat renderHeight = maxOriginY - minOriginY;
  241. if (renderHeight < 0) {
  242. renderHeight = cellHeight;
  243. }
  244. int trackCount = floorf(renderHeight/cellHeight);
  245. int trackIndex = arc4random_uniform(trackCount);//用户改变行高(比如弹幕文字大小不会引起显示bug, 因为虽然是同一个类, 但是trackCount变小了, 所以不会出现trackIndex*cellHeight超出屏幕边界的情况)
  246. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  247. OCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), trackIndex)];
  248. if (trackInfo && trackInfo.nextAvailableTime > CACurrentMediaTime()) {//当前行暂不可用
  249. NSMutableArray *availableTrackInfos = [NSMutableArray array];
  250. for (OCBarrageTrackInfo *info in _trackNextAvailableTime.allValues) {
  251. if (CACurrentMediaTime() > info.nextAvailableTime && [info.trackIdentifier containsString:NSStringFromClass([barrageCell class])]) {//只在同类弹幕中判断是否有可用的轨道
  252. [availableTrackInfos addObject:info];
  253. }
  254. }
  255. if (availableTrackInfos.count > 0) {
  256. OCBarrageTrackInfo *randomInfo = [availableTrackInfos objectAtIndex:arc4random_uniform((int)availableTrackInfos.count)];
  257. trackIndex = randomInfo.trackIndex;
  258. } else {
  259. if (_trackNextAvailableTime.count < trackCount) {//刚开始不是每一条轨道都跑过弹幕, 还有空轨道
  260. NSMutableArray *numberArray = [NSMutableArray array];
  261. for (int index = 0; index < trackCount; index++) {
  262. OCBarrageTrackInfo *emptyTrackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), index)];
  263. if (!emptyTrackInfo) {
  264. [numberArray addObject:[NSNumber numberWithInt:index]];
  265. }
  266. }
  267. if (numberArray.count > 0) {
  268. trackIndex = [[numberArray objectAtIndex:arc4random_uniform((int)numberArray.count)] intValue];
  269. }
  270. }
  271. //真的是没有可用的轨道了
  272. }
  273. }
  274. dispatch_semaphore_signal(_trackInfoLock);
  275. barrageCell.trackIndex = trackIndex;
  276. cellFrame.origin.y = trackIndex*cellHeight+minOriginY;
  277. } else {
  278. switch (self.renderPositionStyle) {
  279. case OCBarrageRenderPositionRandom: {
  280. CGFloat maxY = CGRectGetHeight(self.bounds) - CGRectGetHeight(cellFrame);
  281. int originY = floorl(maxY);
  282. cellFrame.origin.y = arc4random_uniform(originY);
  283. }
  284. break;
  285. case OCBarrageRenderPositionIncrease: {
  286. if (_lastestCell) {
  287. CGRect lastestFrame = _lastestCell.frame;
  288. cellFrame.origin.y = CGRectGetMaxY(lastestFrame);
  289. } else {
  290. cellFrame.origin.y = 0.0;
  291. }
  292. }
  293. break;
  294. default: {
  295. CGFloat renderViewHeight = CGRectGetHeight(self.bounds);
  296. CGFloat cellHeight = CGRectGetHeight(barrageCell.bounds);
  297. int trackCount = floorf(renderViewHeight/cellHeight);
  298. int trackIndex = arc4random_uniform(trackCount);//用户改变行高(比如弹幕文字大小不会引起显示bug, 因为虽然是同一个类, 但是trackCount变小了, 所以不会出现trackIndex*cellHeight超出屏幕边界的情况)
  299. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  300. OCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), trackIndex)];
  301. if (trackInfo && trackInfo.nextAvailableTime > CACurrentMediaTime()) {//当前行暂不可用
  302. NSMutableArray *availableTrackInfos = [NSMutableArray array];
  303. for (OCBarrageTrackInfo *info in _trackNextAvailableTime.allValues) {
  304. if (CACurrentMediaTime() > info.nextAvailableTime && [info.trackIdentifier containsString:NSStringFromClass([barrageCell class])]) {//只在同类弹幕中判断是否有可用的轨道
  305. [availableTrackInfos addObject:info];
  306. }
  307. }
  308. if (availableTrackInfos.count > 0) {
  309. OCBarrageTrackInfo *randomInfo = [availableTrackInfos objectAtIndex:arc4random_uniform((int)availableTrackInfos.count)];
  310. trackIndex = randomInfo.trackIndex;
  311. } else {
  312. if (_trackNextAvailableTime.count < trackCount) {//刚开始不是每一条轨道都跑过弹幕, 还有空轨道
  313. NSMutableArray *numberArray = [NSMutableArray array];
  314. for (int index = 0; index < trackCount; index++) {
  315. OCBarrageTrackInfo *emptyTrackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), index)];
  316. if (!emptyTrackInfo) {
  317. [numberArray addObject:[NSNumber numberWithInt:index]];
  318. }
  319. }
  320. if (numberArray.count > 0) {
  321. trackIndex = [[numberArray objectAtIndex:arc4random_uniform((int)numberArray.count)] intValue];
  322. }
  323. }
  324. //真的是没有可用的轨道了
  325. }
  326. }
  327. dispatch_semaphore_signal(_trackInfoLock);
  328. barrageCell.trackIndex = trackIndex;
  329. cellFrame.origin.y = trackIndex*cellHeight;
  330. }
  331. break;
  332. }
  333. }
  334. if (CGRectGetMaxY(cellFrame) > CGRectGetHeight(self.bounds)) {
  335. cellFrame.origin.y = 0.0; //超过底部, 回到顶部
  336. } else if (cellFrame.origin.y < 0) {
  337. cellFrame.origin.y = 0.0;
  338. }
  339. return cellFrame;
  340. }
  341. - (void)clearIdleCells {
  342. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  343. NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970];
  344. NSEnumerator *enumerator = [self.idleCells reverseObjectEnumerator];
  345. OCBarrageCell *cell;
  346. while (cell = [enumerator nextObject]){
  347. CGFloat time = timeInterval - cell.idleTime;
  348. if (time > 5.0 && cell.idleTime > 0) {
  349. [self.idleCells removeObject:cell];
  350. }
  351. }
  352. if (self.idleCells.count == 0) {
  353. _autoClear = NO;
  354. } else {
  355. [self performSelector:@selector(clearIdleCells) withObject:nil afterDelay:5.0];
  356. }
  357. dispatch_semaphore_signal(_idleCellsLock);
  358. }
  359. - (void)recordTrackInfoWithBarrageCell:(OCBarrageCell *)barrageCell {
  360. NSString *nextAvalibleTimeKey = kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), barrageCell.trackIndex);
  361. CFTimeInterval duration = barrageCell.barrageAnimation.duration;
  362. NSValue *fromValue = nil;
  363. NSValue *toValue = nil;
  364. if ([barrageCell.barrageAnimation isKindOfClass:[CABasicAnimation class]]) {
  365. fromValue = [(CABasicAnimation *)barrageCell.barrageAnimation fromValue];
  366. toValue = [(CABasicAnimation *)barrageCell.barrageAnimation toValue];
  367. } else if ([barrageCell.barrageAnimation isKindOfClass:[CAKeyframeAnimation class]]) {
  368. fromValue = [[(CAKeyframeAnimation *)barrageCell.barrageAnimation values] firstObject];
  369. toValue = [[(CAKeyframeAnimation *)barrageCell.barrageAnimation values] lastObject];
  370. } else {
  371. }
  372. const char *fromeValueType = [fromValue objCType];
  373. const char *toValueType = [toValue objCType];
  374. if (!fromeValueType || !toValueType) {
  375. return;
  376. }
  377. NSString *fromeValueTypeString = [NSString stringWithCString:fromeValueType encoding:NSUTF8StringEncoding];
  378. NSString *toValueTypeString = [NSString stringWithCString:toValueType encoding:NSUTF8StringEncoding];
  379. if (![fromeValueTypeString isEqualToString:toValueTypeString]) {
  380. return;
  381. }
  382. if ([fromeValueTypeString containsString:@"CGPoint"]) {
  383. CGPoint fromPoint = [fromValue CGPointValue];
  384. CGPoint toPoint = [toValue CGPointValue];
  385. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  386. OCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:nextAvalibleTimeKey];
  387. if (!trackInfo) {
  388. trackInfo = [[OCBarrageTrackInfo alloc] init];
  389. trackInfo.trackIdentifier = nextAvalibleTimeKey;
  390. trackInfo.trackIndex = barrageCell.trackIndex;
  391. }
  392. trackInfo.barrageCount++;
  393. trackInfo.nextAvailableTime = CGRectGetWidth(barrageCell.bounds);
  394. CGFloat distanceX = fabs(toPoint.x - fromPoint.x);
  395. CGFloat distanceY = fabs(toPoint.y - fromPoint.y);
  396. CGFloat distance = MAX(distanceX, distanceY);
  397. CGFloat speed = distance/duration;
  398. if (distanceX == distance) {
  399. CFTimeInterval time = CGRectGetWidth(barrageCell.bounds)/speed;
  400. trackInfo.nextAvailableTime = CACurrentMediaTime() + time + 0.1;//多加一点时间
  401. [_trackNextAvailableTime setValue:trackInfo forKey:nextAvalibleTimeKey];
  402. } else if (distanceY == distance) {
  403. // CFTimeInterval time = CGRectGetHeight(barrageCell.bounds)/speed;
  404. } else {
  405. }
  406. dispatch_semaphore_signal(_trackInfoLock);
  407. return;
  408. } else if ([fromeValueTypeString containsString:@"CGVector"]) {
  409. return;
  410. } else if ([fromeValueTypeString containsString:@"CGSize"]) {
  411. return;
  412. } else if ([fromeValueTypeString containsString:@"CGRect"]) {
  413. return;
  414. } else if ([fromeValueTypeString containsString:@"CGAffineTransform"]) {
  415. return;
  416. } else if ([fromeValueTypeString containsString:@"UIEdgeInsets"]) {
  417. return;
  418. } else if ([fromeValueTypeString containsString:@"UIOffset"]) {
  419. return;
  420. }
  421. }
  422. #pragma mark ----- CAAnimationDelegate
  423. - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
  424. if (!flag) {
  425. return;
  426. }
  427. if (self.renderStatus == OCBarrageRenderStoped) {
  428. return;
  429. }
  430. OCBarrageCell *animationedCell = nil;
  431. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  432. for (OCBarrageCell *cell in self.animatingCells) {
  433. CAAnimation *barrageAnimation = [cell barrageAnimation];
  434. if (barrageAnimation == anim) {
  435. animationedCell = cell;
  436. [self.animatingCells removeObject:cell];
  437. break;
  438. }
  439. }
  440. dispatch_semaphore_signal(_animatingCellsLock);
  441. if (!animationedCell) {
  442. return;
  443. }
  444. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  445. OCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([animationedCell class]), animationedCell.trackIndex)];
  446. if (trackInfo) {
  447. trackInfo.barrageCount--;
  448. }
  449. dispatch_semaphore_signal(_trackInfoLock);
  450. [animationedCell removeFromSuperview];
  451. [animationedCell prepareForReuse];
  452. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  453. animationedCell.idleTime = [[NSDate date] timeIntervalSince1970];
  454. [self.idleCells addObject:animationedCell];
  455. dispatch_semaphore_signal(_idleCellsLock);
  456. if (!_autoClear) {
  457. [self performSelector:@selector(clearIdleCells) withObject:nil afterDelay:5.0];
  458. _autoClear = YES;
  459. }
  460. }
  461. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  462. if (event.type == UIEventTypeTouches) {
  463. UITouch *touch = [touches.allObjects firstObject];
  464. CGPoint touchPoint = [touch locationInView:self];
  465. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  466. NSInteger count = self.animatingCells.count;
  467. for (int i = 0; i < count; i++) {
  468. OCBarrageCell *barrageCell = [self.animatingCells objectAtIndex:i];
  469. if ([barrageCell.layer.presentationLayer hitTest:touchPoint]) {
  470. if (barrageCell.barrageDescriptor.touchAction) {
  471. barrageCell.barrageDescriptor.touchAction(barrageCell.barrageDescriptor);
  472. }
  473. break;
  474. }
  475. }
  476. dispatch_semaphore_signal(_animatingCellsLock);
  477. }
  478. }
  479. #pragma mark ----- getter
  480. - (NSMutableArray<OCBarrageCell *> *)animatingCells {
  481. if (!_animatingCells) {
  482. _animatingCells = [[NSMutableArray alloc] init];
  483. }
  484. return _animatingCells;
  485. }
  486. - (NSMutableArray<OCBarrageCell *> *)idleCells {
  487. if (!_idleCells) {
  488. _idleCells = [[NSMutableArray alloc] init];
  489. }
  490. return _idleCells;
  491. }
  492. - (OCBarrageRenderStatus)renderStatus {
  493. return _renderStatus;
  494. }
  495. @end