YOUPAIOCBarrageRenderView.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  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. //只在同类弹幕中判断是否有可用的轨道
  308. [availableTrackInfos addObject:info];
  309. }
  310. }
  311. if (availableTrackInfos.count > 0) {
  312. YOUPAIOCBarrageTrackInfo *randomInfo = [availableTrackInfos objectAtIndex:arc4random_uniform((int)availableTrackInfos.count)];
  313. trackIndex = randomInfo.trackIndex;
  314. } else {
  315. if (_trackNextAvailableTime.count < trackCount) {//刚开始不是每一条轨道都跑过弹幕, 还有空轨道
  316. NSMutableArray *numberArray = [NSMutableArray array];
  317. for (int index = 0; index < trackCount; index++) {
  318. YOUPAIOCBarrageTrackInfo *emptyTrackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), index)];
  319. if (!emptyTrackInfo) {
  320. [numberArray addObject:[NSNumber numberWithInt:index]];
  321. }
  322. }
  323. if (numberArray.count > 0) {
  324. trackIndex = [[numberArray objectAtIndex:arc4random_uniform((int)numberArray.count)] intValue];
  325. }
  326. }
  327. //真的是没有可用的轨道了
  328. }
  329. }
  330. dispatch_semaphore_signal(_trackInfoLock);
  331. barrageCell.trackIndex = trackIndex;
  332. cellFrame.origin.y = trackIndex*cellHeight;
  333. }
  334. break;
  335. }
  336. }
  337. if (CGRectGetMaxY(cellFrame) > CGRectGetHeight(self.bounds)) {
  338. cellFrame.origin.y = 0.0; //超过底部, 回到顶部
  339. } else if (cellFrame.origin.y < 0) {
  340. cellFrame.origin.y = 0.0;
  341. }
  342. // FIXME:写死弹幕的高度,适配花蝶
  343. cellFrame.size.height = 30;
  344. NSLog(@"输出🍀\n cellFrame %@",NSStringFromCGRect(cellFrame));
  345. return cellFrame;
  346. }
  347. - (void)clearIdleCells {
  348. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  349. NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970];
  350. NSEnumerator *enumerator = [self.idleCells reverseObjectEnumerator];
  351. YOUPAIOCBarrageCell *cell;
  352. while (cell = [enumerator nextObject]){
  353. CGFloat time = timeInterval - cell.idleTime;
  354. if (time > 5.0 && cell.idleTime > 0) {
  355. [self.idleCells removeObject:cell];
  356. }
  357. }
  358. if (self.idleCells.count == 0) {
  359. _autoClear = NO;
  360. } else {
  361. [self performSelector:@selector(clearIdleCells) withObject:nil afterDelay:5.0];
  362. }
  363. dispatch_semaphore_signal(_idleCellsLock);
  364. }
  365. - (void)recordTrackInfoWithBarrageCell:(YOUPAIOCBarrageCell *)barrageCell {
  366. NSString *nextAvalibleTimeKey = kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), barrageCell.trackIndex);
  367. CFTimeInterval duration = barrageCell.barrageAnimation.duration;
  368. NSValue *fromValue = nil;
  369. NSValue *toValue = nil;
  370. if ([barrageCell.barrageAnimation isKindOfClass:[CABasicAnimation class]]) {
  371. fromValue = [(CABasicAnimation *)barrageCell.barrageAnimation fromValue];
  372. toValue = [(CABasicAnimation *)barrageCell.barrageAnimation toValue];
  373. } else if ([barrageCell.barrageAnimation isKindOfClass:[CAKeyframeAnimation class]]) {
  374. fromValue = [[(CAKeyframeAnimation *)barrageCell.barrageAnimation values] firstObject];
  375. toValue = [[(CAKeyframeAnimation *)barrageCell.barrageAnimation values] lastObject];
  376. } else {
  377. }
  378. const char *fromeValueType = [fromValue objCType];
  379. const char *toValueType = [toValue objCType];
  380. if (!fromeValueType || !toValueType) {
  381. return;
  382. }
  383. NSString *fromeValueTypeString = [NSString stringWithCString:fromeValueType encoding:NSUTF8StringEncoding];
  384. NSString *toValueTypeString = [NSString stringWithCString:toValueType encoding:NSUTF8StringEncoding];
  385. if (![fromeValueTypeString isEqualToString:toValueTypeString]) {
  386. return;
  387. }
  388. if ([fromeValueTypeString containsString:@"CGPoint"]) {
  389. CGPoint fromPoint = [fromValue CGPointValue];
  390. CGPoint toPoint = [toValue CGPointValue];
  391. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  392. YOUPAIOCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:nextAvalibleTimeKey];
  393. if (!trackInfo) {
  394. trackInfo = [[YOUPAIOCBarrageTrackInfo alloc] init];
  395. trackInfo.trackIdentifier = nextAvalibleTimeKey;
  396. trackInfo.trackIndex = barrageCell.trackIndex;
  397. }
  398. trackInfo.barrageCount++;
  399. trackInfo.nextAvailableTime = CGRectGetWidth(barrageCell.bounds);
  400. CGFloat distanceX = fabs(toPoint.x - fromPoint.x);
  401. CGFloat distanceY = fabs(toPoint.y - fromPoint.y);
  402. CGFloat distance = MAX(distanceX, distanceY);
  403. CGFloat speed = distance/duration;
  404. if (distanceX == distance) {
  405. CFTimeInterval time = CGRectGetWidth(barrageCell.bounds)/speed;
  406. trackInfo.nextAvailableTime = CACurrentMediaTime() + time + 0.1;//多加一点时间
  407. [_trackNextAvailableTime setValue:trackInfo forKey:nextAvalibleTimeKey];
  408. } else if (distanceY == distance) {
  409. // CFTimeInterval time = CGRectGetHeight(barrageCell.bounds)/speed;
  410. } else {
  411. }
  412. dispatch_semaphore_signal(_trackInfoLock);
  413. return;
  414. } else if ([fromeValueTypeString containsString:@"CGVector"]) {
  415. return;
  416. } else if ([fromeValueTypeString containsString:@"CGSize"]) {
  417. return;
  418. } else if ([fromeValueTypeString containsString:@"CGRect"]) {
  419. return;
  420. } else if ([fromeValueTypeString containsString:@"CGAffineTransform"]) {
  421. return;
  422. } else if ([fromeValueTypeString containsString:@"UIEdgeInsets"]) {
  423. return;
  424. } else if ([fromeValueTypeString containsString:@"UIOffset"]) {
  425. return;
  426. }
  427. }
  428. #pragma mark ----- CAAnimationDelegate
  429. - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
  430. if (!flag) {
  431. return;
  432. }
  433. if (self.renderStatus == OCBarrageRenderStoped) {
  434. return;
  435. }
  436. YOUPAIOCBarrageCell *animationedCell = nil;
  437. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  438. for (YOUPAIOCBarrageCell *cell in self.animatingCells) {
  439. CAAnimation *barrageAnimation = [cell barrageAnimation];
  440. if (barrageAnimation == anim) {
  441. animationedCell = cell;
  442. [self.animatingCells removeObject:cell];
  443. break;
  444. }
  445. }
  446. dispatch_semaphore_signal(_animatingCellsLock);
  447. if (!animationedCell) {
  448. return;
  449. }
  450. dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
  451. YOUPAIOCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([animationedCell class]), animationedCell.trackIndex)];
  452. if (trackInfo) {
  453. trackInfo.barrageCount--;
  454. }
  455. dispatch_semaphore_signal(_trackInfoLock);
  456. [animationedCell removeFromSuperview];
  457. [animationedCell prepareForReuse];
  458. dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
  459. animationedCell.idleTime = [[NSDate date] timeIntervalSince1970];
  460. [self.idleCells addObject:animationedCell];
  461. dispatch_semaphore_signal(_idleCellsLock);
  462. if (!_autoClear) {
  463. [self performSelector:@selector(clearIdleCells) withObject:nil afterDelay:5.0];
  464. _autoClear = YES;
  465. }
  466. }
  467. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  468. if (event.type == UIEventTypeTouches) {
  469. UITouch *touch = [touches.allObjects firstObject];
  470. CGPoint touchPoint = [touch locationInView:self];
  471. dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
  472. NSInteger count = self.animatingCells.count;
  473. for (int i = 0; i < count; i++) {
  474. YOUPAIOCBarrageCell *barrageCell = [self.animatingCells objectAtIndex:i];
  475. if ([barrageCell.layer.presentationLayer hitTest:touchPoint]) {
  476. if (barrageCell.barrageDescriptor.touchAction) {
  477. barrageCell.barrageDescriptor.touchAction(barrageCell.barrageDescriptor);
  478. }
  479. if (barrageCell.barrageDescriptor.cellTouchedAction) {
  480. barrageCell.barrageDescriptor.cellTouchedAction(barrageCell.barrageDescriptor, barrageCell);
  481. }
  482. break;
  483. }
  484. }
  485. dispatch_semaphore_signal(_animatingCellsLock);
  486. }
  487. }
  488. #pragma mark ----- getter
  489. - (NSMutableArray<YOUPAIOCBarrageCell *> *)animatingCells {
  490. if (!_animatingCells) {
  491. _animatingCells = [[NSMutableArray alloc] init];
  492. }
  493. return _animatingCells;
  494. }
  495. - (NSMutableArray<YOUPAIOCBarrageCell *> *)idleCells {
  496. if (!_idleCells) {
  497. _idleCells = [[NSMutableArray alloc] init];
  498. }
  499. return _idleCells;
  500. }
  501. - (OCBarrageRenderStatus)renderStatus {
  502. return _renderStatus;
  503. }
  504. //add by leo 穿透
  505. -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
  506. {
  507. //木有开启交互 然后 3.从后往前遍历自己的子控件
  508. NSInteger count = self.subviews.count;
  509. for (NSInteger i = count - 1; i >= 0; i--) {
  510. UIView *childView = self.subviews[i];
  511. // 把当前控件上的坐标系转换成子控件上的坐标系
  512. CGPoint childP = [self convertPoint:point toView:childView];
  513. UIView *fitView = [childView hitTest:childP withEvent:event];
  514. if (fitView) { // 寻找到最合适的view
  515. return fitView;
  516. }
  517. }
  518. return [super hitTest:point withEvent:event];
  519. }
  520. @end