UIView+YOUPAIRCDDanmaku.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. //
  2. // UIView+YOUPAIRCDDanmaku.m
  3. // DanMuDemo
  4. //
  5. // Created by Sin on 16/9/26.
  6. // Copyright © 2016年 Sin. All rights reserved.
  7. //
  8. #import "UIView+YOUPAIRCDDanmaku.h"
  9. #import "YOUPAIRCDDanmaku.h"
  10. #import "YOUPAIRCDDanmakuInfo.h"
  11. #import "YOUPAIRCDDanmakuManager.h"
  12. #define X(view) view.frame.origin.x
  13. #define Y(view) view.frame.origin.y
  14. #define Width(view) view.frame.size.width
  15. #define Height(view) view.frame.size.height
  16. #define Left(view) X(view)
  17. #define Right(view) (X(view) + Width(view))
  18. #define Top(view) Y(view)
  19. #define Bottom(view) (Y(view) + Height(view))
  20. #define CenterX(view) (Left(view) + Right(view))/2
  21. #define CenterY(view) (Top(view) + Bottom(view))/2
  22. @implementation UIView (YOUPAIRCDDanmaku)
  23. #pragma mark - public method
  24. //发送一个弹幕
  25. - (void)sendDanmaku:(YOUPAIRCDDanmaku *)danmaku
  26. {
  27. danmaku.playView.tag = RCDDanmakuViewTag;
  28. [RCDanmakuManager.danmakus addObject:danmaku];
  29. //如果允许过量加载demo,就不进行弹幕的缓存,
  30. if(RCDanmakuManager.isAllowOverLoad){
  31. if(!RCDanmakuManager.isPlaying) {
  32. }else {
  33. [self rc_playDanmaku:danmaku];
  34. }
  35. }else {
  36. [self rc_playOverLoadDanmaku];
  37. }
  38. }
  39. //发送一个特定位置的弹幕,根据point来确定弹幕左上角的位置
  40. - (void)sendDanmaku:(YOUPAIRCDDanmaku *)danmaku atPoint:(CGPoint)point {
  41. [RCDanmakuManager.danmakus addObject:danmaku];
  42. UIView* playerView = danmaku.playView;
  43. CGRect playViewFrame = playerView.frame;
  44. playViewFrame.origin.x = point.x;
  45. playViewFrame.origin.y = point.y;
  46. playerView.frame = playViewFrame;
  47. //确定好弹幕的位置之后发一个中心点位置的弹幕
  48. [self sendDanmaku:danmaku atCenterPoint:playerView.center];
  49. }
  50. //发送一个特定位置的弹幕,根据point来确定弹幕的中心点位置
  51. - (void)sendDanmaku:(YOUPAIRCDDanmaku *)danmaku atCenterPoint:(CGPoint)point {
  52. [RCDanmakuManager.danmakus addObject:danmaku];
  53. UIView* playerView = danmaku.playView;
  54. playerView.center = point;
  55. YOUPAIRCDDanmakuInfo *info = [[YOUPAIRCDDanmakuInfo alloc]init];
  56. info.danmaku = danmaku;
  57. info.danmaku.playView = playerView;
  58. [self rc_checkDanmakuViewInView:playerView];
  59. [self rc_playSpecialDanamku:info];
  60. }
  61. //停止弹幕
  62. - (void)youpaifstopDanmaku
  63. {
  64. RCDanmakuManager.isPlaying = NO;
  65. [RCDanmakuManager.timer invalidate];
  66. RCDanmakuManager.timer = nil;
  67. [RCDanmakuManager.danmakus removeAllObjects];
  68. [RCDanmakuManager.linesDict removeAllObjects];
  69. [RCDanmakuManager.subDanmakuInfos removeAllObjects];
  70. for(UIView *View in RCDanmakuManager.currentDanmakuCache){
  71. [View removeFromSuperview];
  72. }
  73. for(UIView *view in self.subviews){
  74. if(RCDDanmakuViewTag == view.tag){
  75. [view removeFromSuperview];
  76. }
  77. }
  78. [RCDanmakuManager.currentDanmakuCache removeAllObjects];
  79. [RCDanmakuManager reset];
  80. }
  81. //暂停弹幕配合 youpaifresumeDanmaku 使用
  82. - (void)youpaifpauseDanmaku
  83. {
  84. if(RCDanmakuManager.isAllowOverLoad){
  85. if(!RCDanmakuManager.isPlaying) return ;
  86. }else {
  87. if(!RCDanmakuManager.timer || !RCDanmakuManager.timer.isValid ) return;
  88. }
  89. RCDanmakuManager.isPlaying = NO;
  90. [RCDanmakuManager.timer invalidate];
  91. RCDanmakuManager.timer = nil;
  92. for (UIView* View in self.subviews) {
  93. if(RCDDanmakuViewTag == View.tag){
  94. CALayer *layer = View.layer;
  95. CGRect rect = View.frame;
  96. if (layer.presentationLayer) {
  97. rect = ((CALayer *)layer.presentationLayer).frame;
  98. }
  99. View.frame = rect;
  100. [View.layer removeAllAnimations];
  101. }
  102. }
  103. }
  104. //重开弹幕配合 youpaifpauseDanmaku 使用
  105. - (void)youpaifresumeDanmaku
  106. {
  107. if( RCDanmakuManager.isPlaying )return;
  108. RCDanmakuManager.isPlaying = YES;
  109. for (YOUPAIRCDDanmakuInfo* info in RCDanmakuManager.subDanmakuInfos) {
  110. if (info.danmaku.position == RCDDanmakuPositionNone) {
  111. //计算暂停在屏幕上的弹幕的剩余时间
  112. UIView *View = info.danmaku.playView;
  113. CGFloat scale = 1.0 * Right(View) / (Width(self) + Width(View));
  114. NSTimeInterval duration = RCDanmakuManager.duration *scale;
  115. [self rc_performAnimationWithDuration:duration danmakuInfo:info];
  116. }else{
  117. [self rc_performCenterAnimationWithDuration:info.leftTime danmakuInfo:info];
  118. }
  119. }
  120. }
  121. #pragma mark - private method
  122. - (void)rc_playDanmaku:(YOUPAIRCDDanmaku *)danmaku
  123. {
  124. NSLog(@"总弹幕数%zd",RCDanmakuManager.danmakus.count);
  125. danmaku.playView.backgroundColor = [UIColor clearColor];
  126. switch (danmaku.position) {
  127. case RCDDanmakuPositionNone:
  128. [self rc_playFromRightDanmaku:danmaku playerView:danmaku.playView];
  129. break;
  130. case RCDDanmakuPositionCenterTop:
  131. case RCDDanmakuPositionCenterBottom:
  132. [self rc_playCenterDanmaku:danmaku playerView:danmaku.playView];
  133. break;
  134. default:
  135. break;
  136. }
  137. }
  138. #pragma mark over load danmaku
  139. - (void)rc_playOverLoadDanmaku {
  140. if (!RCDanmakuManager.timer && RCDanmakuManager.isPlaying) {
  141. NSTimeInterval interval = RCDanmakuManager.duration / RCDanmakuManager.maxShowLineCount * 1.0 + 0.1;
  142. RCDanmakuManager.timer = [NSTimer timerWithTimeInterval:interval target:self selector:@selector(rc_sendOverLoadDanmaku) userInfo:nil repeats:YES];
  143. [[NSRunLoop currentRunLoop] addTimer:RCDanmakuManager.timer forMode:NSRunLoopCommonModes];
  144. [RCDanmakuManager.timer fire];
  145. }
  146. }
  147. - (void)rc_sendOverLoadDanmaku {
  148. if(RCDanmakuManager.danmakus.count > 0){
  149. NSLog(@"count total %zd",RCDanmakuManager.danmakus.count);
  150. [self rc_playDanmaku:RCDanmakuManager.danmakus[0]];
  151. [RCDanmakuManager.danmakus removeObjectAtIndex:0];
  152. }
  153. }
  154. #pragma mark center top \ bottom
  155. - (void)rc_playCenterDanmaku:(YOUPAIRCDDanmaku *)danmaku playerView:(UIView *)playerView
  156. {
  157. NSAssert(RCDanmakuManager.centerDuration && RCDanmakuManager.maxCenterLineCount, @"如果要使用中间弹幕 必须先设置中间弹幕的时间及最大行数");
  158. YOUPAIRCDDanmakuInfo* newInfo = [[YOUPAIRCDDanmakuInfo alloc] init];
  159. newInfo.danmaku.playView = playerView;
  160. newInfo.leftTime = RCDanmakuManager.centerDuration;
  161. newInfo.danmaku = danmaku;
  162. NSMutableDictionary* centerDict = nil;
  163. if (danmaku.position == RCDDanmakuPositionCenterTop) {
  164. centerDict = RCDanmakuManager.centerTopLinesDict;
  165. }else{
  166. centerDict = RCDanmakuManager.centerBottomLinesDict;
  167. }
  168. NSInteger valueCount = centerDict.allKeys.count;
  169. if (valueCount == 0) {
  170. newInfo.lineCount = 0;
  171. [self rc_addCenterAnimation:newInfo centerDict:centerDict];
  172. return;
  173. }
  174. for (int i = 0; i<valueCount; i++) {
  175. YOUPAIRCDDanmakuInfo* oldInfo = centerDict[@(i)];
  176. if (!oldInfo) break;
  177. if (![oldInfo isKindOfClass:[YOUPAIRCDDanmakuInfo class]]) {
  178. newInfo.lineCount = i;
  179. [self rc_addCenterAnimation:newInfo centerDict:centerDict];
  180. break;
  181. }else if (i == valueCount - 1){
  182. if (valueCount < RCDanmakuManager.maxCenterLineCount) {
  183. newInfo.lineCount = i+1;
  184. [self rc_addCenterAnimation:newInfo centerDict:centerDict];
  185. }else{
  186. [self rc_removeDanmakuInfoAfterAnimation:newInfo];
  187. NSLog(@"同一时间评论太多--排不开了--------------------------");
  188. }
  189. }
  190. }
  191. }
  192. - (void)rc_addCenterAnimation:(YOUPAIRCDDanmakuInfo *)info centerDict:(NSMutableDictionary *)centerDict
  193. {
  194. UIView* View = info.danmaku.playView;
  195. NSInteger lineCount = info.lineCount;
  196. if (info.danmaku.position == RCDDanmakuPositionCenterTop) {
  197. View.frame = CGRectMake((Width(self) - Width(View)) * 0.5, (RCDanmakuManager.lineHeight + RCDanmakuManager.lineMargin) * lineCount, Width(View), Height(View));
  198. NSLog(@"top frame %@",NSStringFromCGRect(View.frame));
  199. }else{
  200. View.frame = CGRectMake((Width(self) - Width(View)) * 0.5, Height(self) - Height(View) - (RCDanmakuManager.lineHeight + RCDanmakuManager.lineMargin) * lineCount, Width(View), Height(View));
  201. NSLog(@"bottom frame %@",NSStringFromCGRect(View.frame));
  202. }
  203. centerDict[@(lineCount)] = info;
  204. [RCDanmakuManager.subDanmakuInfos addObject:info];
  205. [self rc_performCenterAnimationWithDuration:info.leftTime danmakuInfo:info ];
  206. }
  207. - (void)rc_performCenterAnimationWithDuration:(NSTimeInterval)duration danmakuInfo:(YOUPAIRCDDanmakuInfo *)info
  208. {
  209. UIView* View = info.danmaku.playView;
  210. [RCDanmakuManager.currentDanmakuCache addObject:View];
  211. NSLog(@"index %zd",info.lineCount);
  212. [self addSubview:View];
  213. __weak typeof(self) weakSelf = self;
  214. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  215. if(!RCDanmakuManager.isPlaying) return ;
  216. if (info.danmaku.position == RCDDanmakuPositionCenterBottom) {
  217. RCDanmakuManager.centerBottomLinesDict[@(info.lineCount)] = @(0);
  218. }else{
  219. RCDanmakuManager.centerTopLinesDict[@(info.lineCount)] = @(0);
  220. }
  221. [weakSelf rc_removeDanmakuInfoAfterAnimation:info];
  222. });
  223. }
  224. #pragma mark from right
  225. - (void)rc_playFromRightDanmaku:(YOUPAIRCDDanmaku *)danmaku playerView:(UIView *)playerView
  226. {
  227. YOUPAIRCDDanmakuInfo* newInfo = [[YOUPAIRCDDanmakuInfo alloc] init];
  228. newInfo.danmaku.playView = playerView;
  229. newInfo.leftTime = RCDanmakuManager.duration;
  230. newInfo.danmaku = danmaku;
  231. [RCDanmakuManager.currentDanmakuCache addObject:playerView];
  232. NSMutableArray *arr = [self rc_getNSArray];
  233. NSArray *lineArr = RCDanmakuManager.linesDict.allKeys;
  234. [arr removeObjectsInArray:lineArr];
  235. NSInteger lineCount = [self rc_getRandomNum:arr];
  236. if(!RCDanmakuManager.isAllowOverLoad) {
  237. newInfo.lineCount = lineCount;
  238. while (1) {
  239. YOUPAIRCDDanmakuInfo* oldInfo = RCDanmakuManager.linesDict[@(newInfo.lineCount)];
  240. if(oldInfo){
  241. NSInteger randNum = [self rc_getRandomNum:arr];
  242. NSLog(@"----------------------------------");
  243. newInfo.lineCount = randNum*randNum % RCDanmakuManager.maxShowLineCount;
  244. }else{
  245. break;
  246. }
  247. }
  248. }else {
  249. lineCount = abs((int)arc4random()) % RCDanmakuManager.maxShowLineCount;
  250. newInfo.lineCount = lineCount;
  251. }
  252. playerView.frame = CGRectMake(Width(self), 0, Width(playerView), Height(playerView));
  253. [self rc_addAnimationToViewWithInfo:newInfo];
  254. }
  255. - (NSMutableArray *)rc_getNSArray {
  256. NSMutableArray *arr = [NSMutableArray new];
  257. arr = [NSMutableArray new];
  258. for (int i=0;i<RCDanmakuManager.maxShowLineCount;i++){
  259. [arr addObject:@(i)];
  260. }
  261. return arr;
  262. }
  263. - (NSInteger)rc_getRandomNum:(NSArray *)selectingList{
  264. if(selectingList.count == 0){
  265. return 0;
  266. }
  267. NSInteger index = arc4random_uniform((u_int32_t)selectingList.count-1);
  268. if(index<0){
  269. index = 0;
  270. }else if(index >selectingList.count-1) {
  271. index = selectingList.count-1;
  272. }
  273. return [selectingList[index] integerValue];
  274. }
  275. - (void)rc_addAnimationToViewWithInfo:(YOUPAIRCDDanmakuInfo *)info
  276. {
  277. UIView* View = info.danmaku.playView;
  278. NSInteger lineCount = info.lineCount;
  279. NSInteger heigth = KScreenHeight / 2;
  280. CGFloat y = arc4random() % heigth + heigth - Height(View) - SafeHeight;
  281. //(RCDanmakuManager.lineHeight + RCDanmakuManager.lineMargin) * lineCount,
  282. View.frame = CGRectMake(Width(self), y, Width(View), Height(View));
  283. [RCDanmakuManager.subDanmakuInfos addObject:info];
  284. RCDanmakuManager.linesDict[@(lineCount)] = info;
  285. [self rc_performAnimationWithDuration:info.leftTime danmakuInfo:info];
  286. }
  287. - (void)rc_performAnimationWithDuration:(NSTimeInterval)duration danmakuInfo:(YOUPAIRCDDanmakuInfo *)info
  288. {
  289. RCDanmakuManager.isPlaying = YES;
  290. UIView* View = info.danmaku.playView;
  291. CGRect endFrame = CGRectMake(-Width(View), Y(View), Width(View), Height(View));
  292. [self addSubview:View];
  293. __weak typeof(self) weakSelf = self;
  294. [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
  295. View.frame = endFrame;
  296. } completion:^(BOOL finished) {
  297. if (finished) {
  298. [weakSelf rc_removeDanmakuInfoAfterAnimation:info];
  299. [RCDanmakuManager.linesDict removeObjectForKey:@(info.lineCount)];
  300. NSLog(@"count left %zd",RCDanmakuManager.danmakus.count);
  301. }
  302. }];
  303. }
  304. #pragma mark util method
  305. //播放特定位置的弹幕的动画
  306. - (void)rc_playSpecialDanamku:(YOUPAIRCDDanmakuInfo *)info {
  307. UIView *View = info.danmaku.playView;
  308. [RCDanmakuManager.currentDanmakuCache addObject:View];
  309. [self addSubview:View];
  310. __weak typeof(self) weakSelf = self;
  311. NSTimeInterval duration = RCDanmakuManager.specialDuration;
  312. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  313. if(!RCDanmakuManager.isPlaying) return ;
  314. [weakSelf rc_removeDanmakuInfoAfterAnimation:info];
  315. });
  316. }
  317. //一个弹幕消失之后,将其彻底移除
  318. - (void)rc_removeDanmakuInfoAfterAnimation:(YOUPAIRCDDanmakuInfo *)info {
  319. UIView *View = info.danmaku.playView;
  320. [View removeFromSuperview];
  321. [RCDanmakuManager.danmakus removeObject:info.danmaku];
  322. [RCDanmakuManager.currentDanmakuCache removeObject:View];
  323. [RCDanmakuManager.subDanmakuInfos removeObject:info];
  324. info = nil;
  325. }
  326. //检测特定位置弹幕是否是在视频页面中
  327. - (void)rc_checkDanmakuViewInView:(UIView *)View {
  328. if(!CGRectContainsRect(self.frame, View.frame)){
  329. NSLog(@"warning : 特殊点弹幕的位置超出可视范围");
  330. }
  331. }
  332. @end