YOUPAIOCBarrageRenderView.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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 "YOUPAIOCBarrageRenderView.h"
  10. #import "YOUPAIOCBarrageTrackInfo.h"
  11. @implementation YOUPAIOCBarrageRenderView
  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 YOUPAIOCBarrageCell *)dequeueReusableCellWithClass:(Class)barrageCellClass {
  35. YOUPAIOCBarrageCell *barrageCell = nil;
  36. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  37. for (YOUPAIOCBarrageCell *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:[YOUPAIOCBarrageCell class]]) {
  51. return nil;
  52. }
  53. return barrageCell;
  54. }
  55. - (YOUPAIOCBarrageCell *)newCellWithClass:(Class)barrageCellClass {
  56. YOUPAIOCBarrageCell *barrageCell = [[barrageCellClass alloc] init];
  57. if (![barrageCell isKindOfClass:[YOUPAIOCBarrageCell 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. YOUPAIOCBarrageCell *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. YOUPAIOCBarrageCell *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. YOUPAIOCBarrageCell *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)youpaiffireBarrageCell:(YOUPAIOCBarrageCell *)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:[YOUPAIOCBarrageCell class]]) {
  186. return;
  187. }
  188. [barrageCell youpaifclearContents];
  189. [barrageCell youpaifupdateSubviewsData];
  190. [barrageCell youpaiflayoutContentSubviews];
  191. [barrageCell youpaifconvertContentToImage];
  192. [barrageCell sizeToFit];
  193. [barrageCell removeSubViewsAndSublayers];
  194. [barrageCell youpaifaddBorderAttributes];
  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 youpaifaddBarrageAnimationWithDelegate:self];
  204. [self recordTrackInfoWithBarrageCell:barrageCell];
  205. _lastestCell = barrageCell;
  206. }
  207. - (void)addBarrageCell:(YOUPAIOCBarrageCell *)barrageCell WithPositionPriority:(OCBarragePositionPriority)positionPriority {
  208. switch (positionPriority) {
  209. case OCBarragePositionMiddle: {
  210. [self insertSubview:barrageCell aboveSubview:_middlePositionView];
  211. _middlePositionView.userInteractionEnabled = YES;
  212. }
  213. break;
  214. case OCBarragePositionHigh: {
  215. [self insertSubview:barrageCell aboveSubview:_highPositionView];
  216. _highPositionView.userInteractionEnabled = YES;
  217. }
  218. break;
  219. case OCBarragePositionVeryHigh: {
  220. [self insertSubview:barrageCell belowSubview:_veryHighPositionView];
  221. }
  222. break;
  223. default: {
  224. [self insertSubview:barrageCell belowSubview:_lowPositionView];
  225. }
  226. break;
  227. }
  228. }
  229. - (CGRect)calculationBarrageCellFrame:(YOUPAIOCBarrageCell *)barrageCell {
  230. CGRect cellFrame = barrageCell.bounds;
  231. cellFrame.origin.x = CGRectGetMaxX(self.frame);
  232. if (![[NSValue valueWithRange:barrageCell.barrageDescriptor.renderRange] isEqualToValue:[NSValue valueWithRange:NSMakeRange(0, 0)]]) {
  233. CGFloat cellHeight = CGRectGetHeight(barrageCell.bounds);
  234. CGFloat minOriginY = barrageCell.barrageDescriptor.renderRange.location;
  235. CGFloat maxOriginY = barrageCell.barrageDescriptor.renderRange.length;
  236. if (maxOriginY > CGRectGetHeight(self.bounds)) {
  237. maxOriginY = CGRectGetHeight(self.bounds);
  238. }
  239. if (minOriginY < 0) {
  240. minOriginY = 0;
  241. }
  242. CGFloat renderHeight = maxOriginY - minOriginY;
  243. if (renderHeight < 0) {
  244. renderHeight = cellHeight;
  245. }
  246. int trackCount = floorf(renderHeight/cellHeight);
  247. int trackIndex = arc4random_uniform(trackCount);//用户改变行高(比如弹幕文字大小不会引起显示bug, 因为虽然是同一个类, 但是trackCount变小了, 所以不会出现trackIndex*cellHeight超出屏幕边界的情况)
  248. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  249. YOUPAIOCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), trackIndex)];
  250. if (trackInfo && trackInfo.nextAvailableTime > CACurrentMediaTime()) {//当前行暂不可用
  251. NSMutableArray *availableTrackInfos = [NSMutableArray array];
  252. for (YOUPAIOCBarrageTrackInfo *info in _trackNextAvailableTime.allValues) {
  253. if (CACurrentMediaTime() > info.nextAvailableTime && [info.trackIdentifier containsString:NSStringFromClass([barrageCell class])]) {//只在同类弹幕中判断是否有可用的轨道
  254. [availableTrackInfos addObject:info];
  255. }
  256. }
  257. if (availableTrackInfos.count > 0) {
  258. YOUPAIOCBarrageTrackInfo *randomInfo = [availableTrackInfos objectAtIndex:arc4random_uniform((int)availableTrackInfos.count)];
  259. trackIndex = randomInfo.trackIndex;
  260. } else {
  261. if (_trackNextAvailableTime.count < trackCount) {//刚开始不是每一条轨道都跑过弹幕, 还有空轨道
  262. NSMutableArray *numberArray = [NSMutableArray array];
  263. for (int index = 0; index < trackCount; index++) {
  264. YOUPAIOCBarrageTrackInfo *emptyTrackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), index)];
  265. if (!emptyTrackInfo) {
  266. [numberArray addObject:[NSNumber numberWithInt:index]];
  267. }
  268. }
  269. if (numberArray.count > 0) {
  270. trackIndex = [[numberArray objectAtIndex:arc4random_uniform((int)numberArray.count)] intValue];
  271. }
  272. }
  273. //真的是没有可用的轨道了
  274. }
  275. }
  276. dispatch_semaphore_signal(_trackInfoLock);
  277. barrageCell.trackIndex = trackIndex;
  278. cellFrame.origin.y = trackIndex*cellHeight+minOriginY;
  279. } else {
  280. switch (self.renderPositionStyle) {
  281. case OCBarrageRenderPositionRandom: {
  282. CGFloat maxY = CGRectGetHeight(self.bounds) - CGRectGetHeight(cellFrame);
  283. int originY = floorl(maxY);
  284. cellFrame.origin.y = arc4random_uniform(originY);
  285. }
  286. break;
  287. case OCBarrageRenderPositionIncrease: {
  288. if (_lastestCell) {
  289. CGRect lastestFrame = _lastestCell.frame;
  290. cellFrame.origin.y = CGRectGetMaxY(lastestFrame);
  291. } else {
  292. cellFrame.origin.y = 0.0;
  293. }
  294. }
  295. break;
  296. default: {
  297. CGFloat renderViewHeight = CGRectGetHeight(self.bounds);
  298. CGFloat cellHeight = CGRectGetHeight(barrageCell.bounds);
  299. int trackCount = floorf(renderViewHeight/cellHeight);
  300. int trackIndex = arc4random_uniform(trackCount);//用户改变行高(比如弹幕文字大小不会引起显示bug, 因为虽然是同一个类, 但是trackCount变小了, 所以不会出现trackIndex*cellHeight超出屏幕边界的情况)
  301. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  302. YOUPAIOCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), trackIndex)];
  303. if (trackInfo && trackInfo.nextAvailableTime > CACurrentMediaTime()) {//当前行暂不可用
  304. NSMutableArray *availableTrackInfos = [NSMutableArray array];
  305. for (YOUPAIOCBarrageTrackInfo *info in _trackNextAvailableTime.allValues) {
  306. if (CACurrentMediaTime() > info.nextAvailableTime && [info.trackIdentifier containsString:NSStringFromClass([barrageCell class])]) {//只在同类弹幕中判断是否有可用的轨道
  307. [availableTrackInfos addObject:info];
  308. }
  309. }
  310. if (availableTrackInfos.count > 0) {
  311. YOUPAIOCBarrageTrackInfo *randomInfo = [availableTrackInfos objectAtIndex:arc4random_uniform((int)availableTrackInfos.count)];
  312. trackIndex = randomInfo.trackIndex;
  313. } else {
  314. if (_trackNextAvailableTime.count < trackCount) {//刚开始不是每一条轨道都跑过弹幕, 还有空轨道
  315. NSMutableArray *numberArray = [NSMutableArray array];
  316. for (int index = 0; index < trackCount; index++) {
  317. YOUPAIOCBarrageTrackInfo *emptyTrackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), index)];
  318. if (!emptyTrackInfo) {
  319. [numberArray addObject:[NSNumber numberWithInt:index]];
  320. }
  321. }
  322. if (numberArray.count > 0) {
  323. trackIndex = [[numberArray objectAtIndex:arc4random_uniform((int)numberArray.count)] intValue];
  324. }
  325. }
  326. //真的是没有可用的轨道了
  327. }
  328. }
  329. dispatch_semaphore_signal(_trackInfoLock);
  330. barrageCell.trackIndex = trackIndex;
  331. cellFrame.origin.y = trackIndex*cellHeight;
  332. }
  333. break;
  334. }
  335. }
  336. if (CGRectGetMaxY(cellFrame) > CGRectGetHeight(self.bounds)) {
  337. cellFrame.origin.y = 0.0; //超过底部, 回到顶部
  338. } else if (cellFrame.origin.y < 0) {
  339. cellFrame.origin.y = 0.0;
  340. }
  341. return cellFrame;
  342. }
  343. - (void)clearIdleCells {
  344. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  345. NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970];
  346. NSEnumerator *enumerator = [self.idleCells reverseObjectEnumerator];
  347. YOUPAIOCBarrageCell *cell;
  348. while (cell = [enumerator nextObject]){
  349. CGFloat time = timeInterval - cell.idleTime;
  350. if (time > 5.0 && cell.idleTime > 0) {
  351. [self.idleCells removeObject:cell];
  352. }
  353. }
  354. if (self.idleCells.count == 0) {
  355. _autoClear = NO;
  356. } else {
  357. [self performSelector:@selector(clearIdleCells) withObject:nil afterDelay:5.0];
  358. }
  359. dispatch_semaphore_signal(_idleCellsLock);
  360. }
  361. - (void)recordTrackInfoWithBarrageCell:(YOUPAIOCBarrageCell *)barrageCell {
  362. NSString *nextAvalibleTimeKey = kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), barrageCell.trackIndex);
  363. CFTimeInterval duration = barrageCell.barrageAnimation.duration;
  364. NSValue *fromValue = nil;
  365. NSValue *toValue = nil;
  366. if ([barrageCell.barrageAnimation isKindOfClass:[CABasicAnimation class]]) {
  367. fromValue = [(CABasicAnimation *)barrageCell.barrageAnimation fromValue];
  368. toValue = [(CABasicAnimation *)barrageCell.barrageAnimation toValue];
  369. } else if ([barrageCell.barrageAnimation isKindOfClass:[CAKeyframeAnimation class]]) {
  370. fromValue = [[(CAKeyframeAnimation *)barrageCell.barrageAnimation values] firstObject];
  371. toValue = [[(CAKeyframeAnimation *)barrageCell.barrageAnimation values] lastObject];
  372. } else {
  373. }
  374. const char *fromeValueType = [fromValue objCType];
  375. const char *toValueType = [toValue objCType];
  376. if (!fromeValueType || !toValueType) {
  377. return;
  378. }
  379. NSString *fromeValueTypeString = [NSString stringWithCString:fromeValueType encoding:NSUTF8StringEncoding];
  380. NSString *toValueTypeString = [NSString stringWithCString:toValueType encoding:NSUTF8StringEncoding];
  381. if (![fromeValueTypeString isEqualToString:toValueTypeString]) {
  382. return;
  383. }
  384. if ([fromeValueTypeString containsString:@"CGPoint"]) {
  385. CGPoint fromPoint = [fromValue CGPointValue];
  386. CGPoint toPoint = [toValue CGPointValue];
  387. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  388. YOUPAIOCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:nextAvalibleTimeKey];
  389. if (!trackInfo) {
  390. trackInfo = [[YOUPAIOCBarrageTrackInfo alloc] init];
  391. trackInfo.trackIdentifier = nextAvalibleTimeKey;
  392. trackInfo.trackIndex = barrageCell.trackIndex;
  393. }
  394. trackInfo.barrageCount++;
  395. trackInfo.nextAvailableTime = CGRectGetWidth(barrageCell.bounds);
  396. CGFloat distanceX = fabs(toPoint.x - fromPoint.x);
  397. CGFloat distanceY = fabs(toPoint.y - fromPoint.y);
  398. CGFloat distance = MAX(distanceX, distanceY);
  399. CGFloat speed = distance/duration;
  400. if (distanceX == distance) {
  401. CFTimeInterval time = CGRectGetWidth(barrageCell.bounds)/speed;
  402. trackInfo.nextAvailableTime = CACurrentMediaTime() + time + 0.1;//多加一点时间
  403. [_trackNextAvailableTime setValue:trackInfo forKey:nextAvalibleTimeKey];
  404. } else if (distanceY == distance) {
  405. // CFTimeInterval time = CGRectGetHeight(barrageCell.bounds)/speed;
  406. } else {
  407. }
  408. dispatch_semaphore_signal(_trackInfoLock);
  409. return;
  410. } else if ([fromeValueTypeString containsString:@"CGVector"]) {
  411. return;
  412. } else if ([fromeValueTypeString containsString:@"CGSize"]) {
  413. return;
  414. } else if ([fromeValueTypeString containsString:@"CGRect"]) {
  415. return;
  416. } else if ([fromeValueTypeString containsString:@"CGAffineTransform"]) {
  417. return;
  418. } else if ([fromeValueTypeString containsString:@"UIEdgeInsets"]) {
  419. return;
  420. } else if ([fromeValueTypeString containsString:@"UIOffset"]) {
  421. return;
  422. }
  423. }
  424. #pragma mark ----- CAAnimationDelegate
  425. - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
  426. if (!flag) {
  427. return;
  428. }
  429. if (self.renderStatus == OCBarrageRenderStoped) {
  430. return;
  431. }
  432. YOUPAIOCBarrageCell *animationedCell = nil;
  433. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  434. for (YOUPAIOCBarrageCell *cell in self.animatingCells) {
  435. CAAnimation *barrageAnimation = [cell barrageAnimation];
  436. if (barrageAnimation == anim) {
  437. animationedCell = cell;
  438. [self.animatingCells removeObject:cell];
  439. break;
  440. }
  441. }
  442. dispatch_semaphore_signal(_animatingCellsLock);
  443. if (!animationedCell) {
  444. return;
  445. }
  446. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  447. YOUPAIOCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([animationedCell class]), animationedCell.trackIndex)];
  448. if (trackInfo) {
  449. trackInfo.barrageCount--;
  450. }
  451. dispatch_semaphore_signal(_trackInfoLock);
  452. [animationedCell removeFromSuperview];
  453. [animationedCell prepareForReuse];
  454. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  455. animationedCell.idleTime = [[NSDate date] timeIntervalSince1970];
  456. [self.idleCells addObject:animationedCell];
  457. dispatch_semaphore_signal(_idleCellsLock);
  458. if (!_autoClear) {
  459. [self performSelector:@selector(clearIdleCells) withObject:nil afterDelay:5.0];
  460. _autoClear = YES;
  461. }
  462. }
  463. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  464. if (event.type == UIEventTypeTouches) {
  465. UITouch *touch = [touches.allObjects firstObject];
  466. CGPoint touchPoint = [touch locationInView:self];
  467. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  468. NSInteger count = self.animatingCells.count;
  469. for (int i = 0; i < count; i++) {
  470. YOUPAIOCBarrageCell *barrageCell = [self.animatingCells objectAtIndex:i];
  471. if ([barrageCell.layer.presentationLayer hitTest:touchPoint]) {
  472. if (barrageCell.barrageDescriptor.touchAction) {
  473. barrageCell.barrageDescriptor.touchAction(barrageCell.barrageDescriptor);
  474. }
  475. if (barrageCell.barrageDescriptor.cellTouchedAction) {
  476. barrageCell.barrageDescriptor.cellTouchedAction(barrageCell.barrageDescriptor, barrageCell);
  477. }
  478. break;
  479. }
  480. }
  481. dispatch_semaphore_signal(_animatingCellsLock);
  482. }
  483. }
  484. #pragma mark ----- getter
  485. - (NSMutableArray<YOUPAIOCBarrageCell *> *)animatingCells {
  486. if (!_animatingCells) {
  487. _animatingCells = [[NSMutableArray alloc] init];
  488. }
  489. return _animatingCells;
  490. }
  491. - (NSMutableArray<YOUPAIOCBarrageCell *> *)idleCells {
  492. if (!_idleCells) {
  493. _idleCells = [[NSMutableArray alloc] init];
  494. }
  495. return _idleCells;
  496. }
  497. - (OCBarrageRenderStatus)renderStatus {
  498. return _renderStatus;
  499. }
  500. //add by leo 穿透
  501. -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
  502. {
  503. //木有开启交互 然后 3.从后往前遍历自己的子控件
  504. NSInteger count = self.subviews.count;
  505. for (NSInteger i = count - 1; i >= 0; i--) {
  506. UIView *childView = self.subviews[i];
  507. // 把当前控件上的坐标系转换成子控件上的坐标系
  508. CGPoint childP = [self convertPoint:point toView:childView];
  509. UIView *fitView = [childView hitTest:childP withEvent:event];
  510. if (fitView) { // 寻找到最合适的view
  511. return fitView;
  512. }
  513. }
  514. return [super hitTest:point withEvent:event];
  515. }
  516. @end