Commit c5309b94e0aa48ffc809c7a5719be7677eb9744b
1 parent
efed3d62
no message
Showing
14 changed files
with
945 additions
and
15 deletions
LiveVideoCloud/LiveVideoCloud.xcodeproj/project.pbxproj
| ... | ... | @@ -26,6 +26,10 @@ |
| 26 | 26 | 811BB6851E8106AF005C6291 /* CNLivePlayerSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 811BB6821E8106A2005C6291 /* CNLivePlayerSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; |
| 27 | 27 | 811BB6881E8107B4005C6291 /* LVCChatViewTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 811BB6871E8107B4005C6291 /* LVCChatViewTableViewCell.m */; }; |
| 28 | 28 | 811BB68E1E8116C8005C6291 /* NSString+StringSize.m in Sources */ = {isa = PBXBuildFile; fileRef = 811BB68D1E8116C8005C6291 /* NSString+StringSize.m */; }; |
| 29 | + 8128E26E1E8A3AFE00B40E75 /* LVCDanmuUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 8128E26D1E8A3AFE00B40E75 /* LVCDanmuUtil.m */; }; | |
| 30 | + 8128E2711E8A3BF000B40E75 /* LVCDanmuView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8128E2701E8A3BF000B40E75 /* LVCDanmuView.m */; }; | |
| 31 | + 8128E2741E8A3C1900B40E75 /* LVCDanmuLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 8128E2731E8A3C1900B40E75 /* LVCDanmuLabel.m */; }; | |
| 32 | + 8128E2771E8A3CD700B40E75 /* LVCDanmuManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8128E2761E8A3CD700B40E75 /* LVCDanmuManager.m */; }; | |
| 29 | 33 | 8130911E1E84C0F0005F0BFF /* LVCMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 8130911D1E84C0F0005F0BFF /* LVCMessage.m */; }; |
| 30 | 34 | 81A1B9A91E8277410079FF5F /* LVCInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 81A1B9A81E8277410079FF5F /* LVCInputView.m */; }; |
| 31 | 35 | 81A482F51E88ACC80097A80A /* LVCTelevisionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 81A482F41E88ACC80097A80A /* LVCTelevisionView.m */; }; |
| ... | ... | @@ -274,6 +278,14 @@ |
| 274 | 278 | 811BB6871E8107B4005C6291 /* LVCChatViewTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LVCChatViewTableViewCell.m; sourceTree = "<group>"; }; |
| 275 | 279 | 811BB68C1E8116C8005C6291 /* NSString+StringSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+StringSize.h"; sourceTree = "<group>"; }; |
| 276 | 280 | 811BB68D1E8116C8005C6291 /* NSString+StringSize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+StringSize.m"; sourceTree = "<group>"; }; |
| 281 | + 8128E26C1E8A3AFE00B40E75 /* LVCDanmuUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LVCDanmuUtil.h; sourceTree = "<group>"; }; | |
| 282 | + 8128E26D1E8A3AFE00B40E75 /* LVCDanmuUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LVCDanmuUtil.m; sourceTree = "<group>"; }; | |
| 283 | + 8128E26F1E8A3BF000B40E75 /* LVCDanmuView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LVCDanmuView.h; sourceTree = "<group>"; }; | |
| 284 | + 8128E2701E8A3BF000B40E75 /* LVCDanmuView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LVCDanmuView.m; sourceTree = "<group>"; }; | |
| 285 | + 8128E2721E8A3C1900B40E75 /* LVCDanmuLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LVCDanmuLabel.h; sourceTree = "<group>"; }; | |
| 286 | + 8128E2731E8A3C1900B40E75 /* LVCDanmuLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LVCDanmuLabel.m; sourceTree = "<group>"; }; | |
| 287 | + 8128E2751E8A3CD700B40E75 /* LVCDanmuManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LVCDanmuManager.h; sourceTree = "<group>"; }; | |
| 288 | + 8128E2761E8A3CD700B40E75 /* LVCDanmuManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LVCDanmuManager.m; sourceTree = "<group>"; }; | |
| 277 | 289 | 8130911C1E84C0F0005F0BFF /* LVCMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LVCMessage.h; sourceTree = "<group>"; }; |
| 278 | 290 | 8130911D1E84C0F0005F0BFF /* LVCMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LVCMessage.m; sourceTree = "<group>"; }; |
| 279 | 291 | 81A1B9A71E8277410079FF5F /* LVCInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LVCInputView.h; sourceTree = "<group>"; }; |
| ... | ... | @@ -701,6 +713,21 @@ |
| 701 | 713 | path = ../ChatView; |
| 702 | 714 | sourceTree = "<group>"; |
| 703 | 715 | }; |
| 716 | + 8128E26B1E8A3AD400B40E75 /* Danmu */ = { | |
| 717 | + isa = PBXGroup; | |
| 718 | + children = ( | |
| 719 | + 8128E26C1E8A3AFE00B40E75 /* LVCDanmuUtil.h */, | |
| 720 | + 8128E26D1E8A3AFE00B40E75 /* LVCDanmuUtil.m */, | |
| 721 | + 8128E26F1E8A3BF000B40E75 /* LVCDanmuView.h */, | |
| 722 | + 8128E2701E8A3BF000B40E75 /* LVCDanmuView.m */, | |
| 723 | + 8128E2721E8A3C1900B40E75 /* LVCDanmuLabel.h */, | |
| 724 | + 8128E2731E8A3C1900B40E75 /* LVCDanmuLabel.m */, | |
| 725 | + 8128E2751E8A3CD700B40E75 /* LVCDanmuManager.h */, | |
| 726 | + 8128E2761E8A3CD700B40E75 /* LVCDanmuManager.m */, | |
| 727 | + ); | |
| 728 | + path = Danmu; | |
| 729 | + sourceTree = "<group>"; | |
| 730 | + }; | |
| 704 | 731 | 812E504E1E7699B9004F5FCD /* CNLiveSDK */ = { |
| 705 | 732 | isa = PBXGroup; |
| 706 | 733 | children = ( |
| ... | ... | @@ -1630,6 +1657,7 @@ |
| 1630 | 1657 | D2888DAA1E6EC014003A77C8 /* View */ = { |
| 1631 | 1658 | isa = PBXGroup; |
| 1632 | 1659 | children = ( |
| 1660 | + 8128E26B1E8A3AD400B40E75 /* Danmu */, | |
| 1633 | 1661 | 811BB67B1E80F858005C6291 /* ChatView */, |
| 1634 | 1662 | 810B64DE1E779AE900133E15 /* LVCPlayer.h */, |
| 1635 | 1663 | 810B64DF1E779AE900133E15 /* LVCPlayer.m */, |
| ... | ... | @@ -1881,6 +1909,7 @@ |
| 1881 | 1909 | D2029D011E6EAE2E0027CD92 /* UIColor+HexString.m in Sources */, |
| 1882 | 1910 | D2029CA41E6EADE10027CD92 /* MJRefreshComponent.m in Sources */, |
| 1883 | 1911 | D2575C7A1E76A4F300E3A8FC /* NHCarViews.m in Sources */, |
| 1912 | + 8128E26E1E8A3AFE00B40E75 /* LVCDanmuUtil.m in Sources */, | |
| 1884 | 1913 | D2029CBC1E6EADE10027CD92 /* SDWebImageDownloaderOperation.m in Sources */, |
| 1885 | 1914 | D2029CA51E6EADE10027CD92 /* MJRefreshFooter.m in Sources */, |
| 1886 | 1915 | D22830FC1E77C2D900627701 /* BottomToolView.m in Sources */, |
| ... | ... | @@ -1933,6 +1962,7 @@ |
| 1933 | 1962 | D2029CC61E6EADE10027CD92 /* SSKeychain.m in Sources */, |
| 1934 | 1963 | 811BB68E1E8116C8005C6291 /* NSString+StringSize.m in Sources */, |
| 1935 | 1964 | D2029C8B1E6EADE10027CD92 /* AFURLResponseSerialization.m in Sources */, |
| 1965 | + 8128E2711E8A3BF000B40E75 /* LVCDanmuView.m in Sources */, | |
| 1936 | 1966 | D2029C921E6EADE10027CD92 /* UIImageView+AFNetworking.m in Sources */, |
| 1937 | 1967 | D224B1971E826ED800CC61D0 /* ChatBarrageAnimAction.m in Sources */, |
| 1938 | 1968 | D2029C9E1E6EADE10027CD92 /* NSObject+MJCoding.m in Sources */, |
| ... | ... | @@ -1943,7 +1973,6 @@ |
| 1943 | 1973 | D2029D041E6EAE2E0027CD92 /* UILabel+ContentSize.m in Sources */, |
| 1944 | 1974 | D25530031E79115100DF38A5 /* AnimQueueManager.m in Sources */, |
| 1945 | 1975 | D2029C871E6EADE10027CD92 /* AFHTTPSessionManager.m in Sources */, |
| 1946 | - D236968D1E7FC7C200E4BF71 /* CNBarrageManager.m in Sources */, | |
| 1947 | 1976 | 81A482F81E88AFA20097A80A /* LVCFirstHeaderReusableView.m in Sources */, |
| 1948 | 1977 | D2029B861E6EAAA50027CD92 /* main.m in Sources */, |
| 1949 | 1978 | D2029C8C1E6EADE10027CD92 /* AFURLSessionManager.m in Sources */, |
| ... | ... | @@ -1952,6 +1981,7 @@ |
| 1952 | 1981 | D2888DB11E6EC05F003A77C8 /* TempViewController.m in Sources */, |
| 1953 | 1982 | 81A483011E88B0130097A80A /* LVCBannerModel.m in Sources */, |
| 1954 | 1983 | 810B65021E779B1F00133E15 /* MASViewAttribute.m in Sources */, |
| 1984 | + 8128E2771E8A3CD700B40E75 /* LVCDanmuManager.m in Sources */, | |
| 1955 | 1985 | D2B7B2511E8247D400ED084E /* ChatBarrageManager.m in Sources */, |
| 1956 | 1986 | D2029CAC1E6EADE10027CD92 /* MJRefreshBackStateFooter.m in Sources */, |
| 1957 | 1987 | D2029CB11E6EADE10027CD92 /* MJRefreshConst.m in Sources */, |
| ... | ... | @@ -1961,6 +1991,7 @@ |
| 1961 | 1991 | D2029D051E6EAE2E0027CD92 /* UILabel+labelFitSize.m in Sources */, |
| 1962 | 1992 | D2029CB21E6EADE10027CD92 /* UIScrollView+MJExtension.m in Sources */, |
| 1963 | 1993 | D29D88481E88C53300543A03 /* LVCUGCLivePrepareView.m in Sources */, |
| 1994 | + 8128E2741E8A3C1900B40E75 /* LVCDanmuLabel.m in Sources */, | |
| 1964 | 1995 | D2029D091E6EAE2E0027CD92 /* Foundation+Log.m in Sources */, |
| 1965 | 1996 | D2029C8E1E6EADE10027CD92 /* AFImageDownloader.m in Sources */, |
| 1966 | 1997 | 81A483201E88F4BF0097A80A /* LVCProgramModel.m in Sources */, | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/ChatView/LVCInputView.m
| ... | ... | @@ -45,7 +45,7 @@ |
| 45 | 45 | [self.delegate lvcInputView:self sendMessage:_inputTextField.text]; |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | - [self endEditing:YES]; | |
| 48 | +// [self endEditing:YES]; | |
| 49 | 49 | _inputTextField.text = @""; |
| 50 | 50 | |
| 51 | 51 | } |
| ... | ... | @@ -64,7 +64,7 @@ |
| 64 | 64 | [self.delegate lvcInputView:self sendMessage:_inputTextField.text]; |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | - [self endEditing:YES]; | |
| 67 | +// [self endEditing:YES]; | |
| 68 | 68 | textField.text = @""; |
| 69 | 69 | return YES; |
| 70 | 70 | } | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/Controller/HomeViewController.m
| ... | ... | @@ -11,7 +11,7 @@ |
| 11 | 11 | #import "LVCInteractionController.h" |
| 12 | 12 | #import "LVCLivePlayerController.h" |
| 13 | 13 | #import "LVCUGCLivePlayerController.h" |
| 14 | -#import "LVCTVLiveViewController.h" | |
| 14 | +#import "LVCLivePlayerController.h" | |
| 15 | 15 | |
| 16 | 16 | @interface HomeViewController () |
| 17 | 17 | |
| ... | ... | @@ -56,8 +56,8 @@ |
| 56 | 56 | |
| 57 | 57 | - (void)liveAction |
| 58 | 58 | { |
| 59 | - LVCTVLiveViewController *liveVC = [[LVCTVLiveViewController alloc] init]; | |
| 60 | -// self.navigationController.navigationBar.hidden = YES; | |
| 59 | + LVCLivePlayerController *liveVC = [[LVCLivePlayerController alloc] init]; | |
| 60 | + self.navigationController.navigationBar.hidden = YES; | |
| 61 | 61 | [self.navigationController pushViewController:liveVC animated:YES]; |
| 62 | 62 | } |
| 63 | 63 | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/Controller/LVCLivePlayerController.m
| ... | ... | @@ -26,6 +26,9 @@ |
| 26 | 26 | @property (nonatomic, strong) LVCInputView *inputView; |
| 27 | 27 | @property (nonatomic, strong) LVCChatView *chatView; |
| 28 | 28 | |
| 29 | +@property (nonatomic, strong) NSTimer *timer; | |
| 30 | +@property (nonatomic, assign) NSTimeInterval countTime; | |
| 31 | + | |
| 29 | 32 | @end |
| 30 | 33 | |
| 31 | 34 | @implementation LVCLivePlayerController |
| ... | ... | @@ -72,8 +75,13 @@ |
| 72 | 75 | [self makeConstraints]; |
| 73 | 76 | |
| 74 | 77 | [_player autoHideControlView]; //自动隐藏工具条 |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 75 | 82 | } |
| 76 | 83 | |
| 84 | + | |
| 77 | 85 | - (void)addObservers |
| 78 | 86 | { |
| 79 | 87 | [[NSNotificationCenter defaultCenter] addObserver:self |
| ... | ... | @@ -140,9 +148,7 @@ |
| 140 | 148 | //当键盘出现 |
| 141 | 149 | - (void)keyboardWillShow:(NSNotification *)aNotification{ |
| 142 | 150 | //获取键盘的高度 |
| 143 | - NSDictionary* info = [aNotification userInfo]; | |
| 144 | - CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size; | |
| 145 | - _inputView.frame = CGRectMake(0, SCREEN_HEIGHT-kbSize.height-50, SCREEN_WIDTH, 50); | |
| 151 | + | |
| 146 | 152 | } |
| 147 | 153 | |
| 148 | 154 | - (void)keyboardWillHide:(NSNotification *)aNotification{ |
| ... | ... | @@ -214,7 +220,7 @@ |
| 214 | 220 | } |
| 215 | 221 | } |
| 216 | 222 | |
| 217 | -- (void)lvcPlayer:(LVCPlayer *)player clickedFullScreenBtn:(UIButton *)fullScreenBtn | |
| 223 | +- (void)lvcPlayer:(LVCPlayer *)player clicketullScreenBtn:(UIButton *)fullScreenBtn | |
| 218 | 224 | { |
| 219 | 225 | if (_player.isFullScreen) {//全屏 |
| 220 | 226 | [self toHalfScreenWithrotationAngle:M_PI_2]; |
| ... | ... | @@ -236,8 +242,7 @@ |
| 236 | 242 | { |
| 237 | 243 | NSLog(@"发送消息(%@)",message); |
| 238 | 244 | |
| 239 | - [_chatView sendMsgWithMessage:message]; | |
| 240 | - | |
| 245 | +// [_chatView sendMsgWithMessage:message]; | |
| 241 | 246 | } |
| 242 | 247 | |
| 243 | 248 | - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event |
| ... | ... | @@ -315,6 +320,7 @@ |
| 315 | 320 | return _chatView; |
| 316 | 321 | } |
| 317 | 322 | |
| 323 | + | |
| 318 | 324 | - (void)didReceiveMemoryWarning { |
| 319 | 325 | [super didReceiveMemoryWarning]; |
| 320 | 326 | // Dispose of any resources that can be recreated. | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/Controller/LVCMoreProgramViewController.m
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/Danmu/LVCDanmuLabel.h
0 → 100644
| 1 | +// | |
| 2 | +// LVCDanmuLabel.h | |
| 3 | +// LiveVideoCloud | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/28. | |
| 6 | +// Copyright © 2017年 cnlive. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <UIKit/UIKit.h> | |
| 10 | +#import "LVCDanmuUtil.h" | |
| 11 | + | |
| 12 | +@interface LVCDanmuLabel : UILabel | |
| 13 | + | |
| 14 | + | |
| 15 | +@property (nonatomic, strong, readonly) NSDictionary *info; | |
| 16 | +@property (nonatomic, readonly) DanmuState danmuState; | |
| 17 | +@property (nonatomic, readonly) CGFloat animationDuartion; | |
| 18 | +@property (nonatomic, strong, readonly) NSDictionary *dicOptional; | |
| 19 | + | |
| 20 | +@property (nonatomic, readonly) CGFloat speed; | |
| 21 | +@property (nonatomic, readonly) CGFloat currentRightX; | |
| 22 | + | |
| 23 | +@property (nonatomic, readonly) CGFloat startTime; | |
| 24 | + | |
| 25 | + | |
| 26 | +@property (nonatomic, getter=isFontSizeBig, readonly) BOOL fontSizeBig; | |
| 27 | +@property (nonatomic, getter=isFontSizeMiddle, readonly) BOOL fontSizeMiddle; | |
| 28 | +@property (nonatomic, getter=isFontSizeSmall, readonly) BOOL fontSizeSmall; | |
| 29 | +@property (nonatomic, getter=isMoveModeRolling, readonly) BOOL moveModeRolling; | |
| 30 | +@property (nonatomic, getter=isPositionTop, readonly) BOOL positionTop; | |
| 31 | +@property (nonatomic, getter=isPositionMiddle, readonly) BOOL positionMiddle; | |
| 32 | +@property (nonatomic, getter=isPositionBottom, readonly) BOOL positionBottom; | |
| 33 | + | |
| 34 | ++ (instancetype)createWithInfo:(NSDictionary *)info inView:(UIView *)view; | |
| 35 | + | |
| 36 | +- (void)setDanmuChannel:(NSUInteger)channel offset:(CGFloat)xy; | |
| 37 | + | |
| 38 | +- (void)animationDanmuItem:(NSTimeInterval)waitTime; | |
| 39 | + | |
| 40 | +- (void)pause; | |
| 41 | + | |
| 42 | +- (void)resume:(NSTimeInterval)nowTime; | |
| 43 | + | |
| 44 | +- (void)removeDanmu; | |
| 45 | + | |
| 46 | +@end | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/Danmu/LVCDanmuLabel.m
0 → 100644
| 1 | +// | |
| 2 | +// LVCDanmuLabel.m | |
| 3 | +// LiveVideoCloud | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/28. | |
| 6 | +// Copyright © 2017年 cnlive. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "LVCDanmuLabel.h" | |
| 10 | + | |
| 11 | +static NSString * const DANMU_FONT_SIZE_BIG = @"l"; | |
| 12 | +static NSString * const DANMU_FONT_SIZE_MIDDLE = @"m"; | |
| 13 | +static NSString * const DANMU_FONT_SIZE_SMALL = @"s"; | |
| 14 | + | |
| 15 | +static NSString * const DANMU_MOVE_MODE_ROLLING = @"l"; | |
| 16 | + | |
| 17 | +static NSString * const DANMU_POSITION_TOP = @"t"; | |
| 18 | +static NSString * const DANMU_POSITION_MIDDLE = @"m"; | |
| 19 | +static NSString * const DANMU_POSITION_BOTTOM = @"b"; | |
| 20 | + | |
| 21 | +#define ARC4RANDOM_MAX 0x100000000 | |
| 22 | + | |
| 23 | +#define ROLL_ANIMATION_DURATION_TIME 5 | |
| 24 | + | |
| 25 | +#define ANIMATION_DELAY_TIME 3 | |
| 26 | + | |
| 27 | +@interface LVCDanmuLabel () | |
| 28 | + | |
| 29 | +@property (nonatomic, strong, readwrite) NSDictionary *info; | |
| 30 | +@property (nonatomic, readwrite) DanmuState danmuState; | |
| 31 | +@property (nonatomic, readwrite) CGFloat animationDuartion; | |
| 32 | +@property (nonatomic, strong, readwrite) NSDictionary *dicOptional; | |
| 33 | + | |
| 34 | +@property (nonatomic, readwrite) CGFloat speed; | |
| 35 | +@property (nonatomic, readwrite) CGFloat currentRightX; | |
| 36 | + | |
| 37 | +@property (nonatomic) NSUInteger nChannel; | |
| 38 | +@property (nonatomic, weak) UIView *superView; | |
| 39 | + | |
| 40 | +@property (nonatomic) CGFloat originalX; | |
| 41 | + | |
| 42 | +@end | |
| 43 | + | |
| 44 | +@implementation LVCDanmuLabel | |
| 45 | + | |
| 46 | +- (void)dealloc { | |
| 47 | + _info = nil; | |
| 48 | + _dicOptional = nil; | |
| 49 | +} | |
| 50 | + | |
| 51 | +#pragma mark - Private | |
| 52 | + | |
| 53 | +- (void)p_initData { | |
| 54 | + self.textColor = [UIColor blackColor]; | |
| 55 | + // 弹幕颜色 | |
| 56 | + // id optional = [self.info objectForKey:kDanmuOptionalKey]; | |
| 57 | + // if ([optional isKindOfClass:[NSString class]]) { | |
| 58 | + // optional = [LVCDanmuUtil defaultOptions]; | |
| 59 | + // } | |
| 60 | + id optional = [LVCDanmuUtil randomOptions]; | |
| 61 | + self.dicOptional = [NSDictionary dictionaryWithDictionary:optional]; | |
| 62 | + | |
| 63 | + int color = [[optional objectForKey:@"c"] intValue]; | |
| 64 | + [self setTextColor:[LVCDanmuUtil colorFromARGB: color]]; | |
| 65 | + | |
| 66 | + CGFloat fontsize = 15; | |
| 67 | + if (self.isFontSizeBig) { | |
| 68 | + fontsize = 19; | |
| 69 | + }else if (self.isFontSizeMiddle) { | |
| 70 | + fontsize = 17; | |
| 71 | + } | |
| 72 | + | |
| 73 | + UIFont *font = [UIFont systemFontOfSize:fontsize]; | |
| 74 | + self.font = font; | |
| 75 | + | |
| 76 | + NSString *content = [_info objectForKey:kDanmuContentKey]; | |
| 77 | + self.text = content; | |
| 78 | +} | |
| 79 | + | |
| 80 | +- (void)p_initFrame:(CGFloat)offsetX { | |
| 81 | + NSString *content = [self.info objectForKey:kDanmuContentKey]; | |
| 82 | + CGSize size = [LVCDanmuUtil getSizeWithString:content withFont:self.font size:(CGSize){MAXFLOAT, CHANNEL_HEIGHT}]; | |
| 83 | + CGRect frame = (CGRect){(CGPoint){_superView.frame.size.width + offsetX, 0}, size}; | |
| 84 | + self.frame = frame; | |
| 85 | + _originalX = frame.origin.x + frame.size.width; | |
| 86 | +} | |
| 87 | + | |
| 88 | +- (void)p_rollAnimation:(CGFloat)time delay:(NSTimeInterval)waitTime { | |
| 89 | + self.danmuState = DanmuStateAnimationing; | |
| 90 | + [UIView animateWithDuration:time delay:waitTime options:UIViewAnimationOptionCurveLinear animations:^{ | |
| 91 | + CGRect frame = self.frame; | |
| 92 | + frame.origin.x = -self.frame.size.width; | |
| 93 | + self.frame = frame; | |
| 94 | + } completion:^(BOOL finished) { | |
| 95 | + if (finished) | |
| 96 | + [self removeDanmu]; | |
| 97 | + }]; | |
| 98 | +} | |
| 99 | + | |
| 100 | +#pragma mark - Action | |
| 101 | + | |
| 102 | ++ (instancetype)createWithInfo:(NSDictionary *)info inView:(UIView *)view { | |
| 103 | + LVCDanmuLabel *danmuLabel = [[LVCDanmuLabel alloc] init]; | |
| 104 | + | |
| 105 | + danmuLabel.info = info; | |
| 106 | + danmuLabel.superView = view; | |
| 107 | + danmuLabel.nChannel = 0; | |
| 108 | + | |
| 109 | + [danmuLabel p_initData]; | |
| 110 | + [danmuLabel p_initFrame:0]; | |
| 111 | + | |
| 112 | + return danmuLabel; | |
| 113 | +} | |
| 114 | + | |
| 115 | +- (void)setDanmuChannel:(NSUInteger)channel offset:(CGFloat)xy { | |
| 116 | + self.danmuState = DanmuStateStop; | |
| 117 | + self.nChannel = channel; | |
| 118 | + CGRect frame = self.frame; | |
| 119 | + frame.origin.x += xy; | |
| 120 | + frame.origin.y = CHANNEL_HEIGHT*_nChannel; | |
| 121 | + self.frame = frame; | |
| 122 | + _originalX = frame.origin.x + frame.size.width; | |
| 123 | + [_superView addSubview:self]; | |
| 124 | +} | |
| 125 | + | |
| 126 | +- (void)animationDanmuItem:(NSTimeInterval)waitTime { | |
| 127 | + | |
| 128 | + [self p_rollAnimation:self.animationDuartion delay:waitTime]; | |
| 129 | +} | |
| 130 | + | |
| 131 | +- (void)pause { | |
| 132 | + self.danmuState = DanmuStateStop; | |
| 133 | + UIView *view = self; | |
| 134 | + CALayer *layer = view.layer; | |
| 135 | + CGRect rect = view.frame; | |
| 136 | + if (layer.presentationLayer) { | |
| 137 | + rect = ((CALayer *)layer.presentationLayer).frame; | |
| 138 | + // rect.origin.x -= 1; | |
| 139 | + } | |
| 140 | + view.frame = rect; | |
| 141 | + [view.layer removeAllAnimations]; | |
| 142 | +} | |
| 143 | + | |
| 144 | +- (void)resume:(NSTimeInterval)nowTime { | |
| 145 | + CGFloat waitTime = self.startTime; | |
| 146 | + if (waitTime > nowTime) | |
| 147 | + waitTime = waitTime - nowTime; | |
| 148 | + else | |
| 149 | + waitTime = 0; | |
| 150 | + | |
| 151 | + CGFloat time = (self.frame.origin.x + self.frame.size.width)/self.speed; | |
| 152 | + [self p_rollAnimation:time delay:waitTime]; | |
| 153 | +} | |
| 154 | + | |
| 155 | +- (void)removeDanmu { | |
| 156 | + self.danmuState = DanmuStateFinish; | |
| 157 | + [self.layer removeAllAnimations]; | |
| 158 | + [self removeFromSuperview]; | |
| 159 | +} | |
| 160 | + | |
| 161 | + | |
| 162 | +#pragma mark - Get | |
| 163 | + | |
| 164 | +- (CGFloat)speed { | |
| 165 | + _speed = _originalX/self.animationDuartion; | |
| 166 | + return _speed; | |
| 167 | +} | |
| 168 | + | |
| 169 | +- (CGFloat)animationDuartion { | |
| 170 | + _animationDuartion = ROLL_ANIMATION_DURATION_TIME; | |
| 171 | + return _animationDuartion; | |
| 172 | +} | |
| 173 | + | |
| 174 | +- (CGFloat)currentRightX { | |
| 175 | + switch (self.danmuState) { | |
| 176 | + case DanmuStateStop: { | |
| 177 | + _currentRightX = CGRectGetMaxX(self.frame) - _superView.frame.size.width; | |
| 178 | + break; | |
| 179 | + } | |
| 180 | + case DanmuStateAnimationing: { | |
| 181 | + CALayer *layer = self.layer; | |
| 182 | + _currentRightX = _originalX; | |
| 183 | + if (layer.presentationLayer) | |
| 184 | + _currentRightX = ((CALayer *)layer.presentationLayer).frame.origin.x + self.frame.size.width; | |
| 185 | + _currentRightX -= _superView.frame.size.width; | |
| 186 | + break; | |
| 187 | + } | |
| 188 | + case DanmuStateFinish: { | |
| 189 | + _currentRightX = -_superView.frame.size.width; | |
| 190 | + break; | |
| 191 | + } | |
| 192 | + default: { | |
| 193 | + break; | |
| 194 | + } | |
| 195 | + } | |
| 196 | + return _currentRightX; | |
| 197 | +} | |
| 198 | + | |
| 199 | +#pragma mark - | |
| 200 | + | |
| 201 | +- (CGFloat)startTime { | |
| 202 | + return [[self.info valueForKey:kDanmuTimeKey] floatValue]; | |
| 203 | +} | |
| 204 | + | |
| 205 | +- (NSString *)fontSize { | |
| 206 | + return [self.dicOptional valueForKey: @"s"]; | |
| 207 | +} | |
| 208 | + | |
| 209 | +- (NSString *)moveMode { | |
| 210 | + return [self.dicOptional valueForKey: @"m"]; | |
| 211 | +} | |
| 212 | + | |
| 213 | +- (NSString *)position { | |
| 214 | + return [self.dicOptional valueForKey: @"p"]; | |
| 215 | +} | |
| 216 | + | |
| 217 | +- (BOOL)isFontSizeBig { | |
| 218 | + return [[self fontSize] isEqualToString: DANMU_FONT_SIZE_BIG]; | |
| 219 | +} | |
| 220 | + | |
| 221 | +- (BOOL)isFontSizeMiddle { | |
| 222 | + return [[self fontSize] isEqualToString: DANMU_FONT_SIZE_MIDDLE]; | |
| 223 | +} | |
| 224 | + | |
| 225 | +- (BOOL)isFontSizeSmall { | |
| 226 | + return [[self fontSize] isEqualToString: DANMU_FONT_SIZE_SMALL]; | |
| 227 | +} | |
| 228 | + | |
| 229 | +- (BOOL)isMoveModeRolling { | |
| 230 | + return [[self moveMode] isEqualToString: DANMU_MOVE_MODE_ROLLING]; | |
| 231 | +} | |
| 232 | + | |
| 233 | +- (BOOL)isPositionTop { | |
| 234 | + return [[self position] isEqualToString: DANMU_POSITION_TOP]; | |
| 235 | +} | |
| 236 | + | |
| 237 | +- (BOOL)isPositionMiddle { | |
| 238 | + return [[self position] isEqualToString: DANMU_POSITION_MIDDLE]; | |
| 239 | +} | |
| 240 | + | |
| 241 | +- (BOOL)isPositionBottom { | |
| 242 | + return [[self position] isEqualToString: DANMU_POSITION_BOTTOM]; | |
| 243 | +} | |
| 244 | + | |
| 245 | +/* | |
| 246 | +// Only override drawRect: if you perform custom drawing. | |
| 247 | +// An empty implementation adversely affects performance during animation. | |
| 248 | +- (void)drawRect:(CGRect)rect { | |
| 249 | + // Drawing code | |
| 250 | +} | |
| 251 | +*/ | |
| 252 | + | |
| 253 | +@end | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/Danmu/LVCDanmuManager.h
0 → 100644
| 1 | +// | |
| 2 | +// LVCDanmuManager.h | |
| 3 | +// LiveVideoCloud | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/28. | |
| 6 | +// Copyright © 2017年 cnlive. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import <UIKit/UIKit.h> | |
| 11 | +#import "LVCDanmuUtil.h" | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * 弹幕状态 | |
| 15 | + */ | |
| 16 | +typedef NS_ENUM(NSUInteger, DanmuManagerState) { | |
| 17 | + /** | |
| 18 | + * 弹幕准备 | |
| 19 | + */ | |
| 20 | + DanmuManagerStateWait = 1, | |
| 21 | + /** | |
| 22 | + * 弹幕关闭 | |
| 23 | + */ | |
| 24 | + DanmuManagerStateStop, | |
| 25 | + /** | |
| 26 | + * 弹幕运动 | |
| 27 | + */ | |
| 28 | + DanmuManagerStateAnimationing, | |
| 29 | + /** | |
| 30 | + * 弹幕暂停 | |
| 31 | + */ | |
| 32 | + DanmuManagerStatePause | |
| 33 | +}; | |
| 34 | + | |
| 35 | +@interface LVCDanmuManager : NSObject | |
| 36 | + | |
| 37 | +/** | |
| 38 | + * 弹幕添加的UIView | |
| 39 | + */ | |
| 40 | +@property (nonatomic, strong, readonly) UIView *danmuView; | |
| 41 | +/** | |
| 42 | + * 弹幕状态 | |
| 43 | + */ | |
| 44 | +@property (nonatomic, readonly) DanmuManagerState danmuManagerState; | |
| 45 | +/** | |
| 46 | + * 创建弹幕管理对象 | |
| 47 | + * | |
| 48 | + * @param frame 弹幕显示的frame | |
| 49 | + * @param infos 弹幕信息,数组集合 | |
| 50 | + * @param view 弹幕添加的UIView | |
| 51 | + * @param time 弹幕刷新的间隔时间,一般1秒 | |
| 52 | + * | |
| 53 | + * @return | |
| 54 | + */ | |
| 55 | +- (instancetype)initWithFrame:(CGRect)frame data:(NSArray *)infos inView:(UIView *)view durationTime:(NSUInteger)time; | |
| 56 | +/** | |
| 57 | + * 弹幕初始化创建 | |
| 58 | + */ | |
| 59 | +- (void)initStart; | |
| 60 | +/** | |
| 61 | + * 弹幕滚动 | |
| 62 | + * | |
| 63 | + * @param startTime 目前的时间点 | |
| 64 | + */ | |
| 65 | +- (void)rollDanmu:(NSTimeInterval)startTime; | |
| 66 | +/** | |
| 67 | + * 弹幕关闭 | |
| 68 | + */ | |
| 69 | +- (void)stop; | |
| 70 | +/** | |
| 71 | + * 弹幕暂停 | |
| 72 | + */ | |
| 73 | +- (void)pause; | |
| 74 | +/** | |
| 75 | + * 弹幕继续 | |
| 76 | + * | |
| 77 | + * @param nowTime 现在的时间点 | |
| 78 | + */ | |
| 79 | +- (void)resume:(NSTimeInterval)nowTime; | |
| 80 | +/** | |
| 81 | + * 弹幕重新恢复 | |
| 82 | + */ | |
| 83 | +- (void)restart; | |
| 84 | +/** | |
| 85 | + * 发射弹幕 | |
| 86 | + * | |
| 87 | + * @param info 弹幕信息,字典集合 | |
| 88 | + */ | |
| 89 | +- (void)insertDanmu:(NSDictionary *)info; | |
| 90 | +/** | |
| 91 | + * 重置弹幕 | |
| 92 | + * | |
| 93 | + * @param frame 弹幕显示的frame | |
| 94 | + * @param infos 弹幕信息,数组集合 | |
| 95 | + * @param view 弹幕添加的UIView | |
| 96 | + * @param time 弹幕刷新的间隔时间,一般1秒 | |
| 97 | + */ | |
| 98 | +- (void)resetDanmuWithFrame:(CGRect)frame data:(NSArray *)infos inView:(UIView *)view durationTime:(NSUInteger)time; | |
| 99 | +/** | |
| 100 | + * 重置弹幕 | |
| 101 | + * | |
| 102 | + * @param frame 弹幕显示的frame | |
| 103 | + */ | |
| 104 | +- (void)resetDanmuWithFrame:(CGRect)frame; | |
| 105 | + | |
| 106 | + | |
| 107 | +@end | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/Danmu/LVCDanmuManager.m
0 → 100644
| 1 | +// | |
| 2 | +// LVCDanmuManager.m | |
| 3 | +// LiveVideoCloud | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/28. | |
| 6 | +// Copyright © 2017年 cnlive. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "LVCDanmuManager.h" | |
| 10 | + | |
| 11 | +#import "LVCDanmuLabel.h" | |
| 12 | +#import "LVCDanmuView.h" | |
| 13 | + | |
| 14 | +#define CHANNEL_WIDTH_MAX 120 | |
| 15 | +#define CHANNEL_SPACE 10 | |
| 16 | + | |
| 17 | +struct DanmuPositionStruct { | |
| 18 | + NSInteger start; | |
| 19 | + NSInteger length; | |
| 20 | +}; | |
| 21 | +typedef struct DanmuPositionStruct DanmuPositionStruct; | |
| 22 | + | |
| 23 | +@interface LVCDanmuManager () | |
| 24 | + | |
| 25 | +@property (nonatomic, strong, readwrite) UIView *danmuView; | |
| 26 | +@property (nonatomic, readwrite) DanmuManagerState danmuManagerState; | |
| 27 | + | |
| 28 | +@property (nonatomic) CGRect frame; | |
| 29 | +@property (nonatomic, strong) NSArray *infos; | |
| 30 | +@property (nonatomic, weak) UIView *superView; | |
| 31 | +@property (nonatomic) NSUInteger durationTime;//添加弹幕的间隔时间 | |
| 32 | +@property (nonatomic) NSUInteger currentIndex; | |
| 33 | + | |
| 34 | +@property (nonatomic, strong) NSMutableArray *arRollChannelInfo; | |
| 35 | +@property (nonatomic) NSUInteger countChannel; | |
| 36 | + | |
| 37 | +@property (nonatomic) DanmuPositionStruct upPosition; | |
| 38 | +@property (nonatomic) DanmuPositionStruct middlePosition; | |
| 39 | +@property (nonatomic) DanmuPositionStruct downPosition; | |
| 40 | + | |
| 41 | +@end | |
| 42 | + | |
| 43 | +@implementation LVCDanmuManager | |
| 44 | + | |
| 45 | +- (void)dealloc { | |
| 46 | + _infos = nil; | |
| 47 | + _danmuView = nil; | |
| 48 | + _arRollChannelInfo = nil; | |
| 49 | +} | |
| 50 | + | |
| 51 | +- (instancetype)initWithFrame:(CGRect)frame data:(NSArray *)infos inView:(UIView *)view durationTime:(NSUInteger)time { | |
| 52 | + self = [super init]; | |
| 53 | + if (self) { | |
| 54 | + self.frame = frame; | |
| 55 | + self.infos = infos; | |
| 56 | + self.superView = view; | |
| 57 | + self.durationTime = time; | |
| 58 | + | |
| 59 | + [self p_initInfo]; | |
| 60 | + } | |
| 61 | + return self; | |
| 62 | +} | |
| 63 | + | |
| 64 | +#pragma mark - Private | |
| 65 | + | |
| 66 | +- (void)p_initInfo { | |
| 67 | + _countChannel = self.frame.size.height/CHANNEL_HEIGHT; | |
| 68 | + | |
| 69 | + self.arRollChannelInfo = [NSMutableArray arrayWithCapacity:_countChannel]; | |
| 70 | + | |
| 71 | + NSUInteger sectionLines = nearbyintf((CGFloat)_countChannel / 3); | |
| 72 | + NSUInteger firstLines = MAX(_countChannel - sectionLines*2, sectionLines); | |
| 73 | + //滚动航道布局 | |
| 74 | + //上中下,假设10,上:0-3,中:4-6,下:7-9 | |
| 75 | + _upPosition = (DanmuPositionStruct){0, firstLines}; | |
| 76 | + _middlePosition = (DanmuPositionStruct){_upPosition.length, sectionLines}; | |
| 77 | + _downPosition = (DanmuPositionStruct){_middlePosition.start + _middlePosition.length, _countChannel - _middlePosition.start - _middlePosition.length}; | |
| 78 | + | |
| 79 | + _danmuManagerState = DanmuManagerStateWait; | |
| 80 | +} | |
| 81 | + | |
| 82 | +- (void)p_initData { | |
| 83 | + [self.arRollChannelInfo removeAllObjects]; | |
| 84 | + | |
| 85 | + for (int i = 0; i < _countChannel; i++) { | |
| 86 | + [self.arRollChannelInfo addObject:[NSNumber numberWithInt:i]]; | |
| 87 | + } | |
| 88 | + | |
| 89 | + self.currentIndex = 0; | |
| 90 | + [self.danmuView removeFromSuperview]; | |
| 91 | + self.danmuView = nil; | |
| 92 | + self.danmuView = [[LVCDanmuView alloc] initWithFrame:_frame]; | |
| 93 | + [self.superView addSubview:self.danmuView]; | |
| 94 | + | |
| 95 | + _danmuManagerState = DanmuManagerStateWait; | |
| 96 | +} | |
| 97 | + | |
| 98 | +- (void)p_danmu:(NSTimeInterval)startTime { | |
| 99 | + __weak typeof(self) weakSelf = self; | |
| 100 | + | |
| 101 | + __block NSMutableArray *danmuInfos = [NSMutableArray new]; | |
| 102 | + __block NSUInteger index = 0; | |
| 103 | + | |
| 104 | + for (int i = (int)self.currentIndex; i < self.infos.count; i++) { | |
| 105 | + NSDictionary *obj = [self.infos objectAtIndex:i]; | |
| 106 | + CGFloat time = [[obj objectForKey:kDanmuTimeKey] floatValue]; | |
| 107 | + if (time >= startTime && time < startTime + weakSelf.durationTime) { | |
| 108 | + [danmuInfos addObject:obj]; | |
| 109 | + } | |
| 110 | + if (time >= startTime + weakSelf.durationTime) { | |
| 111 | + index = i; | |
| 112 | + break; | |
| 113 | + } | |
| 114 | + } | |
| 115 | + | |
| 116 | + if (danmuInfos.count > 0) { | |
| 117 | + self.currentIndex = index; | |
| 118 | + | |
| 119 | + void(^func)(LVCDanmuLabel *danmuLabel, NSUInteger idx, CGFloat offsetXY) = ^(LVCDanmuLabel *danmuLabel, NSUInteger idx, CGFloat offsetXY){ | |
| 120 | + if (idx != NSNotFound) { | |
| 121 | + [danmuLabel setDanmuChannel:idx offset:offsetXY]; | |
| 122 | + CGFloat time = [danmuLabel startTime]; | |
| 123 | + time = time - startTime; | |
| 124 | + [danmuLabel animationDanmuItem:time]; | |
| 125 | + } | |
| 126 | + }; | |
| 127 | + | |
| 128 | + [danmuInfos enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) { | |
| 129 | + __block LVCDanmuLabel *danmuLabel = [LVCDanmuLabel createWithInfo:obj inView:weakSelf.danmuView]; | |
| 130 | + [weakSelf p_getRollBestChannel:danmuLabel completion:^(NSUInteger idx, CGFloat offsetX) { | |
| 131 | + func(danmuLabel, idx, offsetX); | |
| 132 | + }]; | |
| 133 | + }]; | |
| 134 | + } | |
| 135 | +} | |
| 136 | + | |
| 137 | +- (void)p_getRollBestChannel:(LVCDanmuLabel *)newDanmuL completion:(void(^)(NSUInteger idx, CGFloat offsetX))completion { | |
| 138 | + DanmuPositionStruct danmuPosition; | |
| 139 | + if (newDanmuL.isPositionMiddle) { | |
| 140 | + danmuPosition = _middlePosition; | |
| 141 | + } | |
| 142 | + else if (newDanmuL.isPositionBottom) { | |
| 143 | + danmuPosition = _downPosition; | |
| 144 | + } | |
| 145 | + else { | |
| 146 | + danmuPosition = _upPosition; | |
| 147 | + } | |
| 148 | + | |
| 149 | + NSUInteger index = danmuPosition.start; | |
| 150 | + BOOL bFind = NO; | |
| 151 | + for (int i = (int)danmuPosition.start; i < danmuPosition.start + danmuPosition.length; i++) { | |
| 152 | + id obj = [self.arRollChannelInfo objectAtIndex:i]; | |
| 153 | + index = i; | |
| 154 | + if ([obj isKindOfClass:[LVCDanmuLabel class]]) { | |
| 155 | + bFind = [self p_last:obj new:newDanmuL]; | |
| 156 | + }else { | |
| 157 | + bFind = YES; | |
| 158 | + } | |
| 159 | + | |
| 160 | + if (bFind) | |
| 161 | + break; | |
| 162 | + } | |
| 163 | + | |
| 164 | + if (bFind) { | |
| 165 | + id obj = [self.arRollChannelInfo objectAtIndex:index]; | |
| 166 | + [self.arRollChannelInfo replaceObjectAtIndex:index withObject:newDanmuL]; | |
| 167 | + if ([obj isKindOfClass:[LVCDanmuLabel class]]) { | |
| 168 | + CGFloat x = ((LVCDanmuLabel *)obj).currentRightX; | |
| 169 | + completion(index, x < 0 ? 0 : x); | |
| 170 | + }else | |
| 171 | + completion(index, 0); | |
| 172 | + }else { | |
| 173 | + if (index < danmuPosition.start + danmuPosition.length - 1) { | |
| 174 | + index += 1; | |
| 175 | + [self.arRollChannelInfo replaceObjectAtIndex:index withObject:newDanmuL]; | |
| 176 | + completion(index, 0); | |
| 177 | + } | |
| 178 | + else { | |
| 179 | + NSUInteger index = NSNotFound; | |
| 180 | + index = [self p_allChannelWithPosition:danmuPosition new:newDanmuL]; | |
| 181 | + if (index != NSNotFound) { | |
| 182 | + LVCDanmuLabel *obj = [self.arRollChannelInfo objectAtIndex:index]; | |
| 183 | + [self.arRollChannelInfo replaceObjectAtIndex:index withObject:newDanmuL]; | |
| 184 | + CGFloat x = obj.currentRightX; | |
| 185 | + completion(index, x < CHANNEL_SPACE ? CHANNEL_SPACE : x); | |
| 186 | + }else | |
| 187 | + completion(NSNotFound, 0); | |
| 188 | + } | |
| 189 | + } | |
| 190 | +} | |
| 191 | + | |
| 192 | +//选择完全不会碰撞的航道 | |
| 193 | +- (BOOL)p_last:(LVCDanmuLabel *)lastDanmuL new:(LVCDanmuLabel *)newDanmuL { | |
| 194 | + CGFloat durationTime = newDanmuL.startTime - lastDanmuL.startTime; | |
| 195 | + if (durationTime > newDanmuL.animationDuartion) { | |
| 196 | + return YES; | |
| 197 | + } | |
| 198 | + CGFloat timeS = lastDanmuL.frame.size.width/lastDanmuL.speed; | |
| 199 | + if (timeS >= durationTime) { | |
| 200 | + return NO; | |
| 201 | + } | |
| 202 | + CGFloat timeE = newDanmuL.currentRightX/newDanmuL.speed; | |
| 203 | + if (timeE <= durationTime) { | |
| 204 | + return NO; | |
| 205 | + } | |
| 206 | + | |
| 207 | + return YES; | |
| 208 | +} | |
| 209 | + | |
| 210 | +//选择在不超出缓冲区的且缓冲区最长的航道 | |
| 211 | +- (NSUInteger)p_allChannelWithPosition:(DanmuPositionStruct)danmuPosition new:(LVCDanmuLabel *)newDanmuL { | |
| 212 | + CGFloat width = CHANNEL_WIDTH_MAX; | |
| 213 | + NSUInteger index = NSNotFound; | |
| 214 | + for (int i = (int)danmuPosition.start; i < danmuPosition.start + danmuPosition.length; i++) {id obj = [self.arRollChannelInfo objectAtIndex:i]; | |
| 215 | + if ([obj isKindOfClass:[LVCDanmuLabel class]]) { | |
| 216 | + CGFloat rightX = ((LVCDanmuLabel *)obj).currentRightX; | |
| 217 | + if (rightX <= CHANNEL_WIDTH_MAX) { | |
| 218 | + CGFloat xx = rightX; | |
| 219 | + if (xx < width) { | |
| 220 | + width = xx; | |
| 221 | + index = i; | |
| 222 | + } | |
| 223 | + } | |
| 224 | + } | |
| 225 | + } | |
| 226 | + | |
| 227 | + return index; | |
| 228 | +} | |
| 229 | + | |
| 230 | +- (NSUInteger)p_arDanmuLabel:(NSMutableArray *)arDanmuLs position:(DanmuPositionStruct)danmuPosition new:(LVCDanmuLabel *)newDanmuL { | |
| 231 | + BOOL bFind = NO; | |
| 232 | + NSUInteger index = danmuPosition.start; | |
| 233 | + for (int i = (int)danmuPosition.start; i < danmuPosition.start + danmuPosition.length; i++) { | |
| 234 | + id obj = [arDanmuLs objectAtIndex:i]; | |
| 235 | + index = i; | |
| 236 | + if ([obj isKindOfClass:[LVCDanmuLabel class]]) { | |
| 237 | + LVCDanmuLabel *lastDanmuL = obj; | |
| 238 | + CGFloat durationTime = newDanmuL.startTime - lastDanmuL.startTime; | |
| 239 | + bFind = (durationTime > (newDanmuL.animationDuartion - 1)); | |
| 240 | + }else { | |
| 241 | + bFind = YES; | |
| 242 | + } | |
| 243 | + | |
| 244 | + if (bFind) | |
| 245 | + break; | |
| 246 | + } | |
| 247 | + if (!bFind) { | |
| 248 | + if (index < danmuPosition.start + danmuPosition.length - 1) | |
| 249 | + index += 1; | |
| 250 | + else | |
| 251 | + index = NSNotFound; | |
| 252 | + } | |
| 253 | + | |
| 254 | + return index; | |
| 255 | +} | |
| 256 | + | |
| 257 | +#pragma mark - Action | |
| 258 | + | |
| 259 | +- (void)initStart { | |
| 260 | + if (_danmuManagerState == DanmuManagerStateWait || | |
| 261 | + _danmuManagerState == DanmuManagerStateStop) { | |
| 262 | + [self p_initData]; | |
| 263 | + } | |
| 264 | +} | |
| 265 | + | |
| 266 | +- (void)rollDanmu:(NSTimeInterval)startTime { | |
| 267 | + if (_danmuManagerState == DanmuManagerStateStop) | |
| 268 | + return; | |
| 269 | + if (_danmuManagerState != DanmuManagerStateAnimationing) | |
| 270 | + _danmuManagerState = DanmuManagerStateAnimationing; | |
| 271 | + | |
| 272 | + if ((NSInteger)startTime % _durationTime == 0) { | |
| 273 | + [self p_danmu:startTime]; | |
| 274 | + } | |
| 275 | +} | |
| 276 | + | |
| 277 | +- (void)stop { | |
| 278 | + _danmuManagerState = DanmuManagerStateStop; | |
| 279 | + [self.arRollChannelInfo removeAllObjects]; | |
| 280 | + [self.danmuView.subviews makeObjectsPerformSelector:@selector(removeDanmu)]; | |
| 281 | + [self.danmuView removeFromSuperview]; | |
| 282 | +} | |
| 283 | + | |
| 284 | +- (void)pause { | |
| 285 | + if (_danmuManagerState != DanmuManagerStateAnimationing) | |
| 286 | + return; | |
| 287 | + _danmuManagerState = DanmuManagerStatePause; | |
| 288 | + [self.danmuView.subviews makeObjectsPerformSelector:@selector(pause)]; | |
| 289 | +} | |
| 290 | + | |
| 291 | +- (void)resume:(NSTimeInterval)nowTime { | |
| 292 | + if (_danmuManagerState != DanmuManagerStatePause) | |
| 293 | + return; | |
| 294 | + _danmuManagerState = DanmuManagerStateAnimationing; | |
| 295 | + for (id subview in self.danmuView.subviews) { | |
| 296 | + if ([subview isKindOfClass:[LVCDanmuLabel class]]) { | |
| 297 | + [(LVCDanmuLabel *)subview resume:nowTime]; | |
| 298 | + } | |
| 299 | + } | |
| 300 | +} | |
| 301 | + | |
| 302 | +- (void)restart { | |
| 303 | + [self p_initData]; | |
| 304 | + _danmuManagerState = DanmuManagerStateAnimationing; | |
| 305 | +} | |
| 306 | + | |
| 307 | +- (void)insertDanmu:(NSDictionary *)info { | |
| 308 | + __block LVCDanmuLabel *danmuLabel = [LVCDanmuLabel createWithInfo:info inView:self.danmuView]; | |
| 309 | + [self p_getRollBestChannel:danmuLabel completion:^(NSUInteger idx, CGFloat offsetX) { | |
| 310 | + if (idx != NSNotFound) { | |
| 311 | + [danmuLabel setDanmuChannel:idx offset:offsetX]; | |
| 312 | + } | |
| 313 | + }]; | |
| 314 | +} | |
| 315 | + | |
| 316 | +- (void)resetDanmuWithFrame:(CGRect)frame data:(NSArray *)infos inView:(UIView *)view durationTime:(NSUInteger)time { | |
| 317 | + self.frame = frame; | |
| 318 | + if (infos != nil) | |
| 319 | + self.infos = infos; | |
| 320 | + self.superView = view; | |
| 321 | + self.durationTime = time; | |
| 322 | + | |
| 323 | + [self p_initInfo]; | |
| 324 | +} | |
| 325 | + | |
| 326 | +- (void)resetDanmuWithFrame:(CGRect)frame { | |
| 327 | + self.frame = frame; | |
| 328 | + [self p_initInfo]; | |
| 329 | +} | |
| 330 | + | |
| 331 | + | |
| 332 | +@end | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/Danmu/LVCDanmuUtil.h
0 → 100644
| 1 | +// | |
| 2 | +// LVCDanmuUtil.h | |
| 3 | +// LiveVideoCloud | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/28. | |
| 6 | +// Copyright © 2017年 cnlive. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <Foundation/Foundation.h> | |
| 10 | +#import <UIKit/UIKit.h> | |
| 11 | + | |
| 12 | +typedef NS_ENUM(NSUInteger, DanmuState) { | |
| 13 | + DanmuStateStop = 1, | |
| 14 | + DanmuStateAnimationing, | |
| 15 | + DanmuStateFinish | |
| 16 | +}; | |
| 17 | + | |
| 18 | +#define CHANNEL_HEIGHT 25 | |
| 19 | + | |
| 20 | +extern NSString * const kDanmuContentKey; //弹幕内容 | |
| 21 | +extern NSString * const kDanmuTimeKey; //视频时间 | |
| 22 | +extern NSString * const kDanmuOptionalKey; //弹幕样式 | |
| 23 | + | |
| 24 | +@interface LVCDanmuUtil : NSObject | |
| 25 | + | |
| 26 | ++ (CGSize)getSizeWithString:(NSString *)str withFont:(UIFont *)font size:(CGSize)size; | |
| 27 | + | |
| 28 | ++ (UIColor *)colorFromARGB:(int)argb; | |
| 29 | + | |
| 30 | ++ (NSDictionary *)defaultOptions; | |
| 31 | + | |
| 32 | ++ (NSDictionary *)randomOptions; | |
| 33 | + | |
| 34 | ++ (BOOL)isOrientationLandscape; | |
| 35 | + | |
| 36 | +@end | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/Danmu/LVCDanmuUtil.m
0 → 100644
| 1 | +// | |
| 2 | +// LVCDanmuUtil.m | |
| 3 | +// LiveVideoCloud | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/28. | |
| 6 | +// Copyright © 2017年 cnlive. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "LVCDanmuUtil.h" | |
| 10 | + | |
| 11 | +NSString * const kDanmuContentKey = @"c"; | |
| 12 | +NSString * const kDanmuTimeKey = @"v"; | |
| 13 | +NSString * const kDanmuOptionalKey = @"t"; | |
| 14 | + | |
| 15 | +@implementation LVCDanmuUtil | |
| 16 | + | |
| 17 | ++ (CGSize)getSizeWithString:(NSString *)str withFont:(UIFont *)font size:(CGSize)size { | |
| 18 | + CGRect stringRect = [str boundingRectWithSize:size | |
| 19 | + options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) | |
| 20 | + attributes:@{ NSFontAttributeName : font } | |
| 21 | + context:nil]; | |
| 22 | + | |
| 23 | + return stringRect.size; | |
| 24 | +} | |
| 25 | + | |
| 26 | ++ (UIColor *)colorFromARGB:(int)argb { | |
| 27 | + int blue = argb & 0xff; | |
| 28 | + int green = argb >> 8 & 0xff; | |
| 29 | + int red = argb >> 16 & 0xff; | |
| 30 | + //int alpha = argb >> 24 & 0xff; | |
| 31 | + //NSLog(@"%i, %i, %i", red, green, blue); | |
| 32 | + return [UIColor colorWithRed:red/255.f green:green/255.f blue:blue/255.f alpha: 1]; | |
| 33 | +} | |
| 34 | + | |
| 35 | ++ (long)argbFromHex:(NSString *)hex | |
| 36 | +{ | |
| 37 | + const char *cStr = [hex cStringUsingEncoding:NSASCIIStringEncoding]; | |
| 38 | + long x = strtol(cStr + 1, NULL, 16); | |
| 39 | + return x; | |
| 40 | + // return [self colorFromARGB: (int)x]; | |
| 41 | +} | |
| 42 | + | |
| 43 | ++ (NSDictionary *)defaultOptions { | |
| 44 | + return @{@"c": [NSNumber numberWithLong:[LVCDanmuUtil argbFromHex: @"#ffffff"]], | |
| 45 | + @"m": @"l", | |
| 46 | + @"p": @"t", | |
| 47 | + @"s": @"m", | |
| 48 | + @"l": @"n" | |
| 49 | + }; | |
| 50 | +} | |
| 51 | + | |
| 52 | ++ (NSDictionary *)randomOptions { | |
| 53 | + NSUInteger c = arc4random_uniform(4); | |
| 54 | + NSArray *arColor = @[@"#ffffff", @"#ff0000", @"#00ff00", @"#0000ff"]; | |
| 55 | + NSUInteger s = arc4random_uniform(3); | |
| 56 | + NSArray *arFontSize = @[@"l", @"m", @"s"]; | |
| 57 | + NSUInteger m = arc4random_uniform(4); | |
| 58 | + NSArray *arMode = @[@"l", @"f", @"l", @"l"]; | |
| 59 | + return @{@"c": [NSNumber numberWithLong:[LVCDanmuUtil argbFromHex:arColor[c]]], | |
| 60 | + @"m": arMode[m], | |
| 61 | + @"p": @"t", | |
| 62 | + @"s": arFontSize[s], | |
| 63 | + @"l": @"n" | |
| 64 | + }; | |
| 65 | +} | |
| 66 | + | |
| 67 | ++ (BOOL)isOrientationLandscape { | |
| 68 | + //if (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation])) { | |
| 69 | + if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { | |
| 70 | + return YES; | |
| 71 | + } else { | |
| 72 | + return NO; | |
| 73 | + } | |
| 74 | +} | |
| 75 | + | |
| 76 | +@end | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/Danmu/LVCDanmuView.h
0 → 100644
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/Danmu/LVCDanmuView.m
0 → 100644
| 1 | +// | |
| 2 | +// LVCDanmuView.m | |
| 3 | +// LiveVideoCloud | |
| 4 | +// | |
| 5 | +// Created by iOS on 17/3/28. | |
| 6 | +// Copyright © 2017年 cnlive. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "LVCDanmuView.h" | |
| 10 | + | |
| 11 | +@implementation LVCDanmuView | |
| 12 | + | |
| 13 | +- (instancetype)initWithFrame:(CGRect)frame { | |
| 14 | + self = [super initWithFrame:frame]; | |
| 15 | + if (self) { | |
| 16 | + self.backgroundColor = [UIColor clearColor]; | |
| 17 | + self.clipsToBounds = YES; | |
| 18 | + } | |
| 19 | + return self; | |
| 20 | +} | |
| 21 | + | |
| 22 | +/* | |
| 23 | +// Only override drawRect: if you perform custom drawing. | |
| 24 | +// An empty implementation adversely affects performance during animation. | |
| 25 | +- (void)drawRect:(CGRect)rect { | |
| 26 | + // Drawing code | |
| 27 | +} | |
| 28 | +*/ | |
| 29 | + | |
| 30 | +@end | ... | ... |
LiveVideoCloud/LiveVideoCloud/Classes/Sections/homeSections/View/LVCTelevisionView.m
| ... | ... | @@ -234,6 +234,8 @@ static NSString *otherHeaderId = @"LVCOtherHeaderReusableView"; |
| 234 | 234 | |
| 235 | 235 | } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { |
| 236 | 236 | |
| 237 | + [self.collectionView.header endRefreshing]; | |
| 238 | + | |
| 237 | 239 | NSLog(@"获取电视台数据失败"); |
| 238 | 240 | |
| 239 | 241 | }]; |
| ... | ... | @@ -274,8 +276,7 @@ static NSString *otherHeaderId = @"LVCOtherHeaderReusableView"; |
| 274 | 276 | _collectionView.dataSource = self; |
| 275 | 277 | _collectionView.delegate = self; |
| 276 | 278 | |
| 277 | -// MJRefreshGifHeader *header = [CNRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(refresh)]; | |
| 278 | -// _collectionView.header = header; | |
| 279 | + _collectionView.header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(refresh)]; | |
| 279 | 280 | } |
| 280 | 281 | return _collectionView; |
| 281 | 282 | } | ... | ... |