├── .gitignore ├── .travis.yml ├── BarrageRenderer.podspec ├── BarrageRenderer ├── BarrageEngine │ ├── BarrageCanvas.h │ ├── BarrageCanvas.m │ ├── BarrageClock.h │ ├── BarrageClock.m │ ├── BarrageDescriptor.h │ ├── BarrageDescriptor.m │ ├── BarrageDispatcher.h │ ├── BarrageDispatcher.m │ ├── BarrageRenderer.h │ ├── BarrageRenderer.m │ ├── BarrageSpriteFactory.h │ ├── BarrageSpriteFactory.m │ ├── BarrageSpriteQueue.h │ └── BarrageSpriteQueue.m ├── BarrageHeader.h ├── BarrageLoader │ ├── BarrageLoader.h │ └── BarrageLoader.m └── BarrageSprite │ ├── BarrageFloatImageSprite.h │ ├── BarrageFloatImageSprite.m │ ├── BarrageFloatSprite.h │ ├── BarrageFloatSprite.m │ ├── BarrageFloatTextSprite.h │ ├── BarrageFloatTextSprite.m │ ├── BarrageSprite.h │ ├── BarrageSprite.m │ ├── BarrageSpriteProtocol.h │ ├── BarrageSpriteUtility.h │ ├── BarrageSpriteUtility.m │ ├── BarrageViewPool.h │ ├── BarrageViewPool.m │ ├── BarrageWalkImageSprite.h │ ├── BarrageWalkImageSprite.m │ ├── BarrageWalkSprite.h │ ├── BarrageWalkSprite.m │ ├── BarrageWalkTextSprite.h │ ├── BarrageWalkTextSprite.m │ ├── UIImageView+BarrageView.h │ ├── UIImageView+BarrageView.m │ ├── UILabel+BarrageView.h │ ├── UILabel+BarrageView.m │ ├── UIView+BarrageView.h │ └── UIView+BarrageView.m ├── BarrageRendererDemo.gif ├── BarrageRendererDemo ├── BarrageRendererDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── BarrageRendererDemo.xcscheme ├── BarrageRendererDemo.xcworkspace │ └── contents.xcworkspacedata ├── BarrageRendererDemo │ ├── AdvancedBarrage │ │ ├── AdvancedBarrageController.h │ │ ├── AdvancedBarrageController.m │ │ ├── BarrageWalkImageTextSprite.h │ │ ├── BarrageWalkImageTextSprite.m │ │ ├── MLEmojiLabel+BarrageView.h │ │ └── MLEmojiLabel+BarrageView.m │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── BarrageDemo │ │ ├── AvatarBarrageView.h │ │ ├── AvatarBarrageView.m │ │ ├── FlowerBarrageSprite.h │ │ └── FlowerBarrageSprite.m │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── CommonBarrage │ │ ├── CommonBarrageController.h │ │ └── CommonBarrageController.m │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── SafeObject │ │ ├── NSSafeObject.h │ │ └── NSSafeObject.m │ ├── UIImage+Barrage.h │ ├── UIImage+Barrage.m │ ├── avatar.png │ └── main.m ├── Podfile ├── Podfile.lock └── SafeObject │ ├── NSSafeObject.h │ └── NSSafeObject.m ├── BarrageRendererDemo1.gif ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | Pods/ 27 | .browse.VC.db 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9 3 | xcode_sdk: iphonesimulator10.0 4 | podfile: ./BarrageRendererDemo/Podfile 5 | script: 6 | - set -o pipefail 7 | - travis_retry xcodebuild -workspace ./BarrageRendererDemo/BarrageRendererDemo.xcworkspace -scheme "BarrageRendererDemo" -destination "platform=iOS Simulator,name=iPhone 6" | xcpretty 8 | -------------------------------------------------------------------------------- /BarrageRenderer.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "BarrageRenderer" 3 | s.version = "0.0.1" 4 | s.summary = "With BarrageRenderer, you can easily create barrage or danmaku in your apps." 5 | s.homepage = "https://github.com/unash/BarrageRenderer.git" 6 | s.license = { :type => 'MIT License', :file => 'LICENSE' } 7 | s.author = { "unash" => "unash@exbye.com" } 8 | s.platform = :ios, '6.0' 9 | s.source = { :git => "https://github.com/unash/BarrageRenderer.git", :branch => "master" } 10 | s.source_files = "BarrageRenderer/*.{h,m}","BarrageRenderer/*/*.{h,m}" 11 | end -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageCanvas.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @interface BarrageCanvas : UIView 30 | @property(nonatomic, assign)UIEdgeInsets margin; 31 | @property(nonatomic, assign)BOOL masked; // canvas是否拦截事件, 默认 YES. 32 | @end 33 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageCanvas.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageCanvas.h" 28 | 29 | @implementation BarrageCanvas 30 | 31 | - (instancetype)init 32 | { 33 | if (self = [super init]) { 34 | self.userInteractionEnabled = NO; 35 | self.backgroundColor = [UIColor clearColor]; 36 | self.clipsToBounds = YES; 37 | _margin = UIEdgeInsetsZero; 38 | _masked = YES; 39 | } 40 | return self; 41 | } 42 | 43 | - (void)layoutSubviews 44 | { 45 | [super layoutSubviews]; 46 | if (self.superview) { 47 | CGRect frame = UIEdgeInsetsInsetRect(self.superview.bounds, self.margin); 48 | if (!CGRectEqualToRect(self.frame, frame)) { 49 | self.frame = frame; 50 | } 51 | } 52 | } 53 | 54 | - (void)setMargin:(UIEdgeInsets)margin 55 | { 56 | if(!UIEdgeInsetsEqualToEdgeInsets(margin, _margin)) 57 | { 58 | _margin = margin; 59 | [self setNeedsLayout]; 60 | } 61 | } 62 | 63 | - (void)didMoveToSuperview 64 | { 65 | [self setNeedsLayout]; 66 | [self layoutIfNeeded]; 67 | } 68 | 69 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 70 | { 71 | if (self.masked) return [super hitTest:point withEvent:event]; 72 | for (UIView *view in self.subviews) { 73 | UIView *responder = [view hitTest:[view convertPoint:point fromView:self] withEvent:event]; 74 | if (responder) return responder; 75 | } 76 | return nil; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageClock.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | /// 时间引擎 29 | @interface BarrageClock : NSObject 30 | 31 | /// 通过回调block初始化时钟,block中返回逻辑时间,其值会受到speed的影响. 32 | + (instancetype)clockWithHandler:(void (^)(NSTimeInterval time))block; 33 | 34 | /// 时间流速,默认值为1.0f; 设置必须大于0,否则无效. 35 | @property(nonatomic,assign)CGFloat speed; 36 | 37 | /// 启动时间引擎,根据刷新频率返回逻辑时间. 38 | - (void)start; 39 | 40 | /// 关闭时间引擎; 一些都已结束,或者重新开始,或者归于沉寂. 41 | - (void)stop; 42 | 43 | /// 暂停,相等于把speed置为0; 不过通过start可以恢复. 44 | - (void)pause; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageClock.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageClock.h" 28 | @interface BarrageClock() 29 | { 30 | void (^_block)(NSTimeInterval time); 31 | CADisplayLink * _displayLink; // 周期 32 | CFTimeInterval _previousDate; // 上一次更新时间 33 | CGFloat _pausedSpeed; // 暂停之前的时间流速 34 | } 35 | ///是否处于启动状态 36 | @property(nonatomic,assign)BOOL launched; 37 | ///逻辑时间 38 | @property(nonatomic,assign)NSTimeInterval time; 39 | @end 40 | 41 | @implementation BarrageClock 42 | 43 | + (instancetype)clockWithHandler:(void (^)(NSTimeInterval time))block 44 | { 45 | BarrageClock * clock = [[BarrageClock alloc]initWithHandler:block]; 46 | return clock; 47 | } 48 | 49 | - (instancetype)initWithHandler:(void (^)(NSTimeInterval time))block 50 | { 51 | if (self = [super init]) { 52 | _block = block; 53 | } 54 | return self; 55 | } 56 | 57 | - (void)reset 58 | { 59 | _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)]; 60 | _speed = 1.0f; 61 | _pausedSpeed = _speed; 62 | self.launched = NO; 63 | } 64 | 65 | - (void)update 66 | { 67 | [self updateTime]; 68 | _block(self.time); 69 | } 70 | 71 | - (void)start 72 | { 73 | if (!_displayLink) { 74 | [self reset]; 75 | } 76 | 77 | if (self.launched) { 78 | _speed = _pausedSpeed; 79 | } else { 80 | _previousDate = CACurrentMediaTime(); 81 | [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 82 | self.launched = YES; 83 | } 84 | } 85 | 86 | - (void)setSpeed:(CGFloat)speed 87 | { 88 | if (speed > 0.0f) { 89 | if (_speed != 0.0f) { // 非暂停状态 90 | _speed = speed; 91 | } 92 | _pausedSpeed = speed; 93 | } 94 | } 95 | 96 | - (void)pause 97 | { 98 | _speed = 0.0f; 99 | } 100 | 101 | - (void)stop 102 | { 103 | [_displayLink invalidate]; 104 | _displayLink = nil; 105 | } 106 | 107 | /// 更新逻辑时间系统 108 | - (void)updateTime 109 | { 110 | CFTimeInterval currentDate = CACurrentMediaTime(); 111 | self.time += (currentDate - _previousDate) * self.speed; 112 | _previousDate = currentDate; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageDescriptor.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import "BarrageSpriteProtocol.h" 29 | /// 统一的持久化层 30 | @interface BarrageDescriptor : NSObject 31 | 32 | //TODO: 待实现 33 | - (instancetype)initWithString:(NSString *)xml; 34 | 35 | /// 类名,支持的类名参照BarrageSprite子类 36 | @property(nonatomic,strong)NSString * spriteName; 37 | 38 | /// 属性字典 39 | @property(nonatomic,strong,readonly)NSMutableDictionary * params; 40 | 41 | /// 弹幕标识符,用于防止弹幕重复,内部一次性生成 42 | @property(nonatomic,strong,readonly)NSString * identifier; 43 | 44 | /// 弹幕点击事件回调 45 | - (void)clickAction:(BarrageClickAction)clickAction; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageDescriptor.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageDescriptor.h" 28 | 29 | @interface BarrageDescriptor() 30 | { 31 | NSString * _xml; 32 | } 33 | @end 34 | 35 | @implementation BarrageDescriptor 36 | 37 | - (instancetype)initWithString:(NSString *)xml 38 | { 39 | if (self = [super init]) { 40 | _xml = xml; 41 | _params = [[NSMutableDictionary alloc]init]; 42 | //TODO: 必须要加载 _identifier 字段 43 | } 44 | return self; 45 | } 46 | 47 | - (instancetype)init 48 | { 49 | if (self = [super init]) { 50 | _identifier = [[NSProcessInfo processInfo]globallyUniqueString]; 51 | _params = [[NSMutableDictionary alloc]init]; 52 | _params[@"identifier"] = _identifier; 53 | } 54 | return self; 55 | } 56 | 57 | - (instancetype)copyWithZone:(NSZone *)zone 58 | { 59 | BarrageDescriptor * copy = [BarrageDescriptor allocWithZone:zone]; 60 | copy->_params = [self.params mutableCopy]; 61 | copy->_spriteName = self.spriteName; 62 | copy->_identifier = [[NSProcessInfo processInfo]globallyUniqueString]; 63 | return copy; 64 | } 65 | 66 | - (void)clickAction:(BarrageClickAction)clickAction{ 67 | 68 | self.params[@"clickAction"] = clickAction; 69 | 70 | } 71 | @end 72 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageDispatcher.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @class BarrageSprite; 30 | @class BarrageDispatcher; 31 | 32 | @protocol BarrageDispatcherDelegate 33 | 34 | @optional 35 | /// 是否激活此弹幕精灵. 36 | - (BOOL)shouldActiveSprite:(BarrageSprite *)sprite; 37 | - (NSTimeInterval)timeForBarrageDispatcher:(BarrageDispatcher *)dispatcher; 38 | 39 | @required 40 | - (void)willActiveSprite:(BarrageSprite *)sprite; 41 | - (void)willDeactiveSprite:(BarrageSprite *)sprite; 42 | 43 | @end 44 | 45 | /// 弹幕调度器, 主要完成负载均衡的工作. 46 | @interface BarrageDispatcher : NSObject 47 | 48 | /// 添加精灵. 49 | - (void)addSprite:(BarrageSprite *)sprite; 50 | 51 | /// 派发精灵. 52 | - (void)dispatchSprites; 53 | 54 | /// 是否开启过期精灵缓存功能, 默认关闭, 如需支持后退时重新播放弹幕, 则需置为YES. 55 | @property (nonatomic,assign)BOOL cacheDeadSprites; 56 | 57 | /// 当前活跃的精灵. 58 | @property (nonatomic,strong,readonly)NSArray * activeSprites; 59 | 60 | /// 停止当前被激活的精灵 61 | - (void)deactiveAllSprites; 62 | 63 | /// 平滑系数, 范围为[0,1],当为0时,无平滑; 否则越大,越平滑; 64 | /// 高平滑值在大量弹幕的时候(一般100+),可能造成弹幕丢失 65 | @property(nonatomic,assign)CGFloat smoothness; 66 | 67 | @property (nonatomic,weak)id delegate; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageDispatcher.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageDispatcher.h" 28 | #import "BarrageSprite.h" 29 | #import "BarrageSpriteQueue.h" 30 | 31 | @interface BarrageDispatcher() 32 | { 33 | NSMutableArray * _activeSprites; 34 | BarrageSpriteQueue *_waitingSpriteQueue; /// 当前等待的精灵队列. 35 | NSMutableArray * _deadSprites; /// 当前过期的精灵. 36 | NSTimeInterval _previousTime; 37 | CGFloat _smoothness; 38 | } 39 | 40 | @end 41 | 42 | @implementation BarrageDispatcher 43 | 44 | - (instancetype)init 45 | { 46 | if (self = [super init]) { 47 | _activeSprites = [[NSMutableArray alloc]init]; 48 | _waitingSpriteQueue = [[BarrageSpriteQueue alloc]init]; 49 | _deadSprites = [[NSMutableArray alloc]init]; 50 | _cacheDeadSprites = NO; 51 | _previousTime = 0.0f; 52 | } 53 | return self; 54 | } 55 | 56 | - (void)setDelegate:(id)delegate 57 | { 58 | _delegate = delegate; 59 | _previousTime = [self currentTime]; 60 | } 61 | 62 | - (NSTimeInterval)currentTime 63 | { 64 | if (self.delegate && [self.delegate respondsToSelector:@selector(timeForBarrageDispatcher:)]) { 65 | return [self.delegate timeForBarrageDispatcher:self]; 66 | } 67 | return 0.0f; 68 | } 69 | 70 | - (void)addSprite:(BarrageSprite *)sprite 71 | { 72 | if ([sprite isKindOfClass:[BarrageSprite class]]) { 73 | [_waitingSpriteQueue addSprite:sprite]; 74 | } 75 | } 76 | 77 | /// 停止当前被激活的精灵 78 | - (void)deactiveAllSprites 79 | { 80 | for (NSInteger i = 0; i < _activeSprites.count; i ++) { // 活跃精灵队列 81 | BarrageSprite * sprite = [_activeSprites objectAtIndex:i]; 82 | if (_cacheDeadSprites) { 83 | [_deadSprites addObject:sprite]; 84 | } 85 | [self deactiveSprite:sprite]; 86 | [_activeSprites removeObjectAtIndex:i--]; 87 | } 88 | } 89 | 90 | - (void)setSmoothness:(CGFloat)smoothness 91 | { 92 | if (smoothness<0) { 93 | smoothness = 0; 94 | } else if (smoothness > 1) { 95 | smoothness = 1; 96 | } 97 | _smoothness = smoothness; 98 | } 99 | 100 | /// 派发精灵 101 | - (void)dispatchSprites 102 | { 103 | for (NSInteger i = 0; i < _activeSprites.count; i ++) { 104 | BarrageSprite * sprite = [_activeSprites objectAtIndex:i]; 105 | if (!sprite.isValid) { 106 | if (_cacheDeadSprites) { 107 | [_deadSprites addObject:sprite]; 108 | } 109 | [self deactiveSprite:sprite]; 110 | [_activeSprites removeObjectAtIndex:i--]; 111 | } 112 | } 113 | // 弹幕最大保留时间, 当视频快进时,有可能大于timeWindow 114 | static NSTimeInterval const MAX_EXPIRED_SPRITE_RESERVED_TIME = 0.5f; // 经验值 115 | static NSTimeInterval const DISPATCHER_SMOOTH_FACTOR = 5.0f; // 经验值 116 | NSTimeInterval currentTime = [self currentTime]; 117 | NSTimeInterval timeWindow = currentTime - _previousTime; // 有可能为正,也有可能为负(如果倒退的话) 118 | // NSLog(@"内部时间:%f -- 变化时间:%f",currentTime,timeWindow); 119 | //如果是正, 可能是正常时钟,也可能是快进 120 | if (timeWindow >= 0) { 121 | BarrageSpriteQueue *queue = [_waitingSpriteQueue spriteQueueWithDelayLessThanOrEqualTo:currentTime]; 122 | NSArray *participants = [queue ascendingSprites]; 123 | NSMutableArray *candidates = [NSMutableArray arrayWithCapacity:participants.count]; 124 | 125 | for (NSInteger i = 0; i < participants.count; i++) { 126 | BarrageSprite * sprite = [participants objectAtIndex:i]; 127 | NSTimeInterval overtime = currentTime - sprite.delay; 128 | //NSLog(@"%f",overtime); 129 | if ((_smoothness>0.0f || overtime < timeWindow) && overtime <= MAX_EXPIRED_SPRITE_RESERVED_TIME) { 130 | if ([self shouldActiveSprite:sprite]) { 131 | [candidates addObject:sprite]; 132 | } else { 133 | [_waitingSpriteQueue removeSprite:sprite]; 134 | } 135 | } 136 | else 137 | { 138 | if (_cacheDeadSprites) { 139 | [_deadSprites addObject:sprite]; 140 | } 141 | [_waitingSpriteQueue removeSprite:sprite]; 142 | } 143 | } 144 | 145 | NSInteger count = candidates.count; 146 | 147 | if (_smoothness>0.0f) { //可以优化掉此判断 148 | NSInteger frequence = (NSInteger)floorf(MAX_EXPIRED_SPRITE_RESERVED_TIME * DISPATCHER_SMOOTH_FACTOR/timeWindow * _smoothness); // 估算平滑频率 149 | if (count>0 && frequence>=1) { 150 | count = MAX(1, ceil(count/frequence)); 151 | } 152 | } 153 | 154 | for (NSInteger i = 0; i < count; i++) { 155 | BarrageSprite *sprite = candidates[i]; 156 | [self activeSprite:sprite]; 157 | //NSLog(@"%@",sprite.viewParams[@"text"]); 158 | [_activeSprites addObject:sprite]; 159 | [_waitingSpriteQueue removeSprite:sprite]; 160 | } 161 | } 162 | else // 倒退,需要起死回生 163 | { 164 | for (NSInteger i = 0; i < _deadSprites.count; i++) { // 活跃精灵队列 165 | BarrageSprite * sprite = [_deadSprites objectAtIndex:i]; 166 | if (sprite.delay > currentTime) { 167 | [_waitingSpriteQueue addSprite:sprite]; 168 | [_deadSprites removeObjectAtIndex:i--]; 169 | } 170 | else if (sprite.delay == currentTime) 171 | { 172 | if ([self shouldActiveSprite:sprite]) { 173 | [self activeSprite:sprite]; 174 | [_activeSprites addObject:sprite]; 175 | [_deadSprites removeObjectAtIndex:i--]; 176 | } 177 | } 178 | } 179 | } 180 | 181 | _previousTime = currentTime; 182 | } 183 | 184 | /// 是否可以激活精灵 185 | - (BOOL)shouldActiveSprite:(BarrageSprite *)sprite 186 | { 187 | if (self.delegate && [self.delegate respondsToSelector:@selector(shouldActiveSprite:)]) { 188 | return [self.delegate shouldActiveSprite:sprite]; 189 | } 190 | return YES; 191 | } 192 | 193 | /// 激活精灵 194 | - (void)activeSprite:(BarrageSprite *)sprite 195 | { 196 | if (self.delegate && [self.delegate respondsToSelector:@selector(willActiveSprite:)]) { 197 | [self.delegate willActiveSprite:sprite]; 198 | } 199 | } 200 | 201 | /// 精灵失活 202 | - (void)deactiveSprite:(BarrageSprite *)sprite 203 | { 204 | if (self.delegate && [self.delegate respondsToSelector:@selector(willDeactiveSprite:)]) { 205 | [self.delegate willDeactiveSprite:sprite]; 206 | } 207 | } 208 | 209 | @end 210 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageRenderer.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | #import "BarrageHeader.h" 30 | 31 | @class BarrageDescriptor; 32 | @class BarrageRenderer; 33 | 34 | typedef NS_ENUM(NSInteger, BarrageSpriteStage) { 35 | BarrageSpriteStageBegin = 1, // 弹幕进入屏幕阶段 36 | BarrageSpriteStageEnd = 2 // 弹幕退出屏幕阶段 37 | }; 38 | 39 | @protocol BarrageRendererDelegate 40 | 41 | @optional 42 | /// 通过外部渠道获取当前时间,用于内部时间系统; 当依附的视频具有快进快退功能时,必须实现这个函数,并返回时间轴上的当前时刻,即已经播放的时间. 43 | - (NSTimeInterval)timeForBarrageRenderer:(BarrageRenderer *)renderer; 44 | 45 | /// 弹幕生命周期行为,实验特性; 可试用, 亦可以通过继承 BarrageRenderer 来实现相同功能 46 | - (void)barrageRenderer:(BarrageRenderer *)renderer spriteStage:(BarrageSpriteStage)stage spriteParams:(NSDictionary *)params; 47 | 48 | @end 49 | 50 | /// 弹幕渲染器 51 | @interface BarrageRenderer : NSObject 52 | 53 | #pragma mark - life cycle 54 | 55 | #pragma mark - control 56 | 57 | /// 启动弹幕, 内部时钟从0开始; 58 | /// 若是stop之后的start,则start函数内部会清空records; 59 | /// 视频开始的时候需要同时运行此方法. 60 | - (void)start; 61 | 62 | /// 暂停, 已经渲染上去的保持不变, 此时发送弹幕无效, 内部时钟暂停; 63 | /// 视频暂停的时候需要同时运行此方法. 64 | - (void)pause; 65 | 66 | /// 停止弹幕渲染, 会清空所有; 再发弹幕就无效了; 一切都会停止; 67 | /// 此方法在不再需要弹幕的时候必须调用,否则可能造成内存泄露. 68 | - (void)stop; 69 | 70 | /// 接收弹幕消息, 如果尚未start, 则调用无效. 71 | - (void)receive:(BarrageDescriptor *)descriptor; 72 | 73 | #pragma mark - config 74 | 75 | /// 画布的边距 76 | @property(nonatomic,assign)UIEdgeInsets canvasMargin; 77 | /// 画布是否拦截事件 78 | @property(nonatomic,assign)BOOL masked; 79 | 80 | /// 是否开启平滑; 对于突发弹幕,开启平滑可以降低瞬间的掉帧,默认关闭 81 | /// 对于 CPU 性能不错的手机(比如>=iphone7),推荐开启此功能 82 | /// 设置时间平滑, 应对弹幕激增; 83 | /// 范围为[0,1],当为0时,无平滑; 否则越大,越平滑; 84 | /// 高平滑值在大量弹幕(一般100+)的时候,可能造成弹幕丢失 85 | @property(nonatomic,assign)CGFloat smoothness; 86 | 87 | /// 调整弹幕整体速度, 需要>0, 否则会被抛弃. 88 | @property(nonatomic,assign)CGFloat speed; 89 | 90 | /// 如果时光倒流, 弹幕能不能重新显示. 若设置为YES, 则当执行后退8s->5s时,会显示6s时刻上的弹幕. 91 | @property(nonatomic,assign)BOOL redisplay; 92 | 93 | /// 获取外部时间的代理,若需将弹幕固定到时间点上,则需设置此代理 94 | @property(nonatomic,weak)id delegate; 95 | 96 | #pragma mark - output 97 | 98 | /// 返回给外部的view 99 | @property(nonatomic,weak)UIView * view; 100 | 101 | /// 获取当前屏幕弹幕数量,spriteName表示弹幕类名,如果传入nil,则计算屏幕显示出的所有弹幕数量. 102 | - (NSInteger)spritesNumberWithName:(NSString *)spriteName; 103 | 104 | /// 移除当前屏幕上的弹幕; 可在转屏幕的时候调用,以应对位置错乱的情况 105 | /// spriteName表示弹幕类名,如果传入nil,则计算屏幕显示出的所有弹幕数量. 106 | - (void)removePresentSpritesWithName:(NSString *)spriteName; 107 | 108 | /// 移除标识符为 identifier 的弹幕 109 | - (void)removeSpriteWithIdentifier:(NSString *)identifier; 110 | 111 | /// 逻辑时间,露出参考. 112 | @property(nonatomic,assign,readonly)NSTimeInterval time; 113 | 114 | #pragma mark - z-index 115 | 116 | /// 是否开启z-index功能,开启之后,性能会稍有降低,绘图会按照z_index进行,值越大,越靠上;默认关闭. 117 | @property(nonatomic,assign)BOOL zIndex; 118 | 119 | #pragma mark - record 120 | 121 | /// 如需要记录,需要在运行start之后立即运行此函数,内部会通过时间差计算delay; 122 | /// 记录弹幕,可能会序列化到本地; 默认为NO. 123 | @property(nonatomic,assign)BOOL recording; 124 | 125 | /// 加载已经存在的弹幕,如果已经start, 会立刻被调用receive; 否则, 会等到start的时候再调用receive. 126 | /// 在 v2.1 之后, 使用 load 不会重新调整 descriptor 的 delay; 而在之前的版本, 会调整 descriptor 的 delay 127 | - (void)load:(NSArray *)descriptors; 128 | 129 | /// 弹幕记录数组. 130 | - (NSArray *)records; 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageRenderer.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageRenderer.h" 28 | #import "BarrageCanvas.h" 29 | #import "BarrageDispatcher.h" 30 | #import "BarrageSprite.h" 31 | #import "BarrageSpriteFactory.h" 32 | #import "BarrageClock.h" 33 | #import "BarrageDescriptor.h" 34 | 35 | NSString * const kBarrageRendererContextCanvasBounds = @"kBarrageRendererContextCanvasBounds"; // 画布大小 36 | NSString * const kBarrageRendererContextRelatedSpirts = @"kBarrageRendererContextRelatedSpirts"; // 相关精灵 37 | NSString * const kBarrageRendererContextTimestamp = @"kBarrageRendererContextTimestamp"; // 时间戳 38 | 39 | @interface BarrageRenderer() 40 | { 41 | BarrageDispatcher * _dispatcher; //调度器 42 | BarrageCanvas * _canvas; // 画布 43 | BarrageClock * _clock; 44 | NSMutableDictionary * _spriteClassMap; 45 | NSMutableDictionary * _context; // 渲染器上下文 46 | 47 | NSMutableArray * _preloadedDescriptors; //预加载的弹幕 48 | NSMutableArray * _records;//记录数组 49 | NSDate * _startTime; //如果是nil,表示弹幕渲染不在运行中; 否则,表示开始的时间 50 | NSTimeInterval _pausedDuration; // 暂停持续时间 51 | NSDate * _pausedTime; // 上次暂停时间; 如果为nil, 说明当前没有暂停 52 | } 53 | @property(nonatomic,assign)NSTimeInterval time; 54 | @property(nonatomic,assign)NSTimeInterval pausedDuration; // 暂停时间 55 | @end 56 | 57 | @implementation BarrageRenderer 58 | @synthesize pausedDuration = _pausedDuration; 59 | #pragma mark - init 60 | - (instancetype)init 61 | { 62 | if (self = [super init]) { 63 | _canvas = [[BarrageCanvas alloc]init]; 64 | _spriteClassMap = [[NSMutableDictionary alloc]init]; 65 | _zIndex = NO; 66 | _context = [[NSMutableDictionary alloc]init]; 67 | _recording = NO; 68 | _startTime = nil; // 尚未开始 69 | _pausedTime = nil; 70 | _redisplay = NO; 71 | _smoothness = 0.0f; 72 | self.pausedDuration = 0; 73 | [self initClock]; 74 | } 75 | return self; 76 | } 77 | 78 | /// 初始化时钟 79 | - (void)initClock 80 | { 81 | __weak id weakSelf = self; 82 | _clock = [BarrageClock clockWithHandler:^(NSTimeInterval time){ 83 | BarrageRenderer * strongSelf = weakSelf; 84 | strongSelf.time = time; 85 | [strongSelf update]; 86 | }]; 87 | } 88 | 89 | #pragma mark - control 90 | /// 接收弹幕,并校对时间 91 | - (void)receive:(BarrageDescriptor *)descriptor 92 | { 93 | [self receive:descriptor withCorrection:YES]; 94 | } 95 | 96 | /// 接收弹幕, 并决定是否要校对时间 97 | - (void)receive:(BarrageDescriptor *)descriptor withCorrection:(BOOL)correction 98 | { 99 | dispatch_async(dispatch_get_main_queue(), ^{ 100 | if (!_startTime) { // 如果没有启动,则抛弃接收弹幕 101 | return; 102 | } 103 | BarrageDescriptor * descriptorCopy = [descriptor copy]; 104 | if (correction) { 105 | [self convertDelayTime:descriptorCopy]; 106 | } 107 | BarrageSprite * sprite = [BarrageSpriteFactory createSpriteWithDescriptor:descriptorCopy]; 108 | [_dispatcher addSprite:sprite]; 109 | if (_recording) { 110 | [self recordDescriptor:descriptorCopy]; 111 | } 112 | }); 113 | } 114 | 115 | - (void)start 116 | { 117 | // 如果之前调整过frame,_canvas的layoutSubviews不会及时调用,于是显示时会出问题 118 | // 解决bug: https://github.com/unash/BarrageRenderer/issues/16 119 | [_canvas setNeedsLayout]; 120 | if (!_startTime) { // 尚未启动,则初始化时间系统 121 | _startTime = [NSDate date]; 122 | _records = [[NSMutableArray alloc]init]; 123 | _dispatcher = [[BarrageDispatcher alloc]init]; 124 | _dispatcher.smoothness = self.smoothness; 125 | _dispatcher.cacheDeadSprites = self.redisplay; 126 | _dispatcher.delegate = self; 127 | } 128 | else if(_pausedTime) 129 | { 130 | _pausedDuration += [[NSDate date]timeIntervalSinceDate:_pausedTime]; 131 | } 132 | _pausedTime = nil; 133 | [_clock start]; 134 | if (_preloadedDescriptors.count) { 135 | for (BarrageDescriptor * descriptor in _preloadedDescriptors) { 136 | [self receive:descriptor withCorrection:NO]; 137 | } 138 | [_preloadedDescriptors removeAllObjects]; 139 | } 140 | } 141 | 142 | - (void)pause 143 | { 144 | if (!_startTime) { // 没有运行, 则暂停无效 145 | return; 146 | } 147 | if (!_pausedTime) { // 当前没有暂停 148 | [_clock pause]; 149 | _pausedTime = [NSDate date]; 150 | } 151 | else 152 | { 153 | _pausedDuration += [[NSDate date]timeIntervalSinceDate:_pausedTime]; 154 | _pausedTime = [NSDate date]; 155 | } 156 | } 157 | 158 | - (void)stop 159 | { 160 | _startTime = nil; 161 | [_clock stop]; 162 | _pausedDuration = 0.0f; 163 | [_dispatcher deactiveAllSprites]; 164 | } 165 | 166 | - (void)setSpeed:(CGFloat)speed 167 | { 168 | if (speed > 0) { 169 | _clock.speed = speed; 170 | } 171 | } 172 | 173 | - (CGFloat)speed 174 | { 175 | return _clock.speed; 176 | } 177 | 178 | - (void)setRedisplay:(BOOL)redisplay 179 | { 180 | _redisplay = redisplay; 181 | if (_dispatcher) { 182 | _dispatcher.cacheDeadSprites = _redisplay; 183 | } 184 | } 185 | 186 | - (void)setSmoothness:(CGFloat)smoothness 187 | { 188 | _smoothness = smoothness; 189 | if (_dispatcher) { 190 | _dispatcher.smoothness = smoothness; 191 | } 192 | } 193 | 194 | - (NSTimeInterval)pausedDuration 195 | { 196 | return _pausedDuration + (_pausedTime?[[NSDate date]timeIntervalSinceDate:_pausedTime]:0); // 当前处于暂停当中 197 | } 198 | 199 | /// 获取当前时间 200 | - (NSTimeInterval)currentTime 201 | { 202 | NSTimeInterval currentTime = 0.0f; 203 | if (self.delegate && [self.delegate respondsToSelector:@selector(timeForBarrageRenderer:)]) { 204 | currentTime = [self.delegate timeForBarrageRenderer:self]; 205 | } 206 | else 207 | { 208 | currentTime = [[NSDate date]timeIntervalSinceDate:_startTime]-self.pausedDuration; 209 | } 210 | return currentTime; 211 | } 212 | 213 | /// 转换descriptor的delay时间(相对于start), 如果delay<0, 则将delay置为0 214 | - (void)convertDelayTime:(BarrageDescriptor *)descriptor 215 | { 216 | NSTimeInterval delay = [[descriptor.params objectForKey:@"delay"]doubleValue]; 217 | delay += [self currentTime]; 218 | if (delay < 0) { 219 | delay = 0; 220 | } 221 | [descriptor.params setObject:@(delay) forKey:@"delay"]; 222 | } 223 | 224 | - (NSInteger)spritesNumberWithName:(NSString *)spriteName 225 | { 226 | NSInteger number = 0; 227 | if (spriteName) { 228 | Class class = NSClassFromString(spriteName); 229 | if (class) { 230 | for (BarrageSprite * sprite in _dispatcher.activeSprites) { 231 | number += [sprite class] == class; 232 | } 233 | } 234 | } 235 | else 236 | { 237 | number = _dispatcher.activeSprites.count; 238 | } 239 | return number; 240 | } 241 | 242 | - (void)removePresentSpritesWithName:(NSString *)spriteName 243 | { 244 | Class class = NSClassFromString(spriteName); 245 | for (BarrageSprite * sprite in _dispatcher.activeSprites) { 246 | if (!class || [sprite class] == class) { 247 | [sprite forceInvalid]; 248 | } 249 | } 250 | } 251 | 252 | - (void)removeSpriteWithIdentifier:(NSString *)identifier 253 | { 254 | for (BarrageSprite * sprite in _dispatcher.activeSprites) { 255 | if ([sprite.viewParams[@"identifier"] isEqualToString:identifier]) { 256 | [sprite forceInvalid]; 257 | break; 258 | } 259 | } 260 | } 261 | 262 | #pragma mark - record 263 | /// 此方法会修改desriptor的值 264 | - (void)recordDescriptor:(BarrageDescriptor *)descriptor 265 | { 266 | __block BOOL exists = NO; 267 | [_records enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL * stop){ 268 | if([((BarrageDescriptor *)obj).identifier isEqualToString:descriptor.identifier]){ 269 | exists = YES; 270 | *stop = YES; 271 | } 272 | }]; 273 | if(!exists){ 274 | [_records addObject:descriptor]; 275 | } 276 | } 277 | 278 | - (NSArray *)records 279 | { 280 | return [_records copy]; 281 | } 282 | 283 | - (void)load:(NSArray *)descriptors 284 | { 285 | if (_startTime) { 286 | for (BarrageDescriptor * descriptor in descriptors) { 287 | [self receive:descriptor withCorrection:NO]; 288 | } 289 | } 290 | else 291 | { 292 | if (!_preloadedDescriptors) { 293 | _preloadedDescriptors = [[NSMutableArray alloc]init]; 294 | } 295 | for (BarrageDescriptor * descriptor in descriptors) { 296 | [_preloadedDescriptors addObject:[descriptor copy]]; 297 | } 298 | } 299 | 300 | } 301 | 302 | #pragma mark - update 303 | /// 每个刷新周期执行一次 304 | - (void)update 305 | { 306 | [_dispatcher dispatchSprites]; // 分发精灵 307 | for (BarrageSprite * sprite in _dispatcher.activeSprites) { 308 | [sprite updateWithTime:self.time]; 309 | } 310 | } 311 | 312 | #pragma mark - BarrageDispatcherDelegate 313 | 314 | - (BOOL)shouldActiveSprite:(BarrageSprite *)sprite 315 | { 316 | return !_pausedTime; 317 | } 318 | 319 | - (void)willActiveSprite:(BarrageSprite *)sprite 320 | { 321 | NSValue * value = [NSValue valueWithCGRect:_canvas.bounds]; 322 | [_context setObject:value forKey:kBarrageRendererContextCanvasBounds]; 323 | 324 | NSArray * itemMap = [_spriteClassMap objectForKey:NSStringFromClass([sprite class])]; 325 | if (itemMap) { 326 | [_context setObject:[itemMap copy] forKey:kBarrageRendererContextRelatedSpirts]; 327 | } 328 | 329 | [_context setObject:@(self.time) forKey:kBarrageRendererContextTimestamp]; 330 | 331 | NSInteger index = [self viewIndexOfSprite:sprite]; 332 | 333 | [sprite activeWithContext:_context]; 334 | [self indexAddSprite:sprite]; 335 | [_canvas insertSubview:sprite.view atIndex:index]; 336 | if (self.delegate && [self.delegate respondsToSelector:@selector(barrageRenderer:spriteStage:spriteParams:)]) { 337 | [self.delegate barrageRenderer:self spriteStage:BarrageSpriteStageBegin spriteParams:sprite.viewParams]; 338 | } 339 | } 340 | 341 | - (NSUInteger)viewIndexOfSprite:(BarrageSprite *)sprite 342 | { 343 | NSInteger index = _dispatcher.activeSprites.count; 344 | 345 | /// 添加根据z-index 增序排列 346 | if (self.zIndex) { 347 | NSMutableArray * preSprites = [[NSMutableArray alloc]initWithArray:_dispatcher.activeSprites]; 348 | [preSprites addObject:sprite]; 349 | NSArray * sortedSprites = [preSprites sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { 350 | return [@(((BarrageSprite *)obj1).z_index) compare:@(((BarrageSprite *)obj2).z_index)]; 351 | }]; 352 | index = [sortedSprites indexOfObject:sprite]; 353 | } 354 | return index; 355 | } 356 | 357 | - (void)willDeactiveSprite:(BarrageSprite *)sprite 358 | { 359 | [self indexRemoveSprite:sprite]; 360 | [sprite.view removeFromSuperview]; 361 | [sprite deactive]; 362 | if (self.delegate && [self.delegate respondsToSelector:@selector(barrageRenderer:spriteStage:spriteParams:)]) { 363 | [self.delegate barrageRenderer:self spriteStage:BarrageSpriteStageEnd spriteParams:sprite.viewParams]; 364 | } 365 | } 366 | 367 | - (NSTimeInterval)timeForBarrageDispatcher:(BarrageDispatcher *)dispatcher 368 | { 369 | if ([dispatcher isEqual:_dispatcher]) { 370 | return [self currentTime]; 371 | } 372 | return 0.0f; // 错误情况 373 | } 374 | 375 | #pragma mark - indexing className-sprites 376 | /// 更新活跃精灵类型索引 377 | - (void)indexAddSprite:(BarrageSprite *)sprite 378 | { 379 | NSString * className = NSStringFromClass([sprite class]); 380 | NSMutableArray * itemMap = [_spriteClassMap objectForKey:className]; 381 | if (!itemMap) { 382 | itemMap = [[NSMutableArray alloc]init]; 383 | [_spriteClassMap setObject:itemMap forKey:className]; 384 | } 385 | [itemMap addObject:sprite]; 386 | } 387 | 388 | /// 更新活跃精灵类型索引 389 | - (void)indexRemoveSprite:(BarrageSprite *)sprite 390 | { 391 | NSString * className = NSStringFromClass([sprite class]); 392 | NSMutableArray * itemMap = [_spriteClassMap objectForKey:className]; 393 | if (!itemMap) { 394 | itemMap = [[NSMutableArray alloc]init]; 395 | [_spriteClassMap setObject:itemMap forKey:className]; 396 | } 397 | [itemMap removeObject:sprite]; 398 | } 399 | 400 | #pragma mark - attributes 401 | 402 | - (UIView *)view 403 | { 404 | return _canvas; 405 | } 406 | 407 | - (void)setCanvasMargin:(UIEdgeInsets)canvasMargin 408 | { 409 | _canvas.margin = canvasMargin; 410 | } 411 | 412 | - (void)setMasked:(BOOL)masked 413 | { 414 | _canvas.masked = masked; 415 | } 416 | @end 417 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageSpriteFactory.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @class BarrageSprite; 30 | @class BarrageDescriptor; 31 | 32 | @interface BarrageSpriteFactory : NSObject 33 | 34 | /// 通过描述符创建精灵 35 | + (BarrageSprite *)createSpriteWithDescriptor:(BarrageDescriptor *)descriptor; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageSpriteFactory.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSpriteFactory.h" 28 | #import "BarrageDescriptor.h" 29 | #import "BarrageSprite.h" 30 | 31 | @implementation BarrageSpriteFactory 32 | 33 | + (BarrageSprite *)createSpriteWithDescriptor:(BarrageDescriptor *)descriptor 34 | { 35 | BarrageSprite * sprite = nil; 36 | 37 | if (descriptor.spriteName.length != 0) { 38 | Class class = NSClassFromString(descriptor.spriteName); 39 | if (class) { 40 | sprite = [[class alloc]init]; 41 | } 42 | } 43 | if (sprite) { 44 | for (NSString * key in [descriptor.params allKeys]) { 45 | id value = descriptor.params[key]; 46 | [sprite setValue:value forKey:key]; 47 | } 48 | //TODO: 临时这么简单粗暴 49 | sprite.viewParams = descriptor.params; 50 | } 51 | return sprite; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageSpriteQueue.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @class BarrageSprite; 30 | @interface BarrageSpriteQueue : NSObject 31 | 32 | /// 添加精灵 33 | - (void)addSprite:(BarrageSprite *)sprite; 34 | 35 | /// 移除精灵 36 | - (void)removeSprite:(BarrageSprite *)sprite; 37 | - (void)removeSprites:(NSArray *)sprites; 38 | 39 | /// 筛选过滤 40 | - (instancetype)spriteQueueWithDelayLessThanOrEqualTo:(NSTimeInterval)delay; 41 | - (instancetype)spriteQueueWithDelayLessThan:(NSTimeInterval)delay; 42 | - (instancetype)spriteQueueWithDelayGreaterThanOrEqualTo:(NSTimeInterval)delay; 43 | - (instancetype)spriteQueueWithDelayGreaterThan:(NSTimeInterval)delay; 44 | 45 | /// 依据delay增序 46 | - (NSArray *)ascendingSprites; 47 | 48 | /// 依据delay降序 49 | - (NSArray *)descendingSprites; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageEngine/BarrageSpriteQueue.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSpriteQueue.h" 28 | #import "BarrageSprite.h" 29 | 30 | @interface BarrageSpriteQueue () 31 | @property(nonatomic,strong,readonly)NSMutableArray *sprites; // 增序排列 32 | - (instancetype)initWithAscendingSprites:(NSArray *)sprites; 33 | @end 34 | 35 | @implementation BarrageSpriteQueue 36 | 37 | - (instancetype)init 38 | { 39 | return [self initWithAscendingSprites:nil]; 40 | } 41 | 42 | - (instancetype)initWithAscendingSprites:(NSArray *)sprites 43 | { 44 | if (self = [super init]) { 45 | _sprites = sprites?[sprites mutableCopy]:[NSMutableArray new]; 46 | } 47 | return self; 48 | } 49 | 50 | #pragma mark - interface 51 | 52 | - (void)addSprite:(BarrageSprite *)sprite 53 | { 54 | NSInteger index = [self indexForSprite:sprite]; 55 | [self.sprites insertObject:sprite atIndex:index]; 56 | } 57 | 58 | - (NSArray *)ascendingSprites 59 | { 60 | return [self.sprites copy]; 61 | } 62 | 63 | - (NSArray *)descendingSprites 64 | { 65 | return [[self.sprites reverseObjectEnumerator]allObjects]; 66 | } 67 | 68 | - (void)removeSprite:(BarrageSprite *)sprite 69 | { 70 | [self.sprites removeObject:sprite]; 71 | } 72 | 73 | - (void)removeSprites:(NSArray *)sprites 74 | { 75 | [self.sprites removeObjectsInArray:sprites]; 76 | } 77 | 78 | - (instancetype)spriteQueueWithDelayLessThanOrEqualTo:(NSTimeInterval)delay 79 | { 80 | return [self spriteQueueWithDelayLessThan:delay equal:YES]; 81 | } 82 | 83 | - (instancetype)spriteQueueWithDelayLessThan:(NSTimeInterval)delay 84 | { 85 | return [self spriteQueueWithDelayLessThan:delay equal:NO]; 86 | } 87 | 88 | - (instancetype)spriteQueueWithDelayLessThan:(NSTimeInterval)delay equal:(BOOL)equal 89 | { 90 | NSInteger total = self.sprites.count; 91 | NSInteger index = [self indexForSpriteDelay:delay]; 92 | while (index <= total-1) { 93 | BarrageSprite *sprite = self.sprites[index]; 94 | if ((equal && sprite.delay > delay)||(!equal && sprite.delay>=delay)) { 95 | break; 96 | } else { 97 | index++; 98 | } 99 | } 100 | while (!equal && index>0 && self.sprites[index-1].delay==delay) { 101 | index--; 102 | } 103 | if (index < 1) { 104 | return [[BarrageSpriteQueue alloc]init]; 105 | } else { 106 | NSArray *subArray = [self.sprites subarrayWithRange:NSMakeRange(0, index)]; 107 | return [[BarrageSpriteQueue alloc]initWithAscendingSprites:subArray]; 108 | } 109 | } 110 | 111 | - (instancetype)spriteQueueWithDelayGreaterThanOrEqualTo:(NSTimeInterval)delay 112 | { 113 | return [self spriteQueueWithDelayGreaterThan:delay equal:YES]; 114 | } 115 | 116 | - (instancetype)spriteQueueWithDelayGreaterThan:(NSTimeInterval)delay 117 | { 118 | return [self spriteQueueWithDelayGreaterThan:delay equal:NO]; 119 | } 120 | 121 | - (instancetype)spriteQueueWithDelayGreaterThan:(NSTimeInterval)delay equal:(BOOL)equal 122 | { 123 | NSInteger total = self.sprites.count; 124 | NSInteger index = [self indexForSpriteDelay:delay]; 125 | index--; 126 | while (index >= 0) { 127 | BarrageSprite *sprite = self.sprites[index]; 128 | if ((equal && sprite.delay < delay)||(!equal && sprite.delay<=delay)) { 129 | break; 130 | } else { 131 | index--; 132 | } 133 | } 134 | while (!equal && index= total-1) { 138 | return [[BarrageSpriteQueue alloc]init]; 139 | } else { 140 | NSArray *subArray = [self.sprites subarrayWithRange:NSMakeRange(index+1, total-index-1)]; 141 | return [[BarrageSpriteQueue alloc]initWithAscendingSprites:subArray]; 142 | } 143 | } 144 | 145 | #pragma mark - util 146 | 147 | // 找到则返回元素在数组中的下标,如果没找到,则返回这个元素在有序数组中的位置 148 | - (NSInteger)indexForSpriteDelay:(NSTimeInterval)delay 149 | { 150 | NSInteger min = 0; 151 | NSInteger max = self.sprites.count - 1; 152 | NSInteger mid = 0; 153 | while (min <= max) { 154 | mid = (min + max) >> 1; 155 | BarrageSprite *baseSprite = self.sprites[mid]; 156 | if (delay > baseSprite.delay) { 157 | min = mid + 1; 158 | } else if (delay < baseSprite.delay) { 159 | max = mid - 1; 160 | } else { 161 | return mid; 162 | } 163 | } 164 | return min; 165 | } 166 | 167 | - (NSInteger)indexForSprite:(BarrageSprite *)sprite 168 | { 169 | return [self indexForSpriteDelay:sprite.delay]; 170 | } 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageHeader.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #ifndef demo_app_main_BarrageHeader_h 28 | #define demo_app_main_BarrageHeader_h 29 | 30 | #import "BarrageRenderer.h" 31 | #import "BarrageLoader.h" 32 | #import "BarrageDescriptor.h" 33 | 34 | #import "BarrageWalkTextSprite.h" 35 | #import "BarrageFloatTextSprite.h" 36 | #import "BarrageWalkImageSprite.h" 37 | #import "BarrageFloatImageSprite.h" 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageLoader/BarrageLoader.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @interface BarrageLoader : NSObject 30 | 31 | /// 通过文件批量获取描述符 32 | + (NSArray *)readDescriptorsWithFile:(NSString *)file; 33 | 34 | /// 将描述符批量写入文件; additional表示是否要追加,默认是NO:覆盖. 35 | + (void)writeDescriptors:(NSArray *)descriptors toFile:(NSString *)file additional:(BOOL)additional; 36 | 37 | /// 支持damaku的文件格式 38 | + (NSArray *)readDescriptorsWithDamakuFile:(NSString *)file; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageLoader/BarrageLoader.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageLoader.h" 28 | 29 | @implementation BarrageLoader 30 | //TODO: 待实现 31 | + (NSArray *)readDescriptorsWithFile:(NSString *)file 32 | { 33 | return nil; 34 | } 35 | 36 | + (NSArray *)readDescriptorsWithDamakuFile:(NSString *)file 37 | { 38 | return nil; 39 | } 40 | 41 | + (void)writeDescriptors:(NSArray *)descriptors toFile:(NSString *)file additional:(BOOL)additional 42 | { 43 | return; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageFloatImageSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatSprite.h" 28 | 29 | /// 悬浮文字精灵 30 | @interface BarrageFloatImageSprite : BarrageFloatSprite 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageFloatImageSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatImageSprite.h" 28 | 29 | @implementation BarrageFloatImageSprite 30 | 31 | - (instancetype)init 32 | { 33 | if (self = [super init]) { 34 | _viewClassName = NSStringFromClass([UIImageView class]); 35 | } 36 | return self; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageFloatSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSprite.h" 28 | 29 | typedef NS_ENUM(NSUInteger, BarrageFloatDirection) { 30 | BarrageFloatDirectionT2B = 1, // 上往下 31 | BarrageFloatDirectionB2T = 2 // 下往上 32 | }; 33 | 34 | ///注: 此处侧边的含义与过场弹幕(BarrageWalkSide)并不相同! 35 | typedef NS_ENUM(NSUInteger, BarrageFloatSide) { 36 | BarrageFloatSideCenter = 0, // 居中,默认值 37 | BarrageFloatSideRight = 1, // 靠右侧,屏幕右侧 38 | BarrageFloatSideLeft = 2 // 靠左侧,屏幕左侧 39 | }; 40 | 41 | /// 悬浮文字精灵 42 | @interface BarrageFloatSprite : BarrageSprite 43 | 44 | /// 存活时间 45 | @property(nonatomic,assign)NSTimeInterval duration; 46 | 47 | /// 方向 48 | @property(nonatomic,assign)BarrageFloatDirection direction; 49 | 50 | /// 运动侧边 51 | @property(nonatomic,assign)BarrageFloatSide side; 52 | 53 | /// 轨道数量 54 | @property(nonatomic,assign)NSUInteger trackNumber; 55 | 56 | /// 隐入时间, 默认0 57 | @property(nonatomic,assign)NSTimeInterval fadeInTime; 58 | 59 | /// 隐出时间, 默认0 60 | @property(nonatomic,assign)NSTimeInterval fadeOutTime; 61 | 62 | /// 防止碰撞,只针对同方向的弹幕有效。默认为NO。开启后,弹幕可能会丢失。 63 | @property(nonatomic,assign)BOOL avoidCollision; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageFloatSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatSprite.h" 28 | 29 | static NSUInteger const STRIP_NUM = 80; // 总共的网格条数 30 | 31 | @interface BarrageFloatSprite() 32 | { 33 | NSTimeInterval _leftActiveTime; 34 | } 35 | @end 36 | 37 | @implementation BarrageFloatSprite 38 | 39 | - (instancetype)init 40 | { 41 | if (self = [super init]) { 42 | _direction = BarrageFloatDirectionT2B; 43 | _trackNumber = 40; 44 | _fadeInTime = 0.0f; 45 | _fadeOutTime = 0.0f; 46 | _side = BarrageFloatSideCenter; 47 | _avoidCollision = NO; 48 | self.duration = 1.0f; 49 | } 50 | return self; 51 | } 52 | 53 | - (void)setDuration:(NSTimeInterval)duration 54 | { 55 | _duration = duration; 56 | _leftActiveTime = _duration; 57 | } 58 | 59 | - (void)updateWithTime:(NSTimeInterval)time 60 | { 61 | [super updateWithTime:time]; 62 | _leftActiveTime = self.duration - (time - self.timestamp); 63 | NSTimeInterval existingTime = time - self.timestamp; 64 | if (_fadeOutTime > 0 && _leftActiveTime < _fadeOutTime) { 65 | self.view.alpha = _leftActiveTime/_fadeOutTime; 66 | } 67 | if (_fadeInTime > 0 && existingTime < _fadeInTime) { 68 | self.view.alpha = existingTime/_fadeOutTime; 69 | } 70 | } 71 | 72 | - (NSTimeInterval)estimateActiveTime 73 | { 74 | return _leftActiveTime; 75 | } 76 | 77 | - (BOOL)validWithTime:(NSTimeInterval)time 78 | { 79 | return [self estimateActiveTime] > 0; 80 | } 81 | 82 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 83 | { 84 | // 获取同方向精灵 85 | NSMutableArray * synclasticSprites = [[NSMutableArray alloc]initWithCapacity:sprites.count]; 86 | for (BarrageFloatSprite * sprite in sprites) { 87 | if (sprite.direction == _direction && sprite.side == self.side) { 88 | [synclasticSprites addObject:sprite]; 89 | } 90 | } 91 | 92 | BOOL down = self.direction == BarrageFloatDirectionT2B; // 是否是朝下方向 93 | 94 | static BOOL const AVAERAGE_STRATEGY = NO; // YES:条纹平均精灵策略; NO:最快时间策略(体验会好一些) 95 | NSTimeInterval stripMaxActiveTimes[STRIP_NUM]={0}; // 每一条网格 已有精灵中最后退出屏幕的时间 96 | NSUInteger stripSpriteNumbers[STRIP_NUM]={0}; // 每一条网格 包含精灵的数目 97 | NSUInteger stripNum = MIN(STRIP_NUM, MAX(self.trackNumber, 1)); // between (1,STRIP_NUM) 98 | CGFloat stripHeight = rect.size.height/stripNum; // 水平条高度 99 | 100 | NSUInteger overlandStripNum = (NSUInteger)ceil((double)self.size.height/stripHeight); // 横跨网格条数目 101 | NSUInteger availableFrom = 0; 102 | NSUInteger leastActiveTimeStrip = 0; // 最小时间的行 103 | NSUInteger leastActiveSpriteStrip = 0; // 最小网格精灵的行 104 | 105 | BOOL hasBestTrack = !self.avoidCollision; 106 | for (NSUInteger i = 0; i < stripNum; i++) { 107 | //寻找当前行里包含的sprites 108 | CGFloat stripFrom = down?(i * stripHeight+rect.origin.y):(rect.origin.y+rect.size.height - i * stripHeight); 109 | CGFloat stripTo = down?(stripFrom + stripHeight):(stripFrom-stripHeight); 110 | 111 | for (BarrageFloatSprite * sprite in synclasticSprites) { 112 | CGFloat spriteFrom = down?sprite.origin.y:(sprite.origin.y+sprite.size.height); 113 | CGFloat spriteTo = down?(sprite.origin.y + sprite.size.height):sprite.origin.y; 114 | if (fabs(spriteTo-spriteFrom)+fabs(stripTo-stripFrom)>MAX(fabs(stripTo-spriteFrom), fabs(spriteTo-stripFrom))) { // 在条条里 115 | stripSpriteNumbers[i]++; 116 | NSTimeInterval activeTime = [sprite estimateActiveTime]; 117 | if (activeTime > stripMaxActiveTimes[i]){ 118 | stripMaxActiveTimes[i] = activeTime; 119 | } 120 | } 121 | } 122 | if (stripMaxActiveTimes[i] > 0) { 123 | availableFrom = i+1; 124 | } 125 | else if (i - availableFrom >= overlandStripNum - 1){ 126 | hasBestTrack |= YES; 127 | break; // eureka! 128 | } 129 | if (i <= stripNum - overlandStripNum) { 130 | if (stripMaxActiveTimes[i] < stripMaxActiveTimes[leastActiveTimeStrip]) { 131 | leastActiveTimeStrip = i; 132 | } 133 | if (stripSpriteNumbers[i] < stripSpriteNumbers[leastActiveSpriteStrip]) { 134 | leastActiveSpriteStrip = i; 135 | } 136 | } 137 | } 138 | if (availableFrom > stripNum - overlandStripNum) { // 那就是没有找到喽 139 | availableFrom = AVAERAGE_STRATEGY?leastActiveSpriteStrip:leastActiveTimeStrip; // 使用最小个数 or 使用最短时间 140 | } 141 | 142 | CGPoint origin = CGPointZero; 143 | origin.x = (rect.origin.x+rect.size.width-self.size.width)/2; 144 | if (self.side == BarrageFloatSideRight) { 145 | origin.x = rect.origin.x+rect.size.width-self.size.width; 146 | } 147 | else if (self.side == BarrageFloatSideLeft) 148 | { 149 | origin.x = rect.origin.x; 150 | } 151 | origin.y = down?(stripHeight * availableFrom+rect.origin.y):(rect.origin.y+rect.size.height-stripHeight * availableFrom - self.size.height); 152 | if (!hasBestTrack) { 153 | origin.y = -self.size.height+rect.origin.y; 154 | _duration = 0; 155 | } 156 | return origin; 157 | } 158 | 159 | @end 160 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageFloatTextSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatSprite.h" 28 | 29 | /// 悬浮文字精灵 30 | @interface BarrageFloatTextSprite : BarrageFloatSprite 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageFloatTextSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageFloatTextSprite.h" 28 | 29 | @implementation BarrageFloatTextSprite 30 | 31 | - (instancetype)init 32 | { 33 | if (self = [super init]) { 34 | _viewClassName = NSStringFromClass([UILabel class]); 35 | } 36 | return self; 37 | } 38 | 39 | - (void)restoreViewState 40 | { 41 | [super restoreViewState]; 42 | self.view.alpha = 1.0f; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import "BarrageSpriteUtility.h" 29 | #import "BarrageSpriteProtocol.h" 30 | 31 | extern NSString * const kBarrageRendererContextCanvasBounds; // 画布大小 32 | extern NSString * const kBarrageRendererContextRelatedSpirts; // 相关精灵 33 | extern NSString * const kBarrageRendererContextTimestamp; // 时间戳 34 | 35 | /// 精灵基类 36 | @interface BarrageSprite : NSObject 37 | { 38 | CGPoint _origin; 39 | BOOL _valid; 40 | CGSize _mandatorySize; 41 | NSString *_viewClassName; 42 | } 43 | 44 | /// 延时, 这个是相对于rendered的绝对时间/秒 45 | /// 如果delay<0, 当然不会提前显示 ^ - ^ 46 | @property(nonatomic,assign)NSTimeInterval delay; 47 | 48 | /// 创建的绝对时间,初始化的时候创建; 目前好像没啥用处 49 | @property(nonatomic,strong,readonly)NSDate * birth; 50 | 51 | /// 时间戳,表示这个弹幕处于的时间位置;相对于时间引擎启动的时候;精灵被激活的时候生成 52 | @property(nonatomic,assign,readonly)CFTimeInterval timestamp; 53 | 54 | // 最底层是0, 往上依次叠加; 默认值是0 55 | @property(nonatomic,assign)NSUInteger z_index; 56 | 57 | /// 起始位置,为了获取这个值,子类需要重写 originInBounds:withSprites: 方法 58 | @property(nonatomic,assign,readonly)CGPoint origin; 59 | 60 | /// 为了获取此值,子类可能需要在 updateWithTime: 中修改 _position成员变量 61 | @property(nonatomic,assign,readonly)CGPoint position; 62 | 63 | /// calculate inside, return for others;如果需要不同大小的size 64 | @property(nonatomic,assign,readonly)CGSize size; 65 | 66 | /// 是否有效,默认YES; 当过了动画时间之后,就会被标记成NO; 永世不得翻身;子类可能需要在 updateWithTime: 中修改 _valid成员变量 67 | @property(nonatomic,assign,readonly,getter=isValid)BOOL valid; 68 | 69 | #pragma mark - reusableView 70 | 71 | /// 输出的view,这样就不必自己再绘制图形了,并且可以使用硬件加速 72 | @property(nonatomic,strong)UIView * view; 73 | 74 | @property(nonatomic,strong,readonly)NSString *viewClassName; 75 | 76 | @property(nonatomic,strong)NSDictionary *viewParams; 77 | 78 | /// 强制性大小,默认为CGSizeZero,大小自适应; 否则使用mandatorySize的值来设置view大小 79 | @property(nonatomic,assign)CGSize mandatorySize; 80 | 81 | #pragma mark - called, part of lifecycle 82 | 83 | /// 结合相关上下文激活精灵; 如要覆盖, 请要先调用super方法 84 | - (void)activeWithContext:(NSDictionary *)context; 85 | 86 | /// 结合相关上下文失活精灵; 如要覆盖, 请要先调用super方法 87 | - (void)deactive; 88 | 89 | /// 用相对时间更新状态; 最好不要覆盖此方法; 如要覆盖, 请要先调用super方法 90 | - (void)updateWithTime:(NSTimeInterval)time; 91 | 92 | #pragma mark - override - 93 | 94 | #pragma mark invalid&valid 95 | 96 | /// 恢复view状态,将要失效时使用 97 | - (void)restoreViewState; 98 | 99 | /// 初始化view状态 100 | - (void)initializeViewState; 101 | 102 | #pragma mark update 103 | /// _position, 此时刻的位置 104 | - (CGRect)rectWithTime:(NSTimeInterval)time; 105 | 106 | /// _valid, 此时刻是否还有效 107 | - (BOOL)validWithTime:(NSTimeInterval)time; 108 | 109 | /// 在本次弹幕周期内,强制将弹幕无效 110 | - (void)forceInvalid; 111 | 112 | #pragma mark launch 113 | /// 返回弹幕的初始位置 114 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites; 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSprite.h" 28 | #import "BarrageViewPool.h" 29 | 30 | @interface BarrageSprite() 31 | @property(nonatomic,strong)UITapGestureRecognizer *tapGestureRecognizer; 32 | @property(nonatomic,assign)BOOL forcedInvalid; 33 | @end 34 | 35 | @implementation BarrageSprite 36 | 37 | @synthesize mandatorySize = _mandatorySize; 38 | @synthesize clickAction = _clickAction; 39 | 40 | @synthesize origin = _origin; 41 | @synthesize valid = _valid; 42 | @synthesize view = _view; 43 | @synthesize viewClassName = _viewClassName; 44 | 45 | - (instancetype)init 46 | { 47 | if (self = [super init]) { 48 | _delay = 0.0f; 49 | _birth = [NSDate date]; 50 | _valid = YES; 51 | _origin.x = _origin.y = MAXFLOAT; 52 | _z_index = 0; 53 | _forcedInvalid = NO; 54 | _mandatorySize = CGSizeZero; 55 | 56 | _viewClassName = NSStringFromClass([UIView class]); 57 | } 58 | return self; 59 | } 60 | 61 | #pragma mark - update 62 | 63 | - (void)updateWithTime:(NSTimeInterval)time 64 | { 65 | _valid = !self.forcedInvalid && [self validWithTime:time]; 66 | _view.frame = [self rectWithTime:time]; 67 | if ([_view respondsToSelector:@selector(updateWithTime:)]) { 68 | [_view updateWithTime:time]; 69 | } 70 | } 71 | 72 | - (CGRect)rectWithTime:(NSTimeInterval)time 73 | { 74 | return CGRectMake(_origin.x, _origin.y, self.size.width, self.size.height); 75 | } 76 | 77 | - (BOOL)validWithTime:(NSTimeInterval)time 78 | { 79 | return YES; 80 | } 81 | 82 | - (void)forceInvalid 83 | { 84 | self.forcedInvalid = YES; 85 | } 86 | 87 | #pragma mark - active and deactive 88 | 89 | - (void)activeWithContext:(NSDictionary *)context 90 | { 91 | CGRect rect = [[context objectForKey:kBarrageRendererContextCanvasBounds]CGRectValue]; 92 | NSArray * sprites = [context objectForKey:kBarrageRendererContextRelatedSpirts]; 93 | NSTimeInterval timestamp = [[context objectForKey:kBarrageRendererContextTimestamp]doubleValue]; 94 | _timestamp = timestamp; 95 | [[BarrageViewPool mainPool]assembleBarrageViewForSprite:self]; 96 | [self initializeViewState]; 97 | [self.view sizeToFit]; 98 | if (!CGSizeEqualToSize(_mandatorySize, CGSizeZero)) { 99 | self.view.frame = CGRectMake(0, 0, _mandatorySize.width, _mandatorySize.height); 100 | } 101 | _origin = [self originInBounds:rect withSprites:sprites]; 102 | self.view.frame = CGRectMake(_origin.x, _origin.y, self.size.width, self.size.height); 103 | } 104 | 105 | - (void)deactive 106 | { 107 | [self restoreViewState]; 108 | self.forcedInvalid = NO; 109 | [[BarrageViewPool mainPool]reclaimBarrageViewForSprite:self]; 110 | } 111 | 112 | /// 恢复view状态,初始化view时使用 113 | - (void)restoreViewState 114 | { 115 | if (self.clickAction) { 116 | self.view.userInteractionEnabled = NO; 117 | [self.view removeGestureRecognizer:self.tapGestureRecognizer]; 118 | } 119 | } 120 | 121 | - (void)initializeViewState 122 | { 123 | self.view.frame = CGRectZero; 124 | [self.view configureWithParams:self.viewParams]; 125 | if (self.clickAction) { 126 | _view.userInteractionEnabled = YES; 127 | [_view addGestureRecognizer:self.tapGestureRecognizer]; 128 | } 129 | } 130 | 131 | #pragma mark - gesture 132 | 133 | - (UIGestureRecognizer *)tapGestureRecognizer 134 | { 135 | if (!_tapGestureRecognizer) { 136 | _tapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(clickSpriteView)]; 137 | } 138 | return _tapGestureRecognizer; 139 | } 140 | 141 | - (void)clickSpriteView 142 | { 143 | if (self.clickAction) self.clickAction(self.viewParams); 144 | } 145 | 146 | /// 区域内的初始位置,只在刚加入渲染器的时候被调用;子类继承需要override. 147 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 148 | { 149 | CGFloat x = random_between(rect.origin.x, rect.origin.x+rect.size.width-self.size.width); 150 | CGFloat y = random_between(rect.origin.y, rect.origin.y+rect.size.height-self.size.height); 151 | return CGPointMake(x, y); 152 | } 153 | 154 | #pragma mark - attributes 155 | 156 | - (void)setClickAction:(BarrageClickAction)clickAction 157 | { 158 | _clickAction = [clickAction copy]; 159 | } 160 | 161 | - (CGPoint)position 162 | { 163 | return self.view.frame.origin; 164 | } 165 | 166 | - (CGSize)size 167 | { 168 | return self.view.bounds.size; 169 | } 170 | 171 | - (void)setValue:(id)value forUndefinedKey:(NSString *)key 172 | { 173 | #ifdef DEBUG 174 | // NSLog(@"[Class:%@] hasNo - [Property:%@]; [Value:%@] will be discarded.",NSStringFromClass([self class]),key,value); 175 | #endif 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageSpriteProtocol.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import 29 | 30 | typedef void(^BarrageClickAction)(NSDictionary *params); 31 | 32 | /// UIView 弹幕协议 33 | @protocol BarrageViewProtocol 34 | 35 | @optional 36 | 37 | - (void)updateWithTime:(NSTimeInterval)time; 38 | 39 | @required 40 | 41 | - (void)prepareForReuse; 42 | - (void)configureWithParams:(NSDictionary *)params; 43 | 44 | @end 45 | 46 | /// Action 弹幕协议 47 | @protocol BarrageActionProtocol 48 | 49 | /// 注入点击行为 50 | @property(nonatomic,strong)BarrageClickAction clickAction; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageSpriteUtility.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | BOOL divideLine(CGFloat * from, CGFloat * to, CGFloat *begin, CGFloat *end); 35 | BOOL searchMaxSpace(CGFloat * p_from,CGFloat * p_to,CGFloat begins[],CGFloat ends[], NSInteger n, CGFloat threshold); 36 | /// 生成在[min,max]的随机数 37 | CGFloat random_between(CGFloat min, CGFloat max); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | @interface BarrageSpriteUtility : NSObject 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageSpriteUtility.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSpriteUtility.h" 28 | 29 | @implementation BarrageSpriteUtility 30 | 31 | /// 一维线段分割函数 32 | /// 如果返回YES,通过[from,to]输出最大剪枝; 如果返回NO, 不存在有效剪枝 33 | /// 左右分支相等,优先使用左边的; 34 | /// 除了from,to, 其余的参数都是不可变的 35 | /// threshold>=0 36 | ///TODO: 此算法尚未验证 37 | BOOL searchMaxSpace(CGFloat * p_from,CGFloat * p_to,CGFloat begins[],CGFloat ends[], NSInteger n, CGFloat threshold) 38 | { 39 | if (n <= 0) { 40 | return YES; 41 | } 42 | if (threshold < 0) { 43 | *p_to = *p_from -1; // 做死了 44 | return NO; 45 | } 46 | CGFloat begin = begins[0]; // 第一次分割 47 | CGFloat end = ends[0]; 48 | CGFloat from = *p_from; 49 | CGFloat to = *p_to; 50 | if (divideLine(&from, &to, &begin, &end)) { 51 | CGFloat len1 = to - from; 52 | CGFloat len2 = end - begin; 53 | if (n>1) { 54 | if (len1 >= threshold) { // 左侧剪枝 55 | searchMaxSpace(&from, &to, begins+1, ends+1, n-1, threshold); 56 | } 57 | if (len2 >= threshold) { // 右侧剪枝 58 | searchMaxSpace(&begin, &end, begins+1, ends+1, n-1, threshold); 59 | } 60 | 61 | } 62 | if (len1 >= len2 && len1 >= threshold) { 63 | *p_from = from; 64 | *p_to = to; 65 | return YES; 66 | } 67 | else if(len2 > len1 && len2 >= threshold) 68 | { 69 | *p_from = begin; 70 | *p_to = end; 71 | return YES; 72 | } 73 | *p_to = *p_from -1; // 做死了 74 | return NO; 75 | } 76 | *p_to = *p_from -1; // 做死了 77 | return NO; 78 | } 79 | 80 | ///TODO: 很奇怪objective-c为什么不能传引用呢? 81 | /// [1,5]/[2,4] => [1,2]/[4,5]; 82 | /// 如果 *to < *from || *end < *begin, return NO; 83 | BOOL divideLine(CGFloat * from, CGFloat * to, CGFloat *begin, CGFloat *end) 84 | { 85 | if (*to >= *from && *end >= *begin) { 86 | CGFloat tmp = *to; 87 | *to = (*begin <= tmp)?*begin:*from-1; 88 | *begin = *end; 89 | *end = (*end >= *from)?tmp:*begin-1; 90 | return YES; 91 | } 92 | return NO; 93 | } 94 | 95 | /// 生成在[min,max]的随机数 96 | CGFloat random_between(CGFloat min, CGFloat max) 97 | { 98 | if (min >= max) { 99 | return min; 100 | } 101 | CGFloat scale = max - min; 102 | return min + scale * random()/RAND_MAX; 103 | } 104 | 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageViewPool.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | /// 虽如是, 性能提升并不明显; 因为性能的瓶颈在于GPU绘制,而非CPU创建对象 30 | 31 | @class BarrageSprite; 32 | 33 | @interface BarrageViewPool : NSObject 34 | 35 | + (instancetype)mainPool; 36 | 37 | /// 装配 view 38 | - (void)assembleBarrageViewForSprite:(BarrageSprite *)sprite; 39 | 40 | /// 归还 view 41 | - (void)reclaimBarrageViewForSprite:(BarrageSprite *)sprite; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageViewPool.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageViewPool.h" 28 | #import "BarrageSpriteProtocol.h" 29 | #import "BarrageSprite.h" 30 | 31 | @interface BarrageViewPool () 32 | { 33 | NSMutableDictionary>*> *_reusableViews; 34 | } 35 | @end 36 | 37 | @implementation BarrageViewPool 38 | 39 | + (instancetype)mainPool 40 | { 41 | static BarrageViewPool *pool; 42 | static dispatch_once_t onceToken; 43 | dispatch_once(&onceToken, ^{ 44 | pool = [[BarrageViewPool alloc]init]; 45 | }); 46 | return pool; 47 | } 48 | 49 | - (instancetype)init 50 | { 51 | if (self = [super init]) { 52 | _reusableViews = [[NSMutableDictionary alloc]init]; 53 | [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(clearReusableSpriteViews) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 54 | } 55 | return self; 56 | } 57 | 58 | - (void)clearReusableSpriteViews 59 | { 60 | [_reusableViews removeAllObjects]; 61 | } 62 | 63 | - (void)dealloc 64 | { 65 | [[NSNotificationCenter defaultCenter]removeObserver:self]; 66 | } 67 | 68 | #pragma mark - assistant 69 | - (NSMutableArray>*)viewsForViewClassifier:(NSString *)classifier 70 | { 71 | NSMutableArray *views = _reusableViews[classifier]; 72 | if (!views) { 73 | views = [[NSMutableArray alloc]init]; 74 | _reusableViews[classifier] = views; 75 | } 76 | return views; 77 | } 78 | 79 | - (NSString *)viewClassifierForSprite:(BarrageSprite *)sprite 80 | { 81 | NSString *classifier = [NSString stringWithFormat:@"%@.%@",NSStringFromClass([self class]),sprite.viewClassName]; 82 | return classifier; 83 | } 84 | 85 | #pragma mark - interface 86 | /// 装配 view 87 | - (void)assembleBarrageViewForSprite:(BarrageSprite *)sprite; 88 | { 89 | NSParameterAssert(sprite.viewClassName.length>0); 90 | Class class = NSClassFromString(sprite.viewClassName); 91 | NSParameterAssert([class conformsToProtocol:@protocol(BarrageViewProtocol)]); 92 | 93 | NSString *classifier = [self viewClassifierForSprite:sprite]; 94 | NSMutableArray *views = [self viewsForViewClassifier:classifier]; 95 | 96 | UIView *view = [views firstObject]; 97 | if (!view) { 98 | view = [[class alloc]initWithFrame:CGRectZero]; 99 | } 100 | else 101 | { 102 | [view prepareForReuse]; 103 | [views removeObject:view]; 104 | } 105 | sprite.view = view; 106 | } 107 | 108 | /// 归还 view 109 | - (void)reclaimBarrageViewForSprite:(BarrageSprite *)sprite; 110 | { 111 | NSParameterAssert([sprite.view conformsToProtocol:@protocol(BarrageViewProtocol)]); 112 | NSString *classifier = [self viewClassifierForSprite:sprite]; 113 | NSMutableArray *views = [self viewsForViewClassifier:classifier]; 114 | if (![views containsObject:sprite.view]) { 115 | [views addObject:sprite.view]; 116 | } 117 | sprite.view = nil; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageWalkImageSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkSprite.h" 28 | 29 | /// 移动文字精灵 30 | @interface BarrageWalkImageSprite : BarrageWalkSprite 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageWalkImageSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkImageSprite.h" 28 | 29 | @implementation BarrageWalkImageSprite 30 | 31 | - (instancetype)init 32 | { 33 | if (self = [super init]) { 34 | _viewClassName = NSStringFromClass([UIImageView class]); 35 | } 36 | return self; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageWalkSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageSprite.h" 28 | 29 | typedef NS_ENUM(NSUInteger, BarrageWalkDirection) { 30 | BarrageWalkDirectionR2L = 1, // 右向左 31 | BarrageWalkDirectionL2R = 2, // 左向右 32 | BarrageWalkDirectionT2B = 3, // 上往下 33 | BarrageWalkDirectionB2T = 4 // 下往上 34 | }; 35 | 36 | ///注: 此处侧边的含义与悬浮弹幕(BarrageFloatSide)并不相同! 37 | typedef NS_ENUM(NSUInteger, BarrageWalkSide) { 38 | BarrageWalkSideDefault = 0, // 默认,根据选择的方向而定 39 | BarrageWalkSideRight = 1, // 靠右侧行驶,运动方向的右手法则 40 | BarrageWalkSideLeft = 2 // 靠左侧行驶,运动方向的左手法则 41 | }; 42 | 43 | /// 移动文字精灵 44 | @interface BarrageWalkSprite : BarrageSprite 45 | { 46 | CGPoint _destination; 47 | } 48 | 49 | /// 速度,point/second 50 | @property(nonatomic,assign)CGFloat speed; 51 | 52 | /// 运动方向 53 | @property(nonatomic,assign)BarrageWalkDirection direction; 54 | 55 | /// 运动侧边 56 | @property(nonatomic,assign)BarrageWalkSide side; 57 | 58 | /// 需要在originInBounds:withSprites: 方法中修改 _destination的值以表示运动的终点 59 | @property(nonatomic,assign,readonly)CGPoint destination; 60 | 61 | /// 防止碰撞,只针对同方向的弹幕有效。默认为NO。开启后,弹幕可能会丢失。 62 | @property(nonatomic,assign)BOOL avoidCollision; 63 | 64 | /// 轨道数量 65 | @property(nonatomic,assign)NSUInteger trackNumber; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageWalkSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkSprite.h" 28 | 29 | static NSUInteger const STRIP_NUM = 160; // 总共的网格条数 30 | 31 | @interface BarrageWalkSprite() 32 | { 33 | BarrageWalkDirection _direction; 34 | } 35 | @end 36 | 37 | @implementation BarrageWalkSprite 38 | @synthesize direction = _direction; 39 | @synthesize destination = _destination; 40 | 41 | - (instancetype)init 42 | { 43 | if (self = [super init]) { 44 | _direction = BarrageWalkDirectionR2L; 45 | _side = BarrageWalkSideDefault; 46 | _speed = 30.0f; // 默认值 47 | _trackNumber = 40; 48 | _avoidCollision = NO; 49 | } 50 | return self; 51 | } 52 | 53 | - (BarrageWalkSide)side 54 | { 55 | if (_side != BarrageWalkSideDefault) return _side; 56 | return [self defaultSideWithDirection:self.direction]; 57 | } 58 | 59 | - (BarrageWalkSide)defaultSideWithDirection:(BarrageWalkDirection)direction 60 | { 61 | if (direction == BarrageWalkDirectionR2L) return BarrageWalkSideRight; 62 | else if (direction == BarrageWalkDirectionL2R) return BarrageWalkSideLeft; 63 | else if (direction == BarrageWalkDirectionT2B) return BarrageWalkSideRight; 64 | else if (direction == BarrageWalkDirectionB2T) return BarrageWalkSideLeft; 65 | return BarrageWalkSideRight; // Chinese way 66 | } 67 | 68 | #pragma mark - update 69 | 70 | - (BOOL)validWithTime:(NSTimeInterval)time 71 | { 72 | return [self estimateActiveTime] > 0; 73 | } 74 | 75 | - (CGRect)rectWithTime:(NSTimeInterval)time 76 | { 77 | CGFloat X = self.destination.x - self.origin.x; 78 | CGFloat Y = self.destination.y - self.origin.y; 79 | CGFloat L = sqrt(X*X + Y*Y); 80 | NSTimeInterval duration = time - self.timestamp; 81 | CGPoint position = CGPointMake(self.origin.x + duration * self.speed * X/L, self.origin.y + duration * self.speed * Y/L); 82 | return CGRectMake(position.x, position.y, self.size.width, self.size.height); 83 | } 84 | 85 | /// 估算精灵的剩余存活时间 86 | - (NSTimeInterval)estimateActiveTime 87 | { 88 | CGFloat activeDistance = 0; 89 | switch (_direction) { 90 | case BarrageWalkDirectionR2L: 91 | activeDistance = self.position.x - _destination.x; 92 | break; 93 | case BarrageWalkDirectionL2R: 94 | activeDistance = _destination.x - self.position.x; 95 | break; 96 | case BarrageWalkDirectionT2B: 97 | activeDistance = _destination.y - self.position.y; 98 | break; 99 | case BarrageWalkDirectionB2T: 100 | activeDistance = self.position.y - _destination.y; 101 | default: 102 | break; 103 | } 104 | return activeDistance/self.speed; 105 | } 106 | 107 | #pragma mark - launch 108 | 109 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 110 | { 111 | // 获取同方向精灵 112 | NSMutableArray * synclasticSprites = [[NSMutableArray alloc]initWithCapacity:sprites.count]; 113 | for (BarrageWalkSprite * sprite in sprites) { 114 | if (sprite.direction == _direction && sprite.side == self.side) { // 找寻同道中人 115 | [synclasticSprites addObject:sprite]; 116 | } 117 | } 118 | 119 | static BOOL const AVAERAGE_STRATEGY = YES; // YES:条纹平均精灵策略(体验会好一些); NO:最快时间策略 120 | NSTimeInterval stripMaxActiveTimes[STRIP_NUM]={0}; // 每一条网格 已有精灵中最后退出屏幕的时间 121 | NSUInteger stripSpriteNumbers[STRIP_NUM]={0}; // 每一条网格 包含精灵的数目 122 | NSUInteger stripNum = MIN(STRIP_NUM, MAX(self.trackNumber, 1)); // between (1,STRIP_NUM) 123 | CGFloat stripHeight = rect.size.height/stripNum; // 水平条高度 124 | CGFloat stripWidth = rect.size.width/stripNum; // 竖直条宽度 125 | BOOL oritation = _direction == BarrageWalkDirectionL2R || _direction == BarrageWalkDirectionR2L; // 方向, YES代表水平弹幕 126 | BOOL rotation = self.side == [self defaultSideWithDirection:_direction]; 127 | /// 计算数据结构,便于应用算法 128 | NSUInteger overlandStripNum = 1; // 横跨网格条数目 129 | if (oritation) { // 水平 130 | overlandStripNum = (NSUInteger)ceil((double)self.size.height/stripHeight); 131 | } 132 | else // 竖直 133 | { 134 | overlandStripNum = (NSUInteger)ceil((double)self.size.width/stripWidth); 135 | } 136 | /// 当前精灵需要的时间,左边碰到边界, 不是真实的活跃时间 137 | NSTimeInterval maxActiveTime = oritation?rect.size.width/self.speed:rect.size.height/self.speed; 138 | NSUInteger availableFrom = 0; 139 | NSUInteger leastActiveTimeStrip = 0; // 最小时间的行 140 | NSUInteger leastActiveSpriteStrip = 0; // 最小网格的行 141 | 142 | BOOL hasBestTrack = !self.avoidCollision; 143 | for (NSUInteger i = 0; i < stripNum; i++) { 144 | //寻找当前行里包含的sprites 145 | CGFloat stripFrom = i * (oritation?stripHeight:stripWidth); 146 | CGFloat stripTo = stripFrom + (oritation?stripHeight:stripWidth); 147 | if (!rotation) { 148 | CGFloat preStripFrom = stripFrom; 149 | stripFrom = (oritation?rect.size.height:rect.size.width) - stripTo; 150 | stripTo = (oritation?rect.size.height:rect.size.width) - preStripFrom; 151 | } 152 | CGFloat lastDistanceAllOut = YES; 153 | for (BarrageWalkSprite * sprite in synclasticSprites) { 154 | CGFloat spriteFrom = oritation?sprite.origin.y:sprite.origin.x; 155 | CGFloat spriteTo = spriteFrom + (oritation?sprite.size.height:sprite.size.width); 156 | if ((spriteTo-spriteFrom)+(stripTo-stripFrom)>MAX(stripTo-spriteFrom, spriteTo-stripFrom)) { // 在条条里 157 | stripSpriteNumbers[i]++; 158 | NSTimeInterval activeTime = [sprite estimateActiveTime]; 159 | if (activeTime > stripMaxActiveTimes[i]){ // 获取最慢的那个 160 | stripMaxActiveTimes[i] = activeTime; 161 | CGFloat distance = oritation?fabs(sprite.position.x-sprite.origin.x):fabs(sprite.position.y-sprite.origin.y); 162 | lastDistanceAllOut = distance > (oritation?sprite.size.width:sprite.size.height); 163 | } 164 | } 165 | } 166 | if (stripMaxActiveTimes[i]>maxActiveTime || !lastDistanceAllOut) { 167 | availableFrom = i+1; 168 | } 169 | else if (i - availableFrom >= overlandStripNum - 1){ 170 | hasBestTrack |= YES; 171 | break; // eureka! 172 | } 173 | if (i <= stripNum - overlandStripNum) { 174 | if (stripMaxActiveTimes[i] < stripMaxActiveTimes[leastActiveTimeStrip]) { 175 | leastActiveTimeStrip = i; 176 | } 177 | if (stripSpriteNumbers[i] < stripSpriteNumbers[leastActiveSpriteStrip]) { 178 | leastActiveSpriteStrip = i; 179 | } 180 | } 181 | } 182 | if (availableFrom > stripNum - overlandStripNum) { // 那就是没有找到喽 183 | availableFrom = AVAERAGE_STRATEGY?leastActiveSpriteStrip:leastActiveTimeStrip; // 使用最小个数 or 使用最短时间 184 | } 185 | 186 | CGPoint origin = CGPointZero; 187 | if (oritation) { // 水平 188 | _destination.y = origin.y = (rotation?stripHeight*availableFrom:rect.size.height-stripHeight * availableFrom-self.size.height)+rect.origin.y; 189 | origin.x = (self.direction == BarrageWalkDirectionL2R)?rect.origin.x - self.size.width:rect.origin.x + rect.size.width; 190 | if (hasBestTrack) { 191 | _destination.x = (self.direction == BarrageWalkDirectionL2R)?rect.origin.x + rect.size.width:rect.origin.x - self.size.width; 192 | } else { 193 | _destination.x = (self.direction == BarrageWalkDirectionL2R)?origin.x-1:origin.x+1; 194 | } 195 | } 196 | else 197 | { 198 | _destination.x = origin.x = (rotation?stripWidth*availableFrom:rect.size.width-stripWidth*availableFrom -self.size.width)+rect.origin.x; 199 | origin.y = (self.direction == BarrageWalkDirectionT2B)?rect.origin.y - self.size.height:rect.origin.y + rect.size.height; 200 | if (hasBestTrack) { 201 | _destination.y = (self.direction == BarrageWalkDirectionT2B)?rect.origin.y + rect.size.height:rect.origin.y - self.size.height; 202 | } else { 203 | _destination.y = (self.direction == BarrageWalkDirectionT2B)?origin.y-1:origin.y+1; 204 | } 205 | } 206 | return origin; 207 | } 208 | 209 | @end 210 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageWalkTextSprite.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkSprite.h" 28 | 29 | /// 移动文字精灵 30 | @interface BarrageWalkTextSprite : BarrageWalkSprite 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/BarrageWalkTextSprite.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "BarrageWalkTextSprite.h" 28 | 29 | @implementation BarrageWalkTextSprite 30 | 31 | - (instancetype)init 32 | { 33 | if (self = [super init]) { 34 | _viewClassName = NSStringFromClass([UILabel class]); 35 | } 36 | return self; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/UIImageView+BarrageView.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import "BarrageSpriteProtocol.h" 29 | 30 | @interface UIImageView (BarrageView) 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/UIImageView+BarrageView.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "UIImageView+BarrageView.h" 28 | #import "UIView+BarrageView.h" 29 | 30 | @implementation UIImageView (BarrageView) 31 | 32 | - (void)prepareForReuse 33 | { 34 | [super prepareForReuse]; 35 | self.image = nil; 36 | } 37 | 38 | - (void)configureWithParams:(NSDictionary *)params 39 | { 40 | [super configureWithParams:params]; 41 | 42 | UIImage *image = params[@"image"]; 43 | if (image) self.image = image; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/UILabel+BarrageView.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import "BarrageSpriteProtocol.h" 29 | 30 | @interface UILabel (BarrageView) 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/UILabel+BarrageView.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "UILabel+BarrageView.h" 28 | #import "UIView+BarrageView.h" 29 | 30 | @implementation UILabel (BarrageView) 31 | 32 | - (void)prepareForReuse 33 | { 34 | [super prepareForReuse]; 35 | self.text = nil; 36 | self.attributedText = [[NSAttributedString alloc]init]; 37 | } 38 | 39 | - (void)configureWithParams:(NSDictionary *)params 40 | { 41 | [super configureWithParams:params]; 42 | 43 | NSString *text = params[@"text"]; 44 | if (text) self.text = text; 45 | 46 | UIColor *textColor = params[@"textColor"]; 47 | if (textColor) self.textColor = textColor; 48 | else self.textColor = [UIColor blackColor]; 49 | 50 | UIColor *shadowColor = params[@"shadowColor"]; 51 | if (shadowColor) self.layer.shadowColor = shadowColor.CGColor; 52 | else self.layer.shadowColor = [UIColor clearColor].CGColor; 53 | 54 | id shadowOffsetObj = params[@"shadowOffset"]; 55 | if (shadowOffsetObj) self.layer.shadowOffset = [shadowOffsetObj CGSizeValue]; 56 | else self.layer.shadowOffset = CGSizeZero; 57 | 58 | id fontSizeObj = params[@"fontSize"]; 59 | CGFloat fontSize = fontSizeObj?[fontSizeObj doubleValue]:16.0f; 60 | 61 | NSString *fontFamily = params[@"fontFamily"]; 62 | self.font = fontFamily?[UIFont fontWithName:fontFamily size:fontSize]:[UIFont systemFontOfSize:fontSize]; 63 | 64 | NSAttributedString *attributedText = params[@"attributedText"]; 65 | if (attributedText) self.attributedText = attributedText; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/UIView+BarrageView.h: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import "BarrageSpriteProtocol.h" 29 | 30 | @interface UIView (BarrageView) 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /BarrageRenderer/BarrageSprite/UIView+BarrageView.m: -------------------------------------------------------------------------------- 1 | // Part of BarrageRenderer. Created by UnAsh. 2 | // Blog: http://blog.exbye.com 3 | // Github: https://github.com/unash/BarrageRenderer 4 | 5 | // This code is distributed under the terms and conditions of the MIT license. 6 | 7 | // Copyright (c) 2015年 UnAsh. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "UIView+BarrageView.h" 28 | 29 | @implementation UIView (BarrageView) 30 | 31 | - (void)prepareForReuse 32 | { 33 | self.backgroundColor = [UIColor clearColor]; 34 | self.layer.borderWidth = 0; 35 | self.layer.borderColor = [UIColor clearColor].CGColor; 36 | self.layer.cornerRadius = 0; 37 | self.clipsToBounds = NO; 38 | } 39 | 40 | - (void)configureWithParams:(NSDictionary *)params 41 | { 42 | UIColor *backgroundColor = params[@"backgroundColor"]; 43 | if (backgroundColor) self.backgroundColor = backgroundColor; 44 | 45 | 46 | id borderWidthObj = params[@"borderWidth"]; 47 | if (borderWidthObj) self.layer.borderWidth = [borderWidthObj doubleValue]; 48 | 49 | UIColor *borderColor = params[@"borderColor"]; 50 | if (borderColor) self.layer.borderColor = borderColor.CGColor; 51 | 52 | /// 圆角,此属性十分影响绘制性能,谨慎使用 53 | id cornerRadiusObj = params[@"cornerRadius"]; 54 | if (cornerRadiusObj) 55 | { 56 | CGFloat cornerRadius = [cornerRadiusObj doubleValue]; 57 | if (cornerRadius > 0) { 58 | self.layer.cornerRadius = cornerRadius; 59 | self.clipsToBounds = YES; 60 | } 61 | } 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /BarrageRendererDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unash/BarrageRenderer/e845b9cff4c02f380b10a9fcc055bf8527b2e0be/BarrageRendererDemo.gif -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | ADF9FFC680A00BA9E4D24F7C /* libPods-BarrageRendererDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 29884472DF4A619E65B68A0B /* libPods-BarrageRendererDemo.a */; }; 11 | CC853E161BFCBE6A000C1FEC /* AdvancedBarrageController.m in Sources */ = {isa = PBXBuildFile; fileRef = CC853E101BFCBE6A000C1FEC /* AdvancedBarrageController.m */; }; 12 | CC853E171BFCBE6A000C1FEC /* BarrageWalkImageTextSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = CC853E121BFCBE6A000C1FEC /* BarrageWalkImageTextSprite.m */; }; 13 | CC853E181BFCBE6A000C1FEC /* CommonBarrageController.m in Sources */ = {isa = PBXBuildFile; fileRef = CC853E151BFCBE6A000C1FEC /* CommonBarrageController.m */; }; 14 | CCC0B4A21B6549C40088CFDE /* avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = CCC0B4A11B6549C40088CFDE /* avatar.png */; }; 15 | CCC0B4A51B654DD50088CFDE /* UIImage+Barrage.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC0B4A41B654DD50088CFDE /* UIImage+Barrage.m */; }; 16 | CCD2D7CC1DFED7F3003C8DE2 /* MLEmojiLabel+BarrageView.m in Sources */ = {isa = PBXBuildFile; fileRef = CCD2D7CB1DFED7F3003C8DE2 /* MLEmojiLabel+BarrageView.m */; }; 17 | CCECEDCB1B5662F500098E7A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CCECEDCA1B5662F500098E7A /* main.m */; }; 18 | CCECEDCE1B5662F500098E7A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CCECEDCD1B5662F500098E7A /* AppDelegate.m */; }; 19 | CCECEDD41B5662F500098E7A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CCECEDD21B5662F500098E7A /* Main.storyboard */; }; 20 | CCECEDD61B5662F500098E7A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CCECEDD51B5662F500098E7A /* Images.xcassets */; }; 21 | CCECEDD91B5662F500098E7A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = CCECEDD71B5662F500098E7A /* LaunchScreen.xib */; }; 22 | CCECEDF11B56673E00098E7A /* NSSafeObject.m in Sources */ = {isa = PBXBuildFile; fileRef = CCECEDF01B56673E00098E7A /* NSSafeObject.m */; }; 23 | D0771AAC1F210DAB0092BC2A /* AvatarBarrageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0771AAB1F210DAB0092BC2A /* AvatarBarrageView.m */; }; 24 | D0B2110C1F226D4800905D24 /* FlowerBarrageSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = D0B2110B1F226D4800905D24 /* FlowerBarrageSprite.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 1B4AA4F8C40F9CCA4E403615 /* Pods-BarrageRendererDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BarrageRendererDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BarrageRendererDemo/Pods-BarrageRendererDemo.debug.xcconfig"; sourceTree = ""; }; 29 | 29884472DF4A619E65B68A0B /* libPods-BarrageRendererDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-BarrageRendererDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 52A68FC2D7818C1433F4955A /* Pods-BarrageRendererDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BarrageRendererDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-BarrageRendererDemo/Pods-BarrageRendererDemo.release.xcconfig"; sourceTree = ""; }; 31 | CC853E0F1BFCBE6A000C1FEC /* AdvancedBarrageController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdvancedBarrageController.h; sourceTree = ""; }; 32 | CC853E101BFCBE6A000C1FEC /* AdvancedBarrageController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AdvancedBarrageController.m; sourceTree = ""; }; 33 | CC853E111BFCBE6A000C1FEC /* BarrageWalkImageTextSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BarrageWalkImageTextSprite.h; sourceTree = ""; }; 34 | CC853E121BFCBE6A000C1FEC /* BarrageWalkImageTextSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BarrageWalkImageTextSprite.m; sourceTree = ""; }; 35 | CC853E141BFCBE6A000C1FEC /* CommonBarrageController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonBarrageController.h; sourceTree = ""; }; 36 | CC853E151BFCBE6A000C1FEC /* CommonBarrageController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonBarrageController.m; sourceTree = ""; }; 37 | CCC0B4A11B6549C40088CFDE /* avatar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = avatar.png; sourceTree = ""; }; 38 | CCC0B4A31B654DD50088CFDE /* UIImage+Barrage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Barrage.h"; sourceTree = ""; }; 39 | CCC0B4A41B654DD50088CFDE /* UIImage+Barrage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Barrage.m"; sourceTree = ""; }; 40 | CCD2D7CA1DFED7F3003C8DE2 /* MLEmojiLabel+BarrageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MLEmojiLabel+BarrageView.h"; sourceTree = ""; }; 41 | CCD2D7CB1DFED7F3003C8DE2 /* MLEmojiLabel+BarrageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MLEmojiLabel+BarrageView.m"; sourceTree = ""; }; 42 | CCECEDC51B5662F500098E7A /* BarrageRendererDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BarrageRendererDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | CCECEDC91B5662F500098E7A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | CCECEDCA1B5662F500098E7A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | CCECEDCC1B5662F500098E7A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | CCECEDCD1B5662F500098E7A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | CCECEDD31B5662F500098E7A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | CCECEDD51B5662F500098E7A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 49 | CCECEDD81B5662F500098E7A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 50 | CCECEDEF1B56673E00098E7A /* NSSafeObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSSafeObject.h; sourceTree = ""; }; 51 | CCECEDF01B56673E00098E7A /* NSSafeObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSSafeObject.m; sourceTree = ""; }; 52 | D0771AAA1F210DAB0092BC2A /* AvatarBarrageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AvatarBarrageView.h; path = BarrageDemo/AvatarBarrageView.h; sourceTree = ""; }; 53 | D0771AAB1F210DAB0092BC2A /* AvatarBarrageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AvatarBarrageView.m; path = BarrageDemo/AvatarBarrageView.m; sourceTree = ""; }; 54 | D0B2110A1F226D4800905D24 /* FlowerBarrageSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlowerBarrageSprite.h; path = BarrageDemo/FlowerBarrageSprite.h; sourceTree = ""; }; 55 | D0B2110B1F226D4800905D24 /* FlowerBarrageSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FlowerBarrageSprite.m; path = BarrageDemo/FlowerBarrageSprite.m; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | CCECEDC21B5662F500098E7A /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ADF9FFC680A00BA9E4D24F7C /* libPods-BarrageRendererDemo.a in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | 6B51F37174CE27536307E887 /* Pods */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 1B4AA4F8C40F9CCA4E403615 /* Pods-BarrageRendererDemo.debug.xcconfig */, 74 | 52A68FC2D7818C1433F4955A /* Pods-BarrageRendererDemo.release.xcconfig */, 75 | ); 76 | name = Pods; 77 | sourceTree = ""; 78 | }; 79 | 8FC9C2B9D55BEF2F294D34DA /* Frameworks */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 29884472DF4A619E65B68A0B /* libPods-BarrageRendererDemo.a */, 83 | ); 84 | name = Frameworks; 85 | sourceTree = ""; 86 | }; 87 | CC853E0E1BFCBE6A000C1FEC /* AdvancedBarrage */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | CC853E0F1BFCBE6A000C1FEC /* AdvancedBarrageController.h */, 91 | CC853E101BFCBE6A000C1FEC /* AdvancedBarrageController.m */, 92 | CC853E111BFCBE6A000C1FEC /* BarrageWalkImageTextSprite.h */, 93 | CC853E121BFCBE6A000C1FEC /* BarrageWalkImageTextSprite.m */, 94 | CCD2D7CA1DFED7F3003C8DE2 /* MLEmojiLabel+BarrageView.h */, 95 | CCD2D7CB1DFED7F3003C8DE2 /* MLEmojiLabel+BarrageView.m */, 96 | ); 97 | path = AdvancedBarrage; 98 | sourceTree = ""; 99 | }; 100 | CC853E131BFCBE6A000C1FEC /* CommonBarrage */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | CC853E141BFCBE6A000C1FEC /* CommonBarrageController.h */, 104 | CC853E151BFCBE6A000C1FEC /* CommonBarrageController.m */, 105 | ); 106 | path = CommonBarrage; 107 | sourceTree = ""; 108 | }; 109 | CCECEDBC1B5662F500098E7A = { 110 | isa = PBXGroup; 111 | children = ( 112 | CCECEDC71B5662F500098E7A /* BarrageRendererDemo */, 113 | CCECEDC61B5662F500098E7A /* Products */, 114 | 6B51F37174CE27536307E887 /* Pods */, 115 | 8FC9C2B9D55BEF2F294D34DA /* Frameworks */, 116 | ); 117 | sourceTree = ""; 118 | }; 119 | CCECEDC61B5662F500098E7A /* Products */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | CCECEDC51B5662F500098E7A /* BarrageRendererDemo.app */, 123 | ); 124 | name = Products; 125 | sourceTree = ""; 126 | }; 127 | CCECEDC71B5662F500098E7A /* BarrageRendererDemo */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | CC853E0E1BFCBE6A000C1FEC /* AdvancedBarrage */, 131 | CC853E131BFCBE6A000C1FEC /* CommonBarrage */, 132 | D0771AAD1F210DAF0092BC2A /* BarrageDemo */, 133 | CCECEDEE1B56673E00098E7A /* SafeObject */, 134 | CCECEDCC1B5662F500098E7A /* AppDelegate.h */, 135 | CCECEDCD1B5662F500098E7A /* AppDelegate.m */, 136 | CCC0B4A31B654DD50088CFDE /* UIImage+Barrage.h */, 137 | CCC0B4A41B654DD50088CFDE /* UIImage+Barrage.m */, 138 | CCC0B4A11B6549C40088CFDE /* avatar.png */, 139 | CCECEDD21B5662F500098E7A /* Main.storyboard */, 140 | CCECEDD51B5662F500098E7A /* Images.xcassets */, 141 | CCECEDD71B5662F500098E7A /* LaunchScreen.xib */, 142 | CCECEDC81B5662F500098E7A /* Supporting Files */, 143 | ); 144 | path = BarrageRendererDemo; 145 | sourceTree = ""; 146 | }; 147 | CCECEDC81B5662F500098E7A /* Supporting Files */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | CCECEDC91B5662F500098E7A /* Info.plist */, 151 | CCECEDCA1B5662F500098E7A /* main.m */, 152 | ); 153 | name = "Supporting Files"; 154 | sourceTree = ""; 155 | }; 156 | CCECEDEE1B56673E00098E7A /* SafeObject */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | CCECEDEF1B56673E00098E7A /* NSSafeObject.h */, 160 | CCECEDF01B56673E00098E7A /* NSSafeObject.m */, 161 | ); 162 | path = SafeObject; 163 | sourceTree = ""; 164 | }; 165 | D0771AAD1F210DAF0092BC2A /* BarrageDemo */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | D0B2110A1F226D4800905D24 /* FlowerBarrageSprite.h */, 169 | D0B2110B1F226D4800905D24 /* FlowerBarrageSprite.m */, 170 | D0771AAA1F210DAB0092BC2A /* AvatarBarrageView.h */, 171 | D0771AAB1F210DAB0092BC2A /* AvatarBarrageView.m */, 172 | ); 173 | name = BarrageDemo; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXGroup section */ 177 | 178 | /* Begin PBXNativeTarget section */ 179 | CCECEDC41B5662F500098E7A /* BarrageRendererDemo */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = CCECEDE81B5662F500098E7A /* Build configuration list for PBXNativeTarget "BarrageRendererDemo" */; 182 | buildPhases = ( 183 | 572CE44BC040E4D2F84C6DE2 /* [CP] Check Pods Manifest.lock */, 184 | CCECEDC11B5662F500098E7A /* Sources */, 185 | CCECEDC21B5662F500098E7A /* Frameworks */, 186 | CCECEDC31B5662F500098E7A /* Resources */, 187 | C495AAF13D59EF1528D7F47D /* [CP] Embed Pods Frameworks */, 188 | 28C90FF3366B6A438BADF6A1 /* [CP] Copy Pods Resources */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | ); 194 | name = BarrageRendererDemo; 195 | productName = BarrageRendererDemo; 196 | productReference = CCECEDC51B5662F500098E7A /* BarrageRendererDemo.app */; 197 | productType = "com.apple.product-type.application"; 198 | }; 199 | /* End PBXNativeTarget section */ 200 | 201 | /* Begin PBXProject section */ 202 | CCECEDBD1B5662F500098E7A /* Project object */ = { 203 | isa = PBXProject; 204 | attributes = { 205 | LastUpgradeCheck = 0720; 206 | ORGANIZATIONNAME = "ExBye Inc."; 207 | TargetAttributes = { 208 | CCECEDC41B5662F500098E7A = { 209 | CreatedOnToolsVersion = 6.3; 210 | DevelopmentTeam = C6FYK5JX7E; 211 | ProvisioningStyle = Manual; 212 | }; 213 | }; 214 | }; 215 | buildConfigurationList = CCECEDC01B5662F500098E7A /* Build configuration list for PBXProject "BarrageRendererDemo" */; 216 | compatibilityVersion = "Xcode 3.2"; 217 | developmentRegion = English; 218 | hasScannedForEncodings = 0; 219 | knownRegions = ( 220 | en, 221 | Base, 222 | ); 223 | mainGroup = CCECEDBC1B5662F500098E7A; 224 | productRefGroup = CCECEDC61B5662F500098E7A /* Products */; 225 | projectDirPath = ""; 226 | projectRoot = ""; 227 | targets = ( 228 | CCECEDC41B5662F500098E7A /* BarrageRendererDemo */, 229 | ); 230 | }; 231 | /* End PBXProject section */ 232 | 233 | /* Begin PBXResourcesBuildPhase section */ 234 | CCECEDC31B5662F500098E7A /* Resources */ = { 235 | isa = PBXResourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | CCC0B4A21B6549C40088CFDE /* avatar.png in Resources */, 239 | CCECEDD41B5662F500098E7A /* Main.storyboard in Resources */, 240 | CCECEDD91B5662F500098E7A /* LaunchScreen.xib in Resources */, 241 | CCECEDD61B5662F500098E7A /* Images.xcassets in Resources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | /* End PBXResourcesBuildPhase section */ 246 | 247 | /* Begin PBXShellScriptBuildPhase section */ 248 | 28C90FF3366B6A438BADF6A1 /* [CP] Copy Pods Resources */ = { 249 | isa = PBXShellScriptBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | ); 253 | inputPaths = ( 254 | "${SRCROOT}/Pods/Target Support Files/Pods-BarrageRendererDemo/Pods-BarrageRendererDemo-resources.sh", 255 | "${PODS_ROOT}/MLEmojiLabel/Classes/MLEmoji_Expression.plist", 256 | "${PODS_ROOT}/MLEmojiLabel/Classes/MLEmoji_ExpressionImage.plist", 257 | "${PODS_ROOT}/MLEmojiLabel/Classes/MLEmoji_Expression.bundle", 258 | "${PODS_ROOT}/MLEmojiLabel/Classes/MLEmoji_Expression.bundle/gifexpression.bundle", 259 | ); 260 | name = "[CP] Copy Pods Resources"; 261 | outputPaths = ( 262 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | shellPath = /bin/sh; 266 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BarrageRendererDemo/Pods-BarrageRendererDemo-resources.sh\"\n"; 267 | showEnvVarsInLog = 0; 268 | }; 269 | 572CE44BC040E4D2F84C6DE2 /* [CP] Check Pods Manifest.lock */ = { 270 | isa = PBXShellScriptBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | inputPaths = ( 275 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 276 | "${PODS_ROOT}/Manifest.lock", 277 | ); 278 | name = "[CP] Check Pods Manifest.lock"; 279 | outputPaths = ( 280 | "$(DERIVED_FILE_DIR)/Pods-BarrageRendererDemo-checkManifestLockResult.txt", 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | shellPath = /bin/sh; 284 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 285 | showEnvVarsInLog = 0; 286 | }; 287 | C495AAF13D59EF1528D7F47D /* [CP] Embed Pods Frameworks */ = { 288 | isa = PBXShellScriptBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | ); 292 | inputPaths = ( 293 | ); 294 | name = "[CP] Embed Pods Frameworks"; 295 | outputPaths = ( 296 | ); 297 | runOnlyForDeploymentPostprocessing = 0; 298 | shellPath = /bin/sh; 299 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BarrageRendererDemo/Pods-BarrageRendererDemo-frameworks.sh\"\n"; 300 | showEnvVarsInLog = 0; 301 | }; 302 | /* End PBXShellScriptBuildPhase section */ 303 | 304 | /* Begin PBXSourcesBuildPhase section */ 305 | CCECEDC11B5662F500098E7A /* Sources */ = { 306 | isa = PBXSourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | CCECEDF11B56673E00098E7A /* NSSafeObject.m in Sources */, 310 | CC853E161BFCBE6A000C1FEC /* AdvancedBarrageController.m in Sources */, 311 | CCC0B4A51B654DD50088CFDE /* UIImage+Barrage.m in Sources */, 312 | CCECEDCE1B5662F500098E7A /* AppDelegate.m in Sources */, 313 | D0B2110C1F226D4800905D24 /* FlowerBarrageSprite.m in Sources */, 314 | D0771AAC1F210DAB0092BC2A /* AvatarBarrageView.m in Sources */, 315 | CCD2D7CC1DFED7F3003C8DE2 /* MLEmojiLabel+BarrageView.m in Sources */, 316 | CC853E181BFCBE6A000C1FEC /* CommonBarrageController.m in Sources */, 317 | CCECEDCB1B5662F500098E7A /* main.m in Sources */, 318 | CC853E171BFCBE6A000C1FEC /* BarrageWalkImageTextSprite.m in Sources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | /* End PBXSourcesBuildPhase section */ 323 | 324 | /* Begin PBXVariantGroup section */ 325 | CCECEDD21B5662F500098E7A /* Main.storyboard */ = { 326 | isa = PBXVariantGroup; 327 | children = ( 328 | CCECEDD31B5662F500098E7A /* Base */, 329 | ); 330 | name = Main.storyboard; 331 | sourceTree = ""; 332 | }; 333 | CCECEDD71B5662F500098E7A /* LaunchScreen.xib */ = { 334 | isa = PBXVariantGroup; 335 | children = ( 336 | CCECEDD81B5662F500098E7A /* Base */, 337 | ); 338 | name = LaunchScreen.xib; 339 | sourceTree = ""; 340 | }; 341 | /* End PBXVariantGroup section */ 342 | 343 | /* Begin XCBuildConfiguration section */ 344 | CCECEDE61B5662F500098E7A /* Debug */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ALWAYS_SEARCH_USER_PATHS = NO; 348 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 349 | CLANG_CXX_LIBRARY = "libc++"; 350 | CLANG_ENABLE_MODULES = YES; 351 | CLANG_ENABLE_OBJC_ARC = YES; 352 | CLANG_WARN_BOOL_CONVERSION = YES; 353 | CLANG_WARN_CONSTANT_CONVERSION = YES; 354 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 355 | CLANG_WARN_EMPTY_BODY = YES; 356 | CLANG_WARN_ENUM_CONVERSION = YES; 357 | CLANG_WARN_INT_CONVERSION = YES; 358 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 359 | CLANG_WARN_UNREACHABLE_CODE = YES; 360 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 361 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 362 | COPY_PHASE_STRIP = NO; 363 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 364 | ENABLE_STRICT_OBJC_MSGSEND = YES; 365 | ENABLE_TESTABILITY = YES; 366 | GCC_C_LANGUAGE_STANDARD = gnu99; 367 | GCC_DYNAMIC_NO_PIC = NO; 368 | GCC_NO_COMMON_BLOCKS = YES; 369 | GCC_OPTIMIZATION_LEVEL = 0; 370 | GCC_PREPROCESSOR_DEFINITIONS = ( 371 | "DEBUG=1", 372 | "$(inherited)", 373 | ); 374 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 377 | GCC_WARN_UNDECLARED_SELECTOR = YES; 378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 379 | GCC_WARN_UNUSED_FUNCTION = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 382 | MTL_ENABLE_DEBUG_INFO = YES; 383 | ONLY_ACTIVE_ARCH = YES; 384 | SDKROOT = iphoneos; 385 | }; 386 | name = Debug; 387 | }; 388 | CCECEDE71B5662F500098E7A /* Release */ = { 389 | isa = XCBuildConfiguration; 390 | buildSettings = { 391 | ALWAYS_SEARCH_USER_PATHS = NO; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BOOL_CONVERSION = YES; 397 | CLANG_WARN_CONSTANT_CONVERSION = YES; 398 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 399 | CLANG_WARN_EMPTY_BODY = YES; 400 | CLANG_WARN_ENUM_CONVERSION = YES; 401 | CLANG_WARN_INT_CONVERSION = YES; 402 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 403 | CLANG_WARN_UNREACHABLE_CODE = YES; 404 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 405 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 406 | COPY_PHASE_STRIP = NO; 407 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 408 | ENABLE_NS_ASSERTIONS = NO; 409 | ENABLE_STRICT_OBJC_MSGSEND = YES; 410 | GCC_C_LANGUAGE_STANDARD = gnu99; 411 | GCC_NO_COMMON_BLOCKS = YES; 412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 414 | GCC_WARN_UNDECLARED_SELECTOR = YES; 415 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 416 | GCC_WARN_UNUSED_FUNCTION = YES; 417 | GCC_WARN_UNUSED_VARIABLE = YES; 418 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 419 | MTL_ENABLE_DEBUG_INFO = NO; 420 | SDKROOT = iphoneos; 421 | VALIDATE_PRODUCT = YES; 422 | }; 423 | name = Release; 424 | }; 425 | CCECEDE91B5662F500098E7A /* Debug */ = { 426 | isa = XCBuildConfiguration; 427 | baseConfigurationReference = 1B4AA4F8C40F9CCA4E403615 /* Pods-BarrageRendererDemo.debug.xcconfig */; 428 | buildSettings = { 429 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 430 | DEVELOPMENT_TEAM = C6FYK5JX7E; 431 | INFOPLIST_FILE = BarrageRendererDemo/Info.plist; 432 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 433 | PRODUCT_BUNDLE_IDENTIFIER = "com.exbye.$(PRODUCT_NAME:rfc1034identifier)"; 434 | PRODUCT_NAME = "$(TARGET_NAME)"; 435 | PROVISIONING_PROFILE = "f80299e9-1494-464c-9397-89a33d7b021d"; 436 | PROVISIONING_PROFILE_SPECIFIER = AllNoPushApps; 437 | }; 438 | name = Debug; 439 | }; 440 | CCECEDEA1B5662F500098E7A /* Release */ = { 441 | isa = XCBuildConfiguration; 442 | baseConfigurationReference = 52A68FC2D7818C1433F4955A /* Pods-BarrageRendererDemo.release.xcconfig */; 443 | buildSettings = { 444 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 445 | DEVELOPMENT_TEAM = C6FYK5JX7E; 446 | INFOPLIST_FILE = BarrageRendererDemo/Info.plist; 447 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 448 | PRODUCT_BUNDLE_IDENTIFIER = "com.exbye.$(PRODUCT_NAME:rfc1034identifier)"; 449 | PRODUCT_NAME = "$(TARGET_NAME)"; 450 | PROVISIONING_PROFILE = "f80299e9-1494-464c-9397-89a33d7b021d"; 451 | PROVISIONING_PROFILE_SPECIFIER = AllNoPushApps; 452 | }; 453 | name = Release; 454 | }; 455 | /* End XCBuildConfiguration section */ 456 | 457 | /* Begin XCConfigurationList section */ 458 | CCECEDC01B5662F500098E7A /* Build configuration list for PBXProject "BarrageRendererDemo" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | CCECEDE61B5662F500098E7A /* Debug */, 462 | CCECEDE71B5662F500098E7A /* Release */, 463 | ); 464 | defaultConfigurationIsVisible = 0; 465 | defaultConfigurationName = Release; 466 | }; 467 | CCECEDE81B5662F500098E7A /* Build configuration list for PBXNativeTarget "BarrageRendererDemo" */ = { 468 | isa = XCConfigurationList; 469 | buildConfigurations = ( 470 | CCECEDE91B5662F500098E7A /* Debug */, 471 | CCECEDEA1B5662F500098E7A /* Release */, 472 | ); 473 | defaultConfigurationIsVisible = 0; 474 | defaultConfigurationName = Release; 475 | }; 476 | /* End XCConfigurationList section */ 477 | }; 478 | rootObject = CCECEDBD1B5662F500098E7A /* Project object */; 479 | } 480 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo.xcodeproj/xcshareddata/xcschemes/BarrageRendererDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 84 | 86 | 92 | 93 | 94 | 95 | 96 | 97 | 103 | 105 | 111 | 112 | 113 | 114 | 116 | 117 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/AdvancedBarrage/AdvancedBarrageController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdvancedBarrageController.h 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 15/11/18. 6 | // Copyright (c) 2015年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AdvancedBarrageController : UIViewController 12 | 13 | - (IBAction)start:(id)sender; 14 | - (IBAction)load:(id)sender; 15 | - (IBAction)hybridA:(id)sender; 16 | - (IBAction)hybridB:(id)sender; 17 | - (IBAction)backward:(id)sender; 18 | - (IBAction)foreward:(id)sender; 19 | @property(nonatomic, strong)IBOutlet UILabel *infoLabel; 20 | @end 21 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/AdvancedBarrage/AdvancedBarrageController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdvancedBarrageController.m 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 15/11/18. 6 | // Copyright (c) 2015年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import "AdvancedBarrageController.h" 10 | #import 11 | #import "UIImage+Barrage.h" 12 | #import "BarrageWalkImageTextSprite.h" 13 | 14 | @interface AdvancedBarrageController() 15 | { 16 | BarrageRenderer * _renderer; 17 | NSTimer * _timer; 18 | NSInteger _index; 19 | NSDate * _startTime; 20 | NSTimeInterval _predictedTime; //快进时间 21 | } 22 | 23 | @end 24 | 25 | @implementation AdvancedBarrageController 26 | 27 | - (void)viewDidLoad 28 | { 29 | [super viewDidLoad]; 30 | _index = 0; 31 | _predictedTime = 0.0f; 32 | [self initBarrageRenderer]; 33 | } 34 | 35 | - (void)initBarrageRenderer 36 | { 37 | _renderer = [[BarrageRenderer alloc]init]; 38 | _renderer.delegate = self; 39 | _renderer.redisplay = YES; 40 | [self.view addSubview:_renderer.view]; 41 | [self.view sendSubviewToBack:_renderer.view]; 42 | } 43 | 44 | - (void)dealloc 45 | { 46 | [_renderer stop]; 47 | } 48 | 49 | #pragma mark - Action 50 | - (IBAction)start:(id)sender 51 | { 52 | _startTime = [NSDate date]; 53 | [_renderer start]; 54 | } 55 | 56 | - (IBAction)load:(id)sender 57 | { 58 | NSInteger const number = 10; 59 | NSMutableArray * descriptors = [[NSMutableArray alloc]init]; 60 | for (NSInteger i = 0; i < number; i++) { 61 | [descriptors addObject:[self walkTextSpriteDescriptorWithDelay:i*2+1]]; 62 | } 63 | [_renderer load:descriptors]; 64 | } 65 | 66 | - (IBAction)hybridA:(id)sender 67 | { 68 | [_renderer receive:[self walkImageTextSpriteDescriptorAWithDirection:BarrageWalkDirectionR2L]]; 69 | } 70 | 71 | - (IBAction)hybridB:(id)sender 72 | { 73 | [_renderer receive:[self walkImageTextSpriteDescriptorBWithDirection:BarrageWalkDirectionL2R]]; 74 | } 75 | 76 | - (IBAction)backward:(id)sender 77 | { 78 | _predictedTime -= 5.0f; 79 | } 80 | 81 | - (IBAction)foreward:(id)sender 82 | { 83 | _predictedTime += 5.0f; 84 | } 85 | 86 | #pragma mark - 弹幕描述符生产方法 87 | 88 | /// 生成精灵描述 - 延时文字弹幕 89 | - (BarrageDescriptor *)walkTextSpriteDescriptorWithDelay:(NSTimeInterval)delay 90 | { 91 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 92 | descriptor.spriteName = NSStringFromClass([BarrageWalkTextSprite class]); 93 | descriptor.params[@"text"] = [NSString stringWithFormat:@"延时弹幕(延时%.0f秒):%ld",delay,(long)_index++]; 94 | descriptor.params[@"textColor"] = [UIColor blueColor]; 95 | descriptor.params[@"speed"] = @(100 * (double)random()/RAND_MAX+50); 96 | descriptor.params[@"direction"] = @(1); 97 | descriptor.params[@"delay"] = @(delay); 98 | return descriptor; 99 | } 100 | 101 | /// 图文混排精灵弹幕 - 过场图文弹幕A 102 | - (BarrageDescriptor *)walkImageTextSpriteDescriptorAWithDirection:(NSInteger)direction 103 | { 104 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 105 | descriptor.spriteName = NSStringFromClass([BarrageWalkImageTextSprite class]); 106 | descriptor.params[@"text"] = [NSString stringWithFormat:@"AA-图文混排/::B过场弹幕:%ld",(long)_index++]; 107 | descriptor.params[@"textColor"] = [UIColor greenColor]; 108 | descriptor.params[@"speed"] = @(100 * (double)random()/RAND_MAX+50); 109 | descriptor.params[@"direction"] = @(direction); 110 | return descriptor; 111 | } 112 | 113 | /// 图文混排精灵弹幕 - 过场图文弹幕B 114 | - (BarrageDescriptor *)walkImageTextSpriteDescriptorBWithDirection:(NSInteger)direction 115 | { 116 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 117 | descriptor.spriteName = NSStringFromClass([BarrageWalkTextSprite class]); 118 | descriptor.params[@"text"] = [NSString stringWithFormat:@"AA-图文混排/::B过场弹幕:%ld",(long)_index++]; 119 | descriptor.params[@"textColor"] = [UIColor greenColor]; 120 | descriptor.params[@"speed"] = @(100 * (double)random()/RAND_MAX+50); 121 | descriptor.params[@"direction"] = @(direction); 122 | 123 | NSTextAttachment * attachment = [[NSTextAttachment alloc]init]; 124 | attachment.image = [[UIImage imageNamed:@"avatar"]barrageImageScaleToSize:CGSizeMake(25.0f, 25.0f)]; 125 | NSMutableAttributedString * attributed = [[NSMutableAttributedString alloc]initWithString: 126 | [NSString stringWithFormat:@"BB-图文混排过场弹幕:%ld",(long)_index++]]; 127 | [attributed insertAttributedString:[NSAttributedString attributedStringWithAttachment:attachment] atIndex:7]; 128 | 129 | descriptor.params[@"attributedText"] = attributed; 130 | descriptor.params[@"textColor"] = [UIColor magentaColor]; 131 | descriptor.params[@"speed"] = @(100 * (double)random()/RAND_MAX+50); 132 | descriptor.params[@"direction"] = @(direction); 133 | return descriptor; 134 | } 135 | 136 | - (void)updateMockVideoProgress 137 | { 138 | NSTimeInterval interval = [[NSDate date]timeIntervalSinceDate:_startTime]; 139 | interval += _predictedTime; 140 | self.infoLabel.text = [NSString stringWithFormat:@"视频进度:%.1fs",interval]; 141 | } 142 | 143 | #pragma mark - BarrageRendererDelegate 144 | 145 | - (NSTimeInterval)timeForBarrageRenderer:(BarrageRenderer *)renderer 146 | { 147 | [self updateMockVideoProgress]; // 仅为演示 148 | NSTimeInterval interval = [[NSDate date]timeIntervalSinceDate:_startTime]; 149 | interval += _predictedTime; 150 | return interval; 151 | } 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/AdvancedBarrage/BarrageWalkImageTextSprite.h: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageWalkImageTextSprite.h 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 15/11/15. 6 | // Copyright (c) 2015年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BarrageWalkImageTextSprite : BarrageWalkTextSprite 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/AdvancedBarrage/BarrageWalkImageTextSprite.m: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageWalkImageTextSprite.m 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 15/11/15. 6 | // Copyright (c) 2015年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import "BarrageWalkImageTextSprite.h" 10 | #import 11 | 12 | @implementation BarrageWalkImageTextSprite 13 | 14 | - (instancetype)init 15 | { 16 | if (self = [super init]) { 17 | _viewClassName = @"MLEmojiLabel"; 18 | } 19 | return self; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/AdvancedBarrage/MLEmojiLabel+BarrageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MLEmojiLabel+BarrageView.h 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 16/12/12. 6 | // Copyright © 2016年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MLEmojiLabel (BarrageView) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/AdvancedBarrage/MLEmojiLabel+BarrageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MLEmojiLabel+BarrageView.m 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 16/12/12. 6 | // Copyright © 2016年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import "MLEmojiLabel+BarrageView.h" 10 | #import 11 | 12 | @implementation MLEmojiLabel (BarrageView) 13 | 14 | - (void)configureWithParams:(NSDictionary *)params 15 | { 16 | [super configureWithParams:params]; 17 | self.lineBreakMode = NSLineBreakByCharWrapping; 18 | self.isNeedAtAndPoundSign = YES; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 15/7/15. 6 | // Copyright (c) 2015年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 15/7/15. 6 | // Copyright (c) 2015年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/BarrageDemo/AvatarBarrageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AvatarBarrageView.h 3 | // BarrageRendererDemo 4 | // 5 | // Created by InAsh on 20/07/2017. 6 | // Copyright © 2017 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AvatarBarrageView : UIView 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/BarrageDemo/AvatarBarrageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AvatarBarrageView.m 3 | // BarrageRendererDemo 4 | // 5 | // Created by InAsh on 20/07/2017. 6 | // Copyright © 2017 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import "AvatarBarrageView.h" 10 | #import 11 | 12 | @interface AvatarBarrageView () 13 | @property(nonatomic,strong)UIImageView *imageView; 14 | @property(nonatomic,strong)UILabel *titleLabel; 15 | @property(nonatomic,assign)NSTimeInterval time; 16 | @property(nonatomic,strong)NSArray *titles; 17 | @end 18 | 19 | @implementation AvatarBarrageView 20 | 21 | - (instancetype)initWithFrame:(CGRect)frame 22 | { 23 | if (self = [super initWithFrame:frame]) { 24 | [self loadSubviews]; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)loadSubviews 30 | { 31 | _imageView = [[UIImageView alloc]init]; 32 | self.imageView.image = [UIImage imageNamed:@"avatar"]; 33 | [self addSubview:self.imageView]; 34 | 35 | _titleLabel = [[UILabel alloc]init]; 36 | self.titleLabel.textColor = [UIColor purpleColor]; 37 | self.titleLabel.font = [UIFont systemFontOfSize:16.0f]; 38 | [self addSubview:self.titleLabel]; 39 | } 40 | 41 | - (void)layoutSubviews 42 | { 43 | CGFloat const imageWidth = 30.0f; 44 | [super layoutSubviews]; 45 | NSTimeInterval time = self.time*10; 46 | NSInteger num = 10; 47 | NSInteger frame = fabs(num/2 - time + (NSInteger)(time/num)*num); 48 | CGFloat newImageWidth = imageWidth*pow(0.9, frame); 49 | self.imageView.frame = CGRectMake((imageWidth-newImageWidth)/2, (imageWidth-newImageWidth)/2, newImageWidth, newImageWidth); 50 | self.titleLabel.frame = CGRectMake(imageWidth, 0, self.bounds.size.width-imageWidth, self.bounds.size.height); 51 | } 52 | 53 | - (CGSize)sizeThatFits:(CGSize)size 54 | { 55 | CGFloat const imageWidth = 30.0f; 56 | UILabel *prototypeLabel = self.titleLabel; 57 | CGFloat maxWidth = 0; 58 | CGFloat maxHeight = 0; 59 | for (NSString *title in self.titles) { 60 | prototypeLabel.text = title; 61 | CGSize titleSize = [prototypeLabel sizeThatFits:CGSizeMake(10000, 10)]; 62 | if (titleSize.width>maxWidth) { 63 | maxWidth = titleSize.width; 64 | } 65 | if (titleSize.height>maxHeight) { 66 | maxHeight = titleSize.height; 67 | } 68 | } 69 | if (imageWidth>maxHeight) { 70 | maxHeight = imageWidth; 71 | } 72 | maxWidth+= imageWidth; 73 | return CGSizeMake(maxWidth, maxHeight); 74 | } 75 | 76 | #pragma mark - BarrageViewProtocol 77 | 78 | - (void)configureWithParams:(NSDictionary *)params 79 | { 80 | [super configureWithParams:params]; 81 | self.titles = params[@"titles"]; 82 | self.titleLabel.text = [self.titles firstObject]; 83 | } 84 | 85 | - (void)updateWithTime:(NSTimeInterval)time 86 | { 87 | _time = time; 88 | [self updateTexts]; 89 | [self setNeedsLayout]; 90 | } 91 | 92 | - (void)updateTexts 93 | { 94 | if (!self.titles.count) { 95 | return; 96 | } 97 | NSInteger frame = ((NSInteger)floor(self.time*5)) % self.titles.count; 98 | self.titleLabel.text = self.titles[frame]; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/BarrageDemo/FlowerBarrageSprite.h: -------------------------------------------------------------------------------- 1 | // 2 | // FlowerBarrageSprite.h 3 | // BarrageRendererDemo 4 | // 5 | // Created by InAsh on 21/07/2017. 6 | // Copyright © 2017 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FlowerBarrageSprite : BarrageSprite 12 | 13 | /// 存活时间 14 | @property(nonatomic,assign)NSTimeInterval duration; 15 | @property(nonatomic,assign)CGFloat scaleRatio; 16 | @property(nonatomic,assign)CGFloat rotateRatio; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/BarrageDemo/FlowerBarrageSprite.m: -------------------------------------------------------------------------------- 1 | // 2 | // FlowerBarrageSprite.m 3 | // BarrageRendererDemo 4 | // 5 | // Created by InAsh on 21/07/2017. 6 | // Copyright © 2017 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import "FlowerBarrageSprite.h" 10 | 11 | @interface FlowerBarrageSprite() 12 | { 13 | NSTimeInterval _leftActiveTime; 14 | } 15 | @property(nonatomic,assign)CGRect originRect; 16 | @end 17 | 18 | @implementation FlowerBarrageSprite 19 | 20 | - (instancetype)init 21 | { 22 | if (self = [super init]) { 23 | self.duration = 1.0f; 24 | self.scaleRatio = 2.0f; 25 | self.rotateRatio = 20.0f; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)setDuration:(NSTimeInterval)duration 31 | { 32 | _duration = duration; 33 | _leftActiveTime = _duration; 34 | } 35 | 36 | - (void)updateWithTime:(NSTimeInterval)time 37 | { 38 | self.view.transform = CGAffineTransformIdentity; 39 | 40 | [super updateWithTime:time]; 41 | 42 | CGFloat x = self.originRect.origin.x; 43 | CGFloat y = (self.originRect.origin.y+self.originRect.size.height)*(self.duration - (time - self.timestamp))/self.duration; 44 | self.view.frame = CGRectMake(x, y, self.originRect.size.width, self.originRect.size.height); 45 | 46 | _leftActiveTime = self.duration - (time - self.timestamp); 47 | CGFloat ratio = 1-_leftActiveTime/self.duration; 48 | self.view.alpha = 1-ratio; 49 | 50 | CGFloat rotateRatio = ratio*self.rotateRatio; 51 | CGFloat scaleRatio = pow(self.scaleRatio, ratio); 52 | 53 | CGAffineTransform transform = CGAffineTransformIdentity; 54 | transform = CGAffineTransformScale(transform, scaleRatio, scaleRatio); 55 | transform = CGAffineTransformRotate(transform, M_PI*rotateRatio); 56 | 57 | self.view.transform = transform; 58 | } 59 | 60 | - (CGRect)rectWithTime:(NSTimeInterval)time 61 | { 62 | return self.view.frame; 63 | } 64 | 65 | - (NSTimeInterval)estimateActiveTime 66 | { 67 | return _leftActiveTime; 68 | } 69 | 70 | - (BOOL)validWithTime:(NSTimeInterval)time 71 | { 72 | return [self estimateActiveTime] > 0; 73 | } 74 | 75 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 76 | { 77 | CGPoint origin = CGPointZero; 78 | _originRect.size.height = self.size.height; 79 | _originRect.size.width = self.size.width; 80 | origin.x = (rect.origin.x+rect.size.width-self.size.width)/2+200*((double)random()/RAND_MAX-0.5); 81 | origin.y = rect.origin.y+rect.size.height; 82 | _originRect.origin = origin; 83 | return origin; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/CommonBarrage/CommonBarrageController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageTestController.h 3 | // demo_app_main 4 | // 5 | // Created by UnAsh on 15/7/10. 6 | // Copyright (c) 2015年 UnAsh. All rights reserved. 7 | // 8 | 9 | #import 10 | @interface CommonBarrageController:UIViewController 11 | 12 | - (IBAction)start:(id)sender; 13 | - (IBAction)stop:(id)sender; 14 | - (IBAction)pause:(id)sender; 15 | - (IBAction)resume:(id)sender; 16 | - (IBAction)faster:(id)sender; 17 | - (IBAction)slower:(id)sender; 18 | @property(nonatomic, strong)IBOutlet UILabel *infoLabel; 19 | @end 20 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/CommonBarrage/CommonBarrageController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageTestController.m 3 | // demo_app_main 4 | // 5 | // Created by UnAsh on 15/7/10. 6 | // Copyright (c) 2015年 UnAsh. All rights reserved. 7 | // 8 | 9 | #import "CommonBarrageController.h" 10 | #import 11 | #import "NSSafeObject.h" 12 | #import "UIImage+Barrage.h" 13 | #import "AvatarBarrageView.h" 14 | #import "FlowerBarrageSprite.h" 15 | 16 | @interface CommonBarrageController() 17 | { 18 | BarrageRenderer * _renderer; 19 | NSTimer * _timer; 20 | NSInteger _index; 21 | } 22 | 23 | @end 24 | 25 | @implementation CommonBarrageController 26 | 27 | - (void)viewDidLoad 28 | { 29 | [super viewDidLoad]; 30 | _index = 0; 31 | [self initBarrageRenderer]; 32 | } 33 | 34 | - (void)initBarrageRenderer 35 | { 36 | _renderer = [[BarrageRenderer alloc]init]; 37 | _renderer.smoothness = .2f; 38 | _renderer.delegate = self; 39 | [self.view addSubview:_renderer.view]; 40 | _renderer.canvasMargin = UIEdgeInsetsMake(10, 10, 10, 10); 41 | // 若想为弹幕增加点击功能, 请添加此句话, 并在Descriptor中注入行为 42 | _renderer.view.userInteractionEnabled = YES; 43 | [self.view sendSubviewToBack:_renderer.view]; 44 | } 45 | 46 | - (void)viewDidAppear:(BOOL)animated 47 | { 48 | [super viewDidAppear:animated]; 49 | } 50 | 51 | - (void)startMockingBarrageMessage 52 | { 53 | [_timer invalidate]; 54 | NSSafeObject * safeObj = [[NSSafeObject alloc]initWithObject:self withSelector:@selector(autoSendBarrage)]; 55 | _timer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:safeObj selector:@selector(excute) userInfo:nil repeats:YES]; 56 | } 57 | 58 | - (void)stopMockingBarrageMessage 59 | { 60 | [_timer invalidate]; 61 | } 62 | 63 | - (void)dealloc 64 | { 65 | [_renderer stop]; 66 | } 67 | 68 | #pragma mark - Action 69 | - (IBAction)start:(id)sender 70 | { 71 | [_renderer start]; 72 | [self startMockingBarrageMessage]; 73 | } 74 | - (IBAction)stop:(id)sender 75 | { 76 | [_renderer stop]; 77 | [self stopMockingBarrageMessage]; 78 | } 79 | - (IBAction)pause:(id)sender 80 | { 81 | [_renderer pause]; 82 | } 83 | - (IBAction)resume:(id)sender 84 | { 85 | [_renderer start]; 86 | } 87 | 88 | - (IBAction)faster:(id)sender 89 | { 90 | CGFloat speed = _renderer.speed + 0.5; 91 | if (speed >= 10) { 92 | speed = 10.0f; 93 | } 94 | _renderer.speed = speed; 95 | } 96 | - (IBAction)slower:(id)sender 97 | { 98 | CGFloat speed = _renderer.speed - 0.5; 99 | if (speed <= 0.5f) { 100 | speed = 0.5; 101 | } 102 | _renderer.speed = speed; 103 | } 104 | 105 | - (void)autoSendBarrage 106 | { 107 | NSInteger spriteNumber = [_renderer spritesNumberWithName:nil]; 108 | self.infoLabel.text = [NSString stringWithFormat:@"当前屏幕弹幕数量: %ld",(long)spriteNumber]; 109 | if (spriteNumber <= 500) { // 用来演示如何限制屏幕上的弹幕量 110 | [_renderer receive:[self walkTextSpriteDescriptorWithDirection:BarrageWalkDirectionR2L side:BarrageWalkSideLeft]]; 111 | [_renderer receive:[self walkTextSpriteDescriptorWithDirection:BarrageWalkDirectionR2L side:BarrageWalkSideDefault]]; 112 | [_renderer receive:[self avatarBarrageViewSpriteDescriptorWithDirection:BarrageWalkDirectionR2L side:BarrageWalkSideDefault]]; 113 | 114 | [_renderer receive:[self walkTextSpriteDescriptorWithDirection:BarrageWalkDirectionB2T side:BarrageWalkSideLeft]]; 115 | [_renderer receive:[self walkTextSpriteDescriptorWithDirection:BarrageWalkDirectionB2T side:BarrageWalkSideRight]]; 116 | [_renderer receive:[self flowerImageSpriteDescriptor]]; 117 | [_renderer receive:[self avatarBarrageViewSpriteDescriptorWithDirection:BarrageWalkDirectionR2L side:BarrageWalkSideDefault]]; 118 | 119 | [_renderer receive:[self floatTextSpriteDescriptorWithDirection:BarrageFloatDirectionB2T side:BarrageFloatSideCenter]]; 120 | [_renderer receive:[self floatTextSpriteDescriptorWithDirection:BarrageFloatDirectionT2B side:BarrageFloatSideLeft]]; 121 | [_renderer receive:[self floatTextSpriteDescriptorWithDirection:BarrageFloatDirectionT2B side:BarrageFloatSideRight]]; 122 | 123 | [_renderer receive:[self walkImageSpriteDescriptorWithDirection:BarrageWalkDirectionL2R]]; 124 | [_renderer receive:[self walkImageSpriteDescriptorWithDirection:BarrageWalkDirectionL2R]]; 125 | [_renderer receive:[self floatImageSpriteDescriptorWithDirection:BarrageFloatDirectionT2B]]; 126 | } 127 | } 128 | 129 | #pragma mark - 弹幕描述符生产方法 130 | 131 | /// 生成精灵描述 - 过场文字弹幕 132 | - (BarrageDescriptor *)walkTextSpriteDescriptorWithDirection:(BarrageWalkDirection)direction 133 | { 134 | return [self walkTextSpriteDescriptorWithDirection:direction side:BarrageWalkSideDefault]; 135 | } 136 | 137 | /// 生成精灵描述 - 过场文字弹幕 138 | - (BarrageDescriptor *)walkTextSpriteDescriptorWithDirection:(BarrageWalkDirection)direction side:(BarrageWalkSide)side 139 | { 140 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 141 | descriptor.spriteName = NSStringFromClass([BarrageWalkTextSprite class]); 142 | descriptor.params[@"bizMsgId"] = [NSString stringWithFormat:@"%ld",(long)_index]; 143 | descriptor.params[@"text"] = [NSString stringWithFormat:@"过场文字弹幕:%ld",(long)_index++]; 144 | descriptor.params[@"textColor"] = [UIColor blueColor]; 145 | descriptor.params[@"speed"] = @(100 * (double)random()/RAND_MAX+50); 146 | descriptor.params[@"direction"] = @(direction); 147 | descriptor.params[@"side"] = @(side); 148 | descriptor.params[@"clickAction"] = ^(NSDictionary *params){ 149 | NSString *msg = [NSString stringWithFormat:@"弹幕 %@ 被点击",params[@"bizMsgId"]]; 150 | UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"提示" message:msg delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:nil]; 151 | [alertView show]; 152 | }; 153 | return descriptor; 154 | } 155 | 156 | /// 演示自定义弹幕样式 157 | - (BarrageDescriptor *)avatarBarrageViewSpriteDescriptorWithDirection:(BarrageWalkDirection)direction side:(BarrageWalkSide)side 158 | { 159 | NSArray *titles1 = @[@"♪└|°з°|┐♪",@"♪└|°ε°|┘♪",@"♪┌|°з°|┘♪",@"♪┌|°ε°|┐♪"]; 160 | NSArray *titles2 = @[@"ʕ•̫͡•ʔ",@"ʕ•̫͡•̫͡•ʔ",@"ʕ•̫͡•=•̫͡•ʔ",@"ʕ•̫͡•ʔ ʕ•̫͡•ʔ",@"ʕ•̫͡•ʔ ʕ•̫͡•̫͡•ʔ",@"ʕ•̫͡•ʔ ʕ•̫͡•=•̫͡•ʔ",@"ʕ•̫͡•ʔ ʕ•̫͡•ʔ ʕ•̫͡•ʔ", 161 | @"ʕ•̫͡•ʔ ʕ•̫͡•=•̫͡•ʔ",@"ʕ•̫͡•ʔ ʕ•̫͡•̫͡•ʔ",@"ʕ•̫͡•ʔ ʕ•̫͡•ʔ",@"ʕ•̫͡•=•̫͡•ʔ",@"ʕ•̫͡•̫͡•ʔ",@"ʕ•̫͡•ʔ"]; 162 | 163 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 164 | descriptor.spriteName = NSStringFromClass([BarrageWalkSprite class]); 165 | descriptor.params[@"speed"] = @(100 * (double)random()/RAND_MAX+50); 166 | descriptor.params[@"direction"] = @(direction); 167 | descriptor.params[@"side"] = @(side); 168 | descriptor.params[@"viewClassName"] = NSStringFromClass([AvatarBarrageView class]); 169 | descriptor.params[@"titles"] = (_index%2) ? titles1: titles2; 170 | 171 | __weak BarrageRenderer *render = _renderer; 172 | descriptor.params[@"clickAction"] = ^(NSDictionary *params){ 173 | [render removeSpriteWithIdentifier:params[@"identifier"]]; 174 | }; 175 | 176 | return descriptor; 177 | } 178 | 179 | - (NSString *)randomString 180 | { 181 | NSInteger count = ceil(10*(double)random()/RAND_MAX); 182 | NSMutableString *string = [[NSMutableString alloc]initWithCapacity:10]; 183 | for (NSInteger i = 0; i < count; i++) { 184 | [string appendString:@"Br"]; 185 | } 186 | return [string copy]; 187 | } 188 | 189 | - (BarrageDescriptor *)flowerImageSpriteDescriptor 190 | { 191 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 192 | descriptor.spriteName = NSStringFromClass([FlowerBarrageSprite class]); 193 | descriptor.params[@"image"] = [[UIImage imageNamed:@"avatar"]barrageImageScaleToSize:CGSizeMake(40.0f, 40.0f)]; 194 | descriptor.params[@"duration"] = @(10); 195 | descriptor.params[@"viewClassName"] = NSStringFromClass([UILabel class]); 196 | descriptor.params[@"text"] = @"^*-*^"; 197 | descriptor.params[@"borderWidth"] = @(1); 198 | descriptor.params[@"borderColor"] = [UIColor grayColor]; 199 | descriptor.params[@"scaleRatio"] = @(4); 200 | descriptor.params[@"rotateRatio"] = @(100); 201 | return descriptor; 202 | } 203 | 204 | /// 生成精灵描述 - 浮动文字弹幕 205 | - (BarrageDescriptor *)floatTextSpriteDescriptorWithDirection:(NSInteger)direction 206 | { 207 | return [self floatTextSpriteDescriptorWithDirection:direction side:BarrageFloatSideCenter]; 208 | } 209 | 210 | /// 生成精灵描述 - 浮动文字弹幕 211 | - (BarrageDescriptor *)floatTextSpriteDescriptorWithDirection:(NSInteger)direction side:(BarrageFloatSide)side 212 | { 213 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 214 | descriptor.spriteName = NSStringFromClass([BarrageFloatTextSprite class]); 215 | descriptor.params[@"text"] = [NSString stringWithFormat:@"AA-图文混排/::B过场弹幕:%ld",(long)_index++]; 216 | descriptor.params[@"viewClassName"] = @"MLEmojiLabel"; 217 | descriptor.params[@"textColor"] = [UIColor purpleColor]; 218 | descriptor.params[@"duration"] = @(3); 219 | descriptor.params[@"fadeInTime"] = @(1); 220 | descriptor.params[@"fadeOutTime"] = @(1); 221 | descriptor.params[@"direction"] = @(direction); 222 | descriptor.params[@"side"] = @(side); 223 | return descriptor; 224 | } 225 | 226 | /// 生成精灵描述 - 过场图片弹幕 227 | - (BarrageDescriptor *)walkImageSpriteDescriptorWithDirection:(NSInteger)direction 228 | { 229 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 230 | descriptor.spriteName = NSStringFromClass([BarrageWalkImageSprite class]); 231 | descriptor.params[@"image"] = [[UIImage imageNamed:@"avatar"]barrageImageScaleToSize:CGSizeMake(20.0f, 20.0f)]; 232 | descriptor.params[@"speed"] = @(100 * (double)random()/RAND_MAX+50); 233 | descriptor.params[@"direction"] = @(direction); 234 | descriptor.params[@"trackNumber"] = @5; // 轨道数量 235 | return descriptor; 236 | } 237 | 238 | /// 生成精灵描述 - 浮动图片弹幕 239 | - (BarrageDescriptor *)floatImageSpriteDescriptorWithDirection:(NSInteger)direction 240 | { 241 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 242 | descriptor.spriteName = NSStringFromClass([BarrageFloatImageSprite class]); 243 | descriptor.params[@"image"] = [[UIImage imageNamed:@"avatar"]barrageImageScaleToSize:CGSizeMake(40.0f, 15.0f)]; 244 | descriptor.params[@"duration"] = @(3); 245 | descriptor.params[@"direction"] = @(direction); 246 | return descriptor; 247 | } 248 | 249 | #pragma mark - BarrageRendererDelegate 250 | 251 | /// 演示如何拿到弹幕的生命周期 252 | - (void)barrageRenderer:(BarrageRenderer *)renderer spriteStage:(BarrageSpriteStage)stage spriteParams:(NSDictionary *)params 253 | { 254 | NSString *subid = [params[@"identifier"] substringToIndex:8]; 255 | if (stage == BarrageSpriteStageBegin) { 256 | NSLog(@"id:%@,bizMsgId:%@ =>进入",subid,params[@"bizMsgId"]); 257 | } else if (stage == BarrageSpriteStageEnd) { 258 | NSLog(@"id:%@,bizMsgId:%@ =>离开",subid,params[@"bizMsgId"]); 259 | /* 注释代码演示了如何复制一条弹幕 260 | BarrageDescriptor * descriptor = [[BarrageDescriptor alloc]init]; 261 | descriptor.spriteName = NSStringFromClass([BarrageWalkTextSprite class]); 262 | [descriptor.params addEntriesFromDictionary:params]; 263 | descriptor.params[@"delay"] = @(0); 264 | [renderer receive:descriptor]; 265 | */ 266 | } 267 | } 268 | 269 | #pragma mark - rotate 270 | 271 | - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator 272 | { 273 | [_renderer removePresentSpritesWithName:nil]; 274 | } 275 | 276 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 277 | { 278 | [_renderer removePresentSpritesWithName:nil]; 279 | } 280 | - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 281 | { 282 | [_renderer removePresentSpritesWithName:nil]; 283 | } 284 | 285 | @end 286 | 287 | #pragma mark - just for test - 288 | 289 | #import 290 | #import 291 | 292 | @interface CommonBarrageController (Test) 293 | 294 | @end 295 | 296 | @implementation CommonBarrageController (Test) 297 | 298 | - (void)testSpriteQueue 299 | { 300 | BarrageSpriteQueue *queue = [[BarrageSpriteQueue alloc]init]; 301 | [self addQueueValue:1 queue:queue]; 302 | [self addQueueValue:3 queue:queue]; 303 | [self addQueueValue:8 queue:queue]; 304 | [self addQueueValue:5 queue:queue]; 305 | [self addQueueValue:4 queue:queue]; 306 | [self addQueueValue:8 queue:queue]; 307 | [self addQueueValue:8 queue:queue]; 308 | [self addQueueValue:7 queue:queue]; 309 | [self addQueueValue:8 queue:queue]; 310 | [self addQueueValue:9 queue:queue]; 311 | [self printQueue:queue]; 312 | BarrageSpriteQueue *ge1 = [queue spriteQueueWithDelayGreaterThanOrEqualTo:8]; 313 | [self printQueue:ge1]; 314 | BarrageSpriteQueue *ge2 = [queue spriteQueueWithDelayGreaterThanOrEqualTo:10]; 315 | [self printQueue:ge2]; 316 | BarrageSpriteQueue *ge3 = [queue spriteQueueWithDelayGreaterThanOrEqualTo:0]; 317 | [self printQueue:ge3]; 318 | BarrageSpriteQueue *ge4 = [queue spriteQueueWithDelayGreaterThanOrEqualTo:1]; 319 | [self printQueue:ge4]; 320 | BarrageSpriteQueue *ge5 = [queue spriteQueueWithDelayGreaterThanOrEqualTo:9]; 321 | [self printQueue:ge5]; 322 | BarrageSpriteQueue *g1 = [queue spriteQueueWithDelayGreaterThan:8]; 323 | [self printQueue:g1]; 324 | BarrageSpriteQueue *g2 = [queue spriteQueueWithDelayGreaterThan:10]; 325 | [self printQueue:g2]; 326 | BarrageSpriteQueue *g3 = [queue spriteQueueWithDelayGreaterThan:0]; 327 | [self printQueue:g3]; 328 | BarrageSpriteQueue *g4 = [queue spriteQueueWithDelayGreaterThan:1]; 329 | [self printQueue:g4]; 330 | BarrageSpriteQueue *g5 = [queue spriteQueueWithDelayGreaterThan:9]; 331 | [self printQueue:g5]; 332 | BarrageSpriteQueue *le1 = [queue spriteQueueWithDelayLessThanOrEqualTo:8]; 333 | [self printQueue:le1]; 334 | BarrageSpriteQueue *le2 = [queue spriteQueueWithDelayLessThanOrEqualTo:10]; 335 | [self printQueue:le2]; 336 | BarrageSpriteQueue *le3 = [queue spriteQueueWithDelayLessThanOrEqualTo:0]; 337 | [self printQueue:le3]; 338 | BarrageSpriteQueue *le4 = [queue spriteQueueWithDelayLessThanOrEqualTo:1]; 339 | [self printQueue:le4]; 340 | BarrageSpriteQueue *le5 = [queue spriteQueueWithDelayLessThanOrEqualTo:9]; 341 | [self printQueue:le5]; 342 | BarrageSpriteQueue *l1 = [queue spriteQueueWithDelayLessThan:8]; 343 | [self printQueue:l1]; 344 | BarrageSpriteQueue *l2 = [queue spriteQueueWithDelayLessThan:10]; 345 | [self printQueue:l2]; 346 | BarrageSpriteQueue *l3 = [queue spriteQueueWithDelayLessThan:0]; 347 | [self printQueue:l3]; 348 | BarrageSpriteQueue *l4 = [queue spriteQueueWithDelayLessThan:1]; 349 | [self printQueue:l4]; 350 | BarrageSpriteQueue *l5 = [queue spriteQueueWithDelayLessThan:9]; 351 | [self printQueue:l5]; 352 | } 353 | 354 | - (void)addQueueValue:(NSTimeInterval)value queue:(BarrageSpriteQueue *)queue 355 | { 356 | BarrageSprite *sprite = [[BarrageSprite alloc]init]; 357 | sprite.delay = value; 358 | [queue addSprite:sprite]; 359 | } 360 | 361 | - (void)printQueue:(BarrageSpriteQueue *)queue 362 | { 363 | NSArray *array = [queue ascendingSprites]; 364 | NSMutableString *string = [[NSMutableString alloc]init]; 365 | for (BarrageSprite *sprite in array) { 366 | [string appendString:[@(sprite.delay)stringValue]]; 367 | [string appendString:@","]; 368 | } 369 | NSLog(@"%@",string); 370 | } 371 | 372 | @end 373 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationPortrait 37 | UIInterfaceOrientationPortraitUpsideDown 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/SafeObject/NSSafeObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSafeObject.h 3 | // demo_app_main 4 | // 5 | // Created by UnAsh on 15/7/13. 6 | // Copyright (c) 2015年 UnAsh. All rights reserved. 7 | // 8 | 9 | #import 10 | /// justForText 11 | @interface NSSafeObject : NSObject 12 | 13 | - (instancetype)initWithObject:(id)object; 14 | - (instancetype)initWithObject:(id)object withSelector:(SEL)selector; 15 | - (void)excute; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/SafeObject/NSSafeObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSafeObject.m 3 | // demo_app_main 4 | // 5 | // Created by UnAsh on 15/7/13. 6 | // Copyright (c) 2015年 UnAsh. All rights reserved. 7 | // 8 | 9 | #import "NSSafeObject.h" 10 | @interface NSSafeObject() 11 | { 12 | __weak id _object; 13 | SEL _sel; 14 | } 15 | @end 16 | @implementation NSSafeObject 17 | - (instancetype)initWithObject:(id)object 18 | { 19 | if (self = [super init]) { 20 | _object = object; 21 | _sel = nil; 22 | } 23 | return self; 24 | } 25 | 26 | - (instancetype)initWithObject:(id)object withSelector:(SEL)selector 27 | { 28 | if(self = [super init]) 29 | { 30 | _object = object; 31 | _sel = selector; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)excute 37 | { 38 | if (_object && _sel && [_object respondsToSelector:_sel]) { 39 | #pragma clang diagnostic push 40 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 41 | [_object performSelector:_sel withObject:nil]; 42 | #pragma clang diagnostic pop 43 | } 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/UIImage+Barrage.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Barrage.h 3 | // 4 | // Created by UnAsh on 15/7/8. 5 | // Copyright (c) 2015年 UnAsh. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface UIImage (Barrage) 11 | - (UIImage *)barrageImageScaleToSize:(CGSize)size; 12 | @end 13 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/UIImage+Barrage.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Barrage.h 3 | // 4 | // Created by UnAsh on 15/7/8. 5 | // Copyright (c) 2015年 UnAsh. All rights reserved. 6 | // 7 | 8 | #import "UIImage+Barrage.h" 9 | 10 | @implementation UIImage (Barrage) 11 | - (UIImage *)barrageImageScaleToSize:(CGSize)size 12 | { 13 | // 创建一个bitmap的context 14 | // 并把它设置成为当前正在使用的context 15 | UIGraphicsBeginImageContext(size); 16 | // 绘制改变大小的图片 17 | [self drawInRect:CGRectMake(0, 0, size.width, size.height)]; 18 | // 从当前context中创建一个改变大小后的图片 19 | UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext(); 20 | // 使当前的context出堆栈 21 | UIGraphicsEndImageContext(); 22 | // 返回新的改变大小后的图片 23 | return scaledImage; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unash/BarrageRenderer/e845b9cff4c02f380b10a9fcc055bf8527b2e0be/BarrageRendererDemo/BarrageRendererDemo/avatar.png -------------------------------------------------------------------------------- /BarrageRendererDemo/BarrageRendererDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // BarrageRendererDemo 4 | // 5 | // Created by UnAsh on 15/7/15. 6 | // Copyright (c) 2015年 ExBye Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BarrageRendererDemo/Podfile: -------------------------------------------------------------------------------- 1 | source "https://github.com/CocoaPods/Old-Specs" 2 | #source "https://github.com/CocoaPods/Specs" 3 | 4 | platform :ios, '6.0' 5 | target "BarrageRendererDemo" do 6 | pod 'BarrageRenderer', :path => '../' 7 | pod 'MLEmojiLabel', '1.0.0' 8 | end 9 | -------------------------------------------------------------------------------- /BarrageRendererDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - BarrageRenderer (0.0.1) 3 | - MLEmojiLabel (1.0.0): 4 | - TTTAttributedLabel (~> 1.13.4) 5 | - TTTAttributedLabel (1.13.4) 6 | 7 | DEPENDENCIES: 8 | - BarrageRenderer (from `../`) 9 | - MLEmojiLabel (= 1.0.0) 10 | 11 | EXTERNAL SOURCES: 12 | BarrageRenderer: 13 | :path: "../" 14 | 15 | SPEC CHECKSUMS: 16 | BarrageRenderer: 1c4204aa4242ec066e5e65191e504356570f94d1 17 | MLEmojiLabel: ceeab17d10d296cc00636bf201c8da597fdede10 18 | TTTAttributedLabel: 0a2ac7b2dd726d32a070dafb01446026b11e624f 19 | 20 | PODFILE CHECKSUM: 775706bdffb2cc4ec819b5f702cfaa18a6859f5f 21 | 22 | COCOAPODS: 1.2.0 23 | -------------------------------------------------------------------------------- /BarrageRendererDemo/SafeObject/NSSafeObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSafeObject.h 3 | // demo_app_main 4 | // 5 | // Created by UnAsh on 15/7/13. 6 | // Copyright (c) 2015年 UnAsh. All rights reserved. 7 | // 8 | 9 | #import 10 | /// justForText 11 | @interface NSSafeObject : NSObject 12 | 13 | - (instancetype)initWithObject:(id)object; 14 | - (instancetype)initWithObject:(id)object withSelector:(SEL)selector; 15 | - (void)excute; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /BarrageRendererDemo/SafeObject/NSSafeObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSafeObject.m 3 | // demo_app_main 4 | // 5 | // Created by UnAsh on 15/7/13. 6 | // Copyright (c) 2015年 UnAsh. All rights reserved. 7 | // 8 | 9 | #import "NSSafeObject.h" 10 | @interface NSSafeObject() 11 | { 12 | __weak id _object; 13 | SEL _sel; 14 | } 15 | @end 16 | @implementation NSSafeObject 17 | - (instancetype)initWithObject:(id)object 18 | { 19 | if (self = [super init]) { 20 | _object = object; 21 | _sel = nil; 22 | } 23 | return self; 24 | } 25 | 26 | - (instancetype)initWithObject:(id)object withSelector:(SEL)selector 27 | { 28 | if(self = [super init]) 29 | { 30 | _object = object; 31 | _sel = selector; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)excute 37 | { 38 | if (_object && _sel && [_object respondsToSelector:_sel]) { 39 | [_object performSelector:_sel withObject:nil]; //TODO: 消除warning. 40 | } 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /BarrageRendererDemo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unash/BarrageRenderer/e845b9cff4c02f380b10a9fcc055bf8527b2e0be/BarrageRendererDemo1.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 unash 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BarrageRenderer 2 | 3 | ![TravisCi](https://api.travis-ci.org/unash/BarrageRenderer.svg?branch=master) 4 | 5 | 一个 iOS 上的开源弹幕渲染库. 6 | 7 | ## 发起原因 8 | 9 | 弹幕实质是多个精灵的时间上的渲染方式. PC/Web上已经有很成熟的解决方案了; Android上比较有名的是BiliBili开源的DanmakuFlameMaster, 但是开源社区尚没有比较好的iOS弹幕渲染器.觉得在二次元文化逐渐渗透的今天,视频弹幕已经是很重要的一种情绪表达方式了.没必要重复造轮子,所以我把自己写的一份弹幕渲染引擎开源了.还有一些需要后续完善的地方,但基本功能已经有了.祝大家玩得开心. 10 | 11 | ## Features 12 | 13 | * 提供过场弹幕(4种方向)与悬浮弹幕(2种方向)支持; 支持图片弹幕与文字弹幕. 14 | * 提供图文弹幕接口attributedText, 可按照demo中的指示生成图文混排弹幕. 15 | * 弹幕字体可定义: 颜色,边框,圆角,背景,字体等皆可定制. 16 | * 自动轨道搜寻算法,新发弹幕会根据相同方向的同种弹幕获取最佳运动轨道. 17 | * 支持延时弹幕,为反复播放弹幕提供可能;支持与外界的时间同步. 18 | * 独立的动画时间系统, 可以统一调整动画速度. 19 | * 特制的动画引擎,播放弹幕更流畅,可承接持续的10条/s的弹幕流速. 20 | * 丰富的扩展接口, 实现了父类的接口就可以自定义弹幕动画. 21 | * 概念较清晰,可以为任意UIView绑定弹幕,当然弹幕内容需要创建控件输入. 22 | * 因为作者记性比较差,所以在很多紧要处添加了注释,理解代码更容易. 23 | * 效果动画如下图所示: 24 | 25 | ![效果动画](./BarrageRendererDemo.gif) 26 | 27 | ![效果动画](./BarrageRendererDemo1.gif) 28 | 29 | 视频演示地址: [http://v.youku.com/v_show/id_XMTI5NDM4ODk3Ng==.html](http://v.youku.com/v_show/id_XMTI5NDM4ODk3Ng==.html) 30 | 31 | ## 使用方式 32 | 33 | 1. 下载版本库,进入BarrageRendererDemo目录. 运行pod update拉取相关库, 即可以运行BarrageRendererDemo.xcworkspace 34 | 1. 也可以在您工程的podfile中添加一条引用: *pod 'BarrageRenderer', '1.9.1'* 并在工程目录下的命令行中运行 pod update, (CocoaPods 版本 0.39) 35 | 1. 或者尝试使用 2.1.0 版本,此版本使用更方便,在部分特殊情况下的性能也有所提升. 36 | 1. 或者将代码下载下来, 将BarrageRenderer/目录添加到您的工程当中 37 | 1. 在需要使用弹幕渲染功能的地方 ```#import``` 38 | 1. 创建BarrageRenderer,添加BarrageRenderer.view, 执行start方法, 通过receive方法输入弹幕描述符descriptor, 即可以显示弹幕. 详见demo. 39 | 1. demo的基本功能演示了: 如何在view上增加一条弹幕, 如何启动、停止、暂停、恢复弹幕播放, 如何减速弹幕的运动速度. 40 | 1. demo的高级功能演示了: 如何使用自定义方式添加图文混排弹幕,如何支持录播中在固定时间点显示固定弹幕的逻辑. 41 | 1. 相关的[一篇博文](http://blog.exbye.com/2015/07/an-open-source-ios-barrage-renderer/) 42 | 43 | ## 使用细节 44 | 45 | ### 基本使用 46 | 47 | 一般的,你只需要在 ```- (void)viewDidLoad``` 里创建一个 BarrageRenderer 对象,并将其 view add 到你想要添加弹幕动画的 view 上,配置就结束了。 48 | 49 | 当你想要添加一条弹幕到屏幕上的时候,你只需要创建一个弹幕描述符 BarrageDescriptor, 为其指定弹幕 Sprite 的类名,然后通过 params 设置一些属性, 调用 BarrageRenderer 的 receive 方法即可成功将弹幕显示在屏幕上. 50 | 51 | 弹幕支持的属性可参照 ```BarrageSpriteProtocol.h``` 文件. 以及在 BarrageSprite 族的属性 52 | 53 | ### 动态移除弹幕 54 | 55 | 2.1.0 新增特性。 56 | 57 | 在某些情况下,你可能需要从屏幕中动态地移除弹幕。2.1.0版本为此提供了一个默认的弹幕标识符 ```params[@"identifier"]``` 以及 一个移除弹幕的方法 ```- (void)removeSpriteWithIdentifier:(NSString *)identifier;```。 举例而言,你可以在用户点击弹幕的时候,移除弹幕,代码如下: 58 | 59 | ``` objective-c 60 | __weak BarrageRenderer *render = _renderer; 61 | descriptor.params[@"clickAction"] = ^(NSDictionary *params){ 62 | [render removeSpriteWithIdentifier:params[@"identifier"]]; 63 | }; 64 | ``` 65 | 66 | ### 更新弹幕视图 67 | 68 | 2.1.0 新增特性。 69 | 70 | 有时候,你想要为你的弹幕精灵 view 添加动画。当然,你可以使用 animation 或者 NSTimer. 由于 BarrageRenderer 整体由 CADisplayLink 驱动,你可以借用 BarrageRenderer 的时钟,来更新你的精灵 view 。这样做的好处在于,当你通过 BarrageRenderer 暂停弹幕时,你的 弹幕精灵 view 也将暂停。为此,你可以在自定义弹幕精灵 view 的时候,实现协议方法 ```- (void)updateWithTime:(NSTimeInterval)time```来依据时间更新你的 view 。连贯起来就成了动画。你可以参考 demo 中 AvatarBarrageView 类的实现。 71 | 72 | 需要注意的是,```- (void)updateWithTime:(NSTimeInterval)time``` 中不要放置过多的计算逻辑。在大量弹幕下,这样有可能造成动画的卡顿。 73 | 74 | ### 设置靠边位置 75 | 76 | 1.9.0 版本支持为过场弹幕与悬浮弹幕设置"靠边"属性。对于过场弹幕,可设置side(BarrageWalkSide)属性;对于悬浮弹幕,可设置side(BarrageFloatSide)属性。代码表现为: 77 | 78 | ``` objective-c 79 | descriptor.params[@"side"] = @(BarrageWalkSideRight); // 过场弹幕中,靠右侧行驶 80 | ``` 81 | 82 | ``` objective-c 83 | descriptor.params[@"side"] = @(BarrageFloatSideLeft); // 悬浮弹幕中,靠屏幕左侧堆叠 84 | ``` 85 | 86 | 具体,可参考代码注释,以及demo中的使用范例。 87 | 88 | ### 设置隐入隐出 89 | 90 | 1.8.0 版本新增属性,仅对悬浮弹幕有效,设置如下: 91 | 92 | ``` objective-c 93 | descriptor.params[@"fadeInTime"] = @(1); // 隐入时间 94 | descriptor.params[@"fadeOutTime"] = @(1); // 隐出时间 95 | ``` 96 | 97 | ### 图文混排弹幕 98 | 99 | 最简单的弹幕只是文本, 但有时候你可能需要添加emoji表情或者图片上去。emoji表情是UTF字符集原生支持的,对待他和其他的文本字符没有区别;对于图片,你有两种方式可以添加图片弹幕, 一种是使用 attributedText 设置属性文本,一种是自定义 view. 自定义 view 可以参考 BarrageWalkImageTextSprite。 需要注意的是,如果 ```- (UIView *)bindingView``` 方法返回的是你自定义的 view,你需要覆盖你自定义 view 的 ```- (CGSize)sizeThatFits``` 方法,返回正确的 view 大小。 100 | 101 | 在 V2 版本中,bindingView 方法被废除,你需要通过 descriptor.params[@"viewClassName"] 指明 sprite 所要关联的 view 类。 102 | 103 | ### 直接在 Sprite 子类中布局元素 104 | 105 | 你可能在方法 ```- (UIView *)bindingView``` 中创建了许多视图元素,而并非返回一个自定义 view,因此,这时候你并不方便自定义 view 的 ```- (CGSize)sizeThatFits``` 方法,为此你可以选择覆盖 BarrageSprite 的 size 属性的 ```- (CGSize)size``` 方法,在此方法中返回你的弹幕 view 的大小。当然,在 ```- (UIView *)bindingView``` 里你要设置各个子 view 的位置,以及处理一些可变大小元素比如 UILabel 的布局问题。 106 | 107 | 在 V2 版本中,bindingView 方法被废除,相关的子 view 布局则写在 sprite 关联的 view 类中。 108 | 109 | ### 外部设置弹幕元素的大小 110 | 111 | 你也可以在创建弹幕描述符的时候强制指定弹幕元素的大小。通过设置: 112 | 113 | ```BarrageDescriptor.params[@"mandatorySize"]``` 114 | 115 | 设置此属性之后,你自定义的弹幕 view 的 ```- (CGSize)sizeThatFits``` 将不再起作用,但是覆盖的 ```- (CGSize)size``` 方法仍然是有效的,因为它的优先级比较高。 116 | 117 | ### 如何调节轨道数量 118 | 119 | 继承自 BarrageFloatSprite 与 BarrageWalkSprite 的弹幕都有 trackNumber 属性,你可以用它来设置弹幕轨道数量。在宏 STRIP_NUM 中规定了最大的轨道数量。需要注意的是,BarrageRenderer 中的轨道概念比其他一些弹幕库的轨道概念更复杂,用它可以比较精确地进行冲突检测。当你的弹幕 view 拥有不同大小的时候,你会意识到他的威力。 120 | 121 | 如果你只是希望简单地调节一下轨道数量,你只需为 trackNumber 属性设置一个值即可。比如10,20... 并注意不要超过 STRIP_NUM 的值。 122 | 123 | ### 如何绑定视频播放时间,即支持快进快退 124 | 125 | 这其实是非直播类视频弹幕的刚需。由于涉及到弹幕存储,所以有些内容并不是单独 BarrageRenderer 可以解决的。BarrageRenderer 支持将弹幕绑定到视频的时间点上。实现策略一般有如下几步: 126 | 127 | 1. 在视频初始化的时候,批量添加弹幕 128 | 1. 设置 BarrageRenderer 的 redisplay 属性为 YES, 指定其 delegate. 129 | 1. 对于1条被添加的 BarrageDescriptor, 为其指定 delay,delay 是此条弹幕对应的视频时间点(一般从服务器端获得);在 2.1.0 版本之后,请尽量调用load方法。如果此条被添加的弹幕 BarrageDescriptor 来自某方实时发送的(因而服务端接口并未给出对应视频时间点),可调用 receive 方法。 130 | 1. 实现 BarrageRendererDelegate 协议方法, 在 ```- (NSTimeInterval)timeForBarrageRenderer:(BarrageRenderer *)renderer;``` 方法中返回当前的视频时间点. 当你的视频播放、快进或者快退时,这个时间也会有变。 131 | 132 | 在 Demo 的 AdvancedBarrageController 中演示了这一流程,可以参照。 133 | 134 | ### 如何控制弹幕显示的区域 135 | 136 | 新版已经支持配置弹幕的显示区域。当你把 BarrageRenderer.view 添加到你的业务 view 上之后,默认情况下,弹幕的 view 会适应你的业务 view,你可以通过 BarrageRenderer 的 canvasMargin 属性来设置弹幕显示区域相对于你业务 view 的大小。如: 137 | ```_renderer.canvasMargin = UIEdgeInsetsMake(10, 10, 10, 10);``` 138 | 139 | ### 修改弹幕的初始位置 140 | 141 | 原生的 BarrageSprite 子类不支持自定义弹幕位置。如果需要,你需要自定义你自己的 BarrageSprite。你可以继承 BarrageWalkTextSprite ,然后覆盖 ```- (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites```根据屏幕上已有的同类弹幕信息返回你的弹幕的初始位置。另外需要注意的是,如果你是继承的 BarrageWalkSprite ,你还需要在此方法中计算出终点位置 ```CGPoint _destination;```的值。 142 | 143 | ### 如何设置弹幕速率与文本长度正相关 144 | 145 | 一些弹幕组件的速度会与文本长度成正比,这在 BarrageRenderer 中实现起来也十分容易。在创建弹幕描述符 BarrageDescriptor 的时候,根据文本长度设置 BarrageSprite 的速度值即可。 146 | 147 | ### 限制过场弹幕只显示屏幕上方5行 148 | 149 | 虽然 BarrageWalkSprite 弹幕设有 trackNumber 属性,但是 trackNumber 的本质,并不很适合某些业务下,固定屏幕弹幕行数的需求。若非要如此,比如需要限制过场弹幕只显示屏幕上方5行,可继承 BarrageWalkTextSprite ,重写```- (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites```方法,如下: 150 | 151 | ```objective-c 152 | - (CGPoint)originInBounds:(CGRect)rect withSprites:(NSArray *)sprites 153 | { 154 | CGRect newRect = rect; 155 | newRect.size.height = 100; // 这里你根据你的文本大小,为固定的行数估出一个合适的高度来 156 | self.trackNumber = 5; // 这里是你需要限定的弹幕行数,这里设置之后,descriptor 的 trackNumber 参数自然就失效了 157 | return [super originInBounds:newRect withSprites:sprites]; 158 | } 159 | ``` 160 | 161 | 如此,就生成了满足上述需求的弹幕形式。 162 | 163 | ### 为弹幕添加点击操作 164 | 165 | BarrageRenderer 默认关闭了交互行为的,但如果需要,你可以启用,只需两步: 166 | 167 | 1. BarrageRenderer.view.userInteractionEnabled = YES; 168 | 1. 为 descriptor.params[@"clickAction"] 添加参数 169 | 170 | 在 2.0.1 版本,clickAction 指定的 block 增加了参数,以支持在点击阶段拿到弹幕的相关信息,比如弹幕消息的 id 。 171 | 172 | ### 如何使事件透传到底层(业务)view 173 | 174 | 开启 BarrageRenderer.view.userInteractionEnabled 之后,所有的事件都会被 BarrageRenderer 拦截掉而到不了你的业务 view,这时候你如果在你的业务 View 上添加一个 Button,而 BarrageRenerer.view 又在 Button 之上的话,那么点击这个 Button 是无效的。你可以设置只拦截弹幕上的事件,而将 BarrageRenderer.view 上的事件透传。通过设置属性: 175 | 176 | * BarrageRenderer.masked = NO; // 默认为YES 177 | 178 | ### 如何对弹幕进行限流 179 | 180 | 通过 ```- (NSInteger)spritesNumberWithName:(NSString *)spriteName;``` 方法可以获取屏幕上当前的弹幕数量,你可以在调用 BarrageRenderer 的 receive 方法之前,获取屏幕上的弹幕数量,然后根据一定的规则决定要不要添加这条弹幕。缩小过场弹幕的速度幅度,也有助于降低弹幕的重叠几率。当然,最合理的方式,还是服务端将大量的弹幕过滤到一个合适的范围之中。 181 | 182 | ### 为弹幕添加背景图片 183 | 184 | 框架原生的 BarrageSprite 族并不支持添加背景图片。如果业务需要,可以通过继承 BarrageSprite 的方式添加。 185 | 186 | ### 提升动画性能 187 | 188 | 弹幕一般呈现在视频之上,而视频解码会消耗大量的 CPU,当可用 CPU 不足时,弹幕动画会出现卡顿。为使弹幕流畅,你可以将 trackNumber 调低一些。另外可以对屏幕上的弹幕数量进行限流。 189 | 190 | 实测中,如果多个弹幕的delay时间相同(或相距在1/60s之内),可能使这些弹幕同时进入屏幕,进而导致瞬间卡顿。真实直播弹幕环境下,这种情况出现的比较少。针对性能较好的iPhone,可以设置 BarrageRenderer 的平滑系数 smoothness ,以优化此问题。此参数从 V2 开始支持。 191 | 192 | ## V2 重构 193 | 194 | 自 2.0 版本起, 对 sprite 及 dispatcher 进行了较大幅度的调整。主要有如下几点: 195 | 196 | 1. 分离 sprite 更新逻辑与弹幕视图,方便两者组合复用 197 | 1. 针对前版本 layout 不方便使用的问题做了优化 198 | 1. 为视图添加复用机制(不过实测中并没有太大性能提升) 199 | 1. 增加平滑度参数,优化一些特殊情况下的性能。 200 | 201 | 如果你在使用 V1 系列时,没有创建自己的 sprite 子类,那么你可以在不改动业务代码的时候,升级到 V2 版本; 否则,你需要改动你的 sprite 子类,当然,改动不会太大。 202 | 203 | **虽然我对V2版本做了测试,但是无法涵盖所有情况。** 如果你的应用难以承担较高风险,那么你也可以保持使用 V1 系列,等到 V2 版本相对稳定时再行迁移, V1 不会再添加新的 feature, 但对于显著的 bug 我还是会提供修复; 如果你刚刚接入 V2, 那么建议你尝试使用 V2。 204 | 205 | V2 在创建自定义弹幕的时候,涉及到两部分: 206 | 207 | 1.继承对应的 BarrageRenderer 子类,你也可以直接使用默认的 BarrageWalkSprite 或 BarrageFloatSprite,涉及到修改对应的 view 时,在创建 descriptor 的时候增加一条如下的代码: 208 | 209 | ``` objective-c 210 | descriptor.params[@"viewClassName"] = @"UILabel"; 211 | ``` 212 | 213 | 2.将原来写在 sprite 子类 bindingView 中的布局代码迁出到独立的view中,为此类实现 BarrageViewProtocol 协议中的方法;一般可以为 view 类添加相应的扩展。比如 UILabel+BarrageView.h。如此,你的 sprite 不必再关心布局的细节,只需要处理好时间逻辑。 214 | 215 | 更详细的使用,你可以参考 BarrageRenderer 中提供的 sprite 默认实现或者 demo。 216 | 217 | ### load 方法的语义变化 218 | 219 | 在 v2.1.0 及之后的版本, load 语义有所调整。之前,load 方法所触发的 receive 调用, 会调整 descriptor 的 delay 参数; 而之后的版本,不再整 descriptor 的 delay 参数。所以对于播放弹幕 前/过程中 从网络加载的批量弹幕(delay属性是具体不变的),推荐使用 load 方法。 220 | 221 | ## 支持与联系 222 | 223 | * 欢迎在GitHub上提出相关的issue; 224 | * 欢迎加入qq群讨论:325298378(回复不一定及时). 225 | --------------------------------------------------------------------------------