├── .gitignore ├── BarrageKit.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── BarrageKit ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Barrage_close.imageset │ │ ├── Barrage_close@2x.png │ │ └── Contents.json │ ├── Barrage_pause.imageset │ │ ├── Barrage_pause@2x.png │ │ └── Contents.json │ ├── Barrage_scroll.imageset │ │ ├── Barrage_scroll@2x.png │ │ └── Contents.json │ ├── Barrage_show.imageset │ │ ├── Barrage_show@2x.png │ │ └── Contents.json │ └── Contents.json ├── BarrageEnum.h ├── BarrageKit │ ├── BarrageKit.h │ ├── BarrageManager.h │ ├── BarrageManager.m │ ├── BarrageModel.h │ ├── BarrageModel.m │ ├── BarrageScene.h │ └── BarrageScene.m ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── FirstViewController.h ├── FirstViewController.m ├── FirstViewController.xib ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── BarrageKitTests ├── BarrageKitTests.m └── Info.plist ├── BarrageKitUITests ├── BarrageKitUITests.m └── Info.plist ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /BarrageKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 460B279F1D6EC7DE00E5FB1D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 460B279E1D6EC7DE00E5FB1D /* main.m */; }; 11 | 460B27A21D6EC7DE00E5FB1D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 460B27A11D6EC7DE00E5FB1D /* AppDelegate.m */; }; 12 | 460B27A51D6EC7DE00E5FB1D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 460B27A41D6EC7DE00E5FB1D /* ViewController.m */; }; 13 | 460B27A81D6EC7DF00E5FB1D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 460B27A61D6EC7DF00E5FB1D /* Main.storyboard */; }; 14 | 460B27AA1D6EC7DF00E5FB1D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 460B27A91D6EC7DF00E5FB1D /* Assets.xcassets */; }; 15 | 460B27AD1D6EC7DF00E5FB1D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 460B27AB1D6EC7DF00E5FB1D /* LaunchScreen.storyboard */; }; 16 | 460B27B81D6EC7DF00E5FB1D /* BarrageKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 460B27B71D6EC7DF00E5FB1D /* BarrageKitTests.m */; }; 17 | 460B27C31D6EC7DF00E5FB1D /* BarrageKitUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 460B27C21D6EC7DF00E5FB1D /* BarrageKitUITests.m */; }; 18 | 460B27D51D6EC9A800E5FB1D /* BarrageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 460B27D41D6EC9A800E5FB1D /* BarrageManager.m */; }; 19 | 460B27D91D6ECFC200E5FB1D /* BarrageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 460B27D81D6ECFC200E5FB1D /* BarrageModel.m */; }; 20 | 464D6D2E1D70310A00454DF6 /* BarrageScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 464D6D2D1D70310A00454DF6 /* BarrageScene.m */; }; 21 | 464D6D331D77DC1400454DF6 /* FirstViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 464D6D311D77DC1400454DF6 /* FirstViewController.m */; }; 22 | 464D6D341D77DC1400454DF6 /* FirstViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 464D6D321D77DC1400454DF6 /* FirstViewController.xib */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 460B27B41D6EC7DF00E5FB1D /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 460B27921D6EC7DE00E5FB1D /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 460B27991D6EC7DE00E5FB1D; 31 | remoteInfo = BarrageKit; 32 | }; 33 | 460B27BF1D6EC7DF00E5FB1D /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = 460B27921D6EC7DE00E5FB1D /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = 460B27991D6EC7DE00E5FB1D; 38 | remoteInfo = BarrageKit; 39 | }; 40 | /* End PBXContainerItemProxy section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 460B279A1D6EC7DE00E5FB1D /* BarrageKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BarrageKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 460B279E1D6EC7DE00E5FB1D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | 460B27A01D6EC7DE00E5FB1D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | 460B27A11D6EC7DE00E5FB1D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | 460B27A31D6EC7DE00E5FB1D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 48 | 460B27A41D6EC7DE00E5FB1D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 49 | 460B27A71D6EC7DF00E5FB1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 50 | 460B27A91D6EC7DF00E5FB1D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 51 | 460B27AC1D6EC7DF00E5FB1D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | 460B27AE1D6EC7DF00E5FB1D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 460B27B31D6EC7DF00E5FB1D /* BarrageKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BarrageKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 460B27B71D6EC7DF00E5FB1D /* BarrageKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BarrageKitTests.m; sourceTree = ""; }; 55 | 460B27B91D6EC7DF00E5FB1D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 460B27BE1D6EC7DF00E5FB1D /* BarrageKitUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BarrageKitUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 460B27C21D6EC7DF00E5FB1D /* BarrageKitUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BarrageKitUITests.m; sourceTree = ""; }; 58 | 460B27C41D6EC7DF00E5FB1D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 460B27D31D6EC9A800E5FB1D /* BarrageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BarrageManager.h; path = BarrageKit/BarrageManager.h; sourceTree = ""; }; 60 | 460B27D41D6EC9A800E5FB1D /* BarrageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BarrageManager.m; path = BarrageKit/BarrageManager.m; sourceTree = ""; }; 61 | 460B27D61D6EC9F100E5FB1D /* BarrageKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BarrageKit.h; path = BarrageKit/BarrageKit.h; sourceTree = ""; }; 62 | 460B27D71D6ECFC200E5FB1D /* BarrageModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BarrageModel.h; path = BarrageKit/BarrageModel.h; sourceTree = ""; }; 63 | 460B27D81D6ECFC200E5FB1D /* BarrageModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BarrageModel.m; path = BarrageKit/BarrageModel.m; sourceTree = ""; }; 64 | 464D6D2C1D70310A00454DF6 /* BarrageScene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BarrageScene.h; path = BarrageKit/BarrageScene.h; sourceTree = ""; }; 65 | 464D6D2D1D70310A00454DF6 /* BarrageScene.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BarrageScene.m; path = BarrageKit/BarrageScene.m; sourceTree = ""; }; 66 | 464D6D2F1D742E2E00454DF6 /* BarrageEnum.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BarrageEnum.h; sourceTree = ""; }; 67 | 464D6D301D77DC1400454DF6 /* FirstViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FirstViewController.h; sourceTree = ""; }; 68 | 464D6D311D77DC1400454DF6 /* FirstViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FirstViewController.m; sourceTree = ""; }; 69 | 464D6D321D77DC1400454DF6 /* FirstViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FirstViewController.xib; sourceTree = ""; }; 70 | /* End PBXFileReference section */ 71 | 72 | /* Begin PBXFrameworksBuildPhase section */ 73 | 460B27971D6EC7DE00E5FB1D /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | 460B27B01D6EC7DF00E5FB1D /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | 460B27BB1D6EC7DF00E5FB1D /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | /* End PBXFrameworksBuildPhase section */ 95 | 96 | /* Begin PBXGroup section */ 97 | 460B27911D6EC7DE00E5FB1D = { 98 | isa = PBXGroup; 99 | children = ( 100 | 460B279C1D6EC7DE00E5FB1D /* BarrageKit */, 101 | 460B27B61D6EC7DF00E5FB1D /* BarrageKitTests */, 102 | 460B27C11D6EC7DF00E5FB1D /* BarrageKitUITests */, 103 | 460B279B1D6EC7DE00E5FB1D /* Products */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 460B279B1D6EC7DE00E5FB1D /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 460B279A1D6EC7DE00E5FB1D /* BarrageKit.app */, 111 | 460B27B31D6EC7DF00E5FB1D /* BarrageKitTests.xctest */, 112 | 460B27BE1D6EC7DF00E5FB1D /* BarrageKitUITests.xctest */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | 460B279C1D6EC7DE00E5FB1D /* BarrageKit */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 460B27A01D6EC7DE00E5FB1D /* AppDelegate.h */, 121 | 460B27A11D6EC7DE00E5FB1D /* AppDelegate.m */, 122 | 464D6D301D77DC1400454DF6 /* FirstViewController.h */, 123 | 464D6D311D77DC1400454DF6 /* FirstViewController.m */, 124 | 464D6D321D77DC1400454DF6 /* FirstViewController.xib */, 125 | 460B27A31D6EC7DE00E5FB1D /* ViewController.h */, 126 | 460B27A41D6EC7DE00E5FB1D /* ViewController.m */, 127 | 460B27D21D6EC8CD00E5FB1D /* BarrageKit */, 128 | 460B27D01D6EC8B900E5FB1D /* Other */, 129 | ); 130 | path = BarrageKit; 131 | sourceTree = ""; 132 | }; 133 | 460B279D1D6EC7DE00E5FB1D /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 460B279E1D6EC7DE00E5FB1D /* main.m */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 460B27B61D6EC7DF00E5FB1D /* BarrageKitTests */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 460B27B71D6EC7DF00E5FB1D /* BarrageKitTests.m */, 145 | 460B27B91D6EC7DF00E5FB1D /* Info.plist */, 146 | ); 147 | path = BarrageKitTests; 148 | sourceTree = ""; 149 | }; 150 | 460B27C11D6EC7DF00E5FB1D /* BarrageKitUITests */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 460B27C21D6EC7DF00E5FB1D /* BarrageKitUITests.m */, 154 | 460B27C41D6EC7DF00E5FB1D /* Info.plist */, 155 | ); 156 | path = BarrageKitUITests; 157 | sourceTree = ""; 158 | }; 159 | 460B27D01D6EC8B900E5FB1D /* Other */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 460B27A61D6EC7DF00E5FB1D /* Main.storyboard */, 163 | 460B27A91D6EC7DF00E5FB1D /* Assets.xcassets */, 164 | 460B27AB1D6EC7DF00E5FB1D /* LaunchScreen.storyboard */, 165 | 460B27AE1D6EC7DF00E5FB1D /* Info.plist */, 166 | 460B279D1D6EC7DE00E5FB1D /* Supporting Files */, 167 | ); 168 | name = Other; 169 | sourceTree = ""; 170 | }; 171 | 460B27D21D6EC8CD00E5FB1D /* BarrageKit */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 460B27D61D6EC9F100E5FB1D /* BarrageKit.h */, 175 | 464D6D2F1D742E2E00454DF6 /* BarrageEnum.h */, 176 | 460B27D31D6EC9A800E5FB1D /* BarrageManager.h */, 177 | 460B27D41D6EC9A800E5FB1D /* BarrageManager.m */, 178 | 460B27D71D6ECFC200E5FB1D /* BarrageModel.h */, 179 | 460B27D81D6ECFC200E5FB1D /* BarrageModel.m */, 180 | 464D6D2C1D70310A00454DF6 /* BarrageScene.h */, 181 | 464D6D2D1D70310A00454DF6 /* BarrageScene.m */, 182 | ); 183 | name = BarrageKit; 184 | sourceTree = ""; 185 | }; 186 | /* End PBXGroup section */ 187 | 188 | /* Begin PBXNativeTarget section */ 189 | 460B27991D6EC7DE00E5FB1D /* BarrageKit */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 460B27C71D6EC7DF00E5FB1D /* Build configuration list for PBXNativeTarget "BarrageKit" */; 192 | buildPhases = ( 193 | 460B27961D6EC7DE00E5FB1D /* Sources */, 194 | 460B27971D6EC7DE00E5FB1D /* Frameworks */, 195 | 460B27981D6EC7DE00E5FB1D /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | ); 201 | name = BarrageKit; 202 | productName = BarrageKit; 203 | productReference = 460B279A1D6EC7DE00E5FB1D /* BarrageKit.app */; 204 | productType = "com.apple.product-type.application"; 205 | }; 206 | 460B27B21D6EC7DF00E5FB1D /* BarrageKitTests */ = { 207 | isa = PBXNativeTarget; 208 | buildConfigurationList = 460B27CA1D6EC7DF00E5FB1D /* Build configuration list for PBXNativeTarget "BarrageKitTests" */; 209 | buildPhases = ( 210 | 460B27AF1D6EC7DF00E5FB1D /* Sources */, 211 | 460B27B01D6EC7DF00E5FB1D /* Frameworks */, 212 | 460B27B11D6EC7DF00E5FB1D /* Resources */, 213 | ); 214 | buildRules = ( 215 | ); 216 | dependencies = ( 217 | 460B27B51D6EC7DF00E5FB1D /* PBXTargetDependency */, 218 | ); 219 | name = BarrageKitTests; 220 | productName = BarrageKitTests; 221 | productReference = 460B27B31D6EC7DF00E5FB1D /* BarrageKitTests.xctest */; 222 | productType = "com.apple.product-type.bundle.unit-test"; 223 | }; 224 | 460B27BD1D6EC7DF00E5FB1D /* BarrageKitUITests */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = 460B27CD1D6EC7DF00E5FB1D /* Build configuration list for PBXNativeTarget "BarrageKitUITests" */; 227 | buildPhases = ( 228 | 460B27BA1D6EC7DF00E5FB1D /* Sources */, 229 | 460B27BB1D6EC7DF00E5FB1D /* Frameworks */, 230 | 460B27BC1D6EC7DF00E5FB1D /* Resources */, 231 | ); 232 | buildRules = ( 233 | ); 234 | dependencies = ( 235 | 460B27C01D6EC7DF00E5FB1D /* PBXTargetDependency */, 236 | ); 237 | name = BarrageKitUITests; 238 | productName = BarrageKitUITests; 239 | productReference = 460B27BE1D6EC7DF00E5FB1D /* BarrageKitUITests.xctest */; 240 | productType = "com.apple.product-type.bundle.ui-testing"; 241 | }; 242 | /* End PBXNativeTarget section */ 243 | 244 | /* Begin PBXProject section */ 245 | 460B27921D6EC7DE00E5FB1D /* Project object */ = { 246 | isa = PBXProject; 247 | attributes = { 248 | LastUpgradeCheck = 0730; 249 | ORGANIZATIONNAME = ManoBoo; 250 | TargetAttributes = { 251 | 460B27991D6EC7DE00E5FB1D = { 252 | CreatedOnToolsVersion = 7.3; 253 | DevelopmentTeam = 67442AUEW4; 254 | }; 255 | 460B27B21D6EC7DF00E5FB1D = { 256 | CreatedOnToolsVersion = 7.3; 257 | TestTargetID = 460B27991D6EC7DE00E5FB1D; 258 | }; 259 | 460B27BD1D6EC7DF00E5FB1D = { 260 | CreatedOnToolsVersion = 7.3; 261 | TestTargetID = 460B27991D6EC7DE00E5FB1D; 262 | }; 263 | }; 264 | }; 265 | buildConfigurationList = 460B27951D6EC7DE00E5FB1D /* Build configuration list for PBXProject "BarrageKit" */; 266 | compatibilityVersion = "Xcode 3.2"; 267 | developmentRegion = English; 268 | hasScannedForEncodings = 0; 269 | knownRegions = ( 270 | en, 271 | Base, 272 | ); 273 | mainGroup = 460B27911D6EC7DE00E5FB1D; 274 | productRefGroup = 460B279B1D6EC7DE00E5FB1D /* Products */; 275 | projectDirPath = ""; 276 | projectRoot = ""; 277 | targets = ( 278 | 460B27991D6EC7DE00E5FB1D /* BarrageKit */, 279 | 460B27B21D6EC7DF00E5FB1D /* BarrageKitTests */, 280 | 460B27BD1D6EC7DF00E5FB1D /* BarrageKitUITests */, 281 | ); 282 | }; 283 | /* End PBXProject section */ 284 | 285 | /* Begin PBXResourcesBuildPhase section */ 286 | 460B27981D6EC7DE00E5FB1D /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | 460B27AD1D6EC7DF00E5FB1D /* LaunchScreen.storyboard in Resources */, 291 | 460B27AA1D6EC7DF00E5FB1D /* Assets.xcassets in Resources */, 292 | 460B27A81D6EC7DF00E5FB1D /* Main.storyboard in Resources */, 293 | 464D6D341D77DC1400454DF6 /* FirstViewController.xib in Resources */, 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | }; 297 | 460B27B11D6EC7DF00E5FB1D /* Resources */ = { 298 | isa = PBXResourcesBuildPhase; 299 | buildActionMask = 2147483647; 300 | files = ( 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | 460B27BC1D6EC7DF00E5FB1D /* Resources */ = { 305 | isa = PBXResourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | }; 311 | /* End PBXResourcesBuildPhase section */ 312 | 313 | /* Begin PBXSourcesBuildPhase section */ 314 | 460B27961D6EC7DE00E5FB1D /* Sources */ = { 315 | isa = PBXSourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | 460B27D91D6ECFC200E5FB1D /* BarrageModel.m in Sources */, 319 | 460B27A51D6EC7DE00E5FB1D /* ViewController.m in Sources */, 320 | 464D6D331D77DC1400454DF6 /* FirstViewController.m in Sources */, 321 | 460B27A21D6EC7DE00E5FB1D /* AppDelegate.m in Sources */, 322 | 460B27D51D6EC9A800E5FB1D /* BarrageManager.m in Sources */, 323 | 460B279F1D6EC7DE00E5FB1D /* main.m in Sources */, 324 | 464D6D2E1D70310A00454DF6 /* BarrageScene.m in Sources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | 460B27AF1D6EC7DF00E5FB1D /* Sources */ = { 329 | isa = PBXSourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | 460B27B81D6EC7DF00E5FB1D /* BarrageKitTests.m in Sources */, 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | 460B27BA1D6EC7DF00E5FB1D /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 460B27C31D6EC7DF00E5FB1D /* BarrageKitUITests.m in Sources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | /* End PBXSourcesBuildPhase section */ 345 | 346 | /* Begin PBXTargetDependency section */ 347 | 460B27B51D6EC7DF00E5FB1D /* PBXTargetDependency */ = { 348 | isa = PBXTargetDependency; 349 | target = 460B27991D6EC7DE00E5FB1D /* BarrageKit */; 350 | targetProxy = 460B27B41D6EC7DF00E5FB1D /* PBXContainerItemProxy */; 351 | }; 352 | 460B27C01D6EC7DF00E5FB1D /* PBXTargetDependency */ = { 353 | isa = PBXTargetDependency; 354 | target = 460B27991D6EC7DE00E5FB1D /* BarrageKit */; 355 | targetProxy = 460B27BF1D6EC7DF00E5FB1D /* PBXContainerItemProxy */; 356 | }; 357 | /* End PBXTargetDependency section */ 358 | 359 | /* Begin PBXVariantGroup section */ 360 | 460B27A61D6EC7DF00E5FB1D /* Main.storyboard */ = { 361 | isa = PBXVariantGroup; 362 | children = ( 363 | 460B27A71D6EC7DF00E5FB1D /* Base */, 364 | ); 365 | name = Main.storyboard; 366 | sourceTree = ""; 367 | }; 368 | 460B27AB1D6EC7DF00E5FB1D /* LaunchScreen.storyboard */ = { 369 | isa = PBXVariantGroup; 370 | children = ( 371 | 460B27AC1D6EC7DF00E5FB1D /* Base */, 372 | ); 373 | name = LaunchScreen.storyboard; 374 | sourceTree = ""; 375 | }; 376 | /* End PBXVariantGroup section */ 377 | 378 | /* Begin XCBuildConfiguration section */ 379 | 460B27C51D6EC7DF00E5FB1D /* Debug */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | ALWAYS_SEARCH_USER_PATHS = NO; 383 | CLANG_ANALYZER_NONNULL = YES; 384 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 385 | CLANG_CXX_LIBRARY = "libc++"; 386 | CLANG_ENABLE_MODULES = YES; 387 | CLANG_ENABLE_OBJC_ARC = YES; 388 | CLANG_WARN_BOOL_CONVERSION = YES; 389 | CLANG_WARN_CONSTANT_CONVERSION = YES; 390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 391 | CLANG_WARN_EMPTY_BODY = YES; 392 | CLANG_WARN_ENUM_CONVERSION = YES; 393 | CLANG_WARN_INT_CONVERSION = YES; 394 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 395 | CLANG_WARN_UNREACHABLE_CODE = YES; 396 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 397 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 398 | COPY_PHASE_STRIP = NO; 399 | DEBUG_INFORMATION_FORMAT = dwarf; 400 | ENABLE_STRICT_OBJC_MSGSEND = YES; 401 | ENABLE_TESTABILITY = YES; 402 | GCC_C_LANGUAGE_STANDARD = gnu99; 403 | GCC_DYNAMIC_NO_PIC = NO; 404 | GCC_NO_COMMON_BLOCKS = YES; 405 | GCC_OPTIMIZATION_LEVEL = 0; 406 | GCC_PREPROCESSOR_DEFINITIONS = ( 407 | "DEBUG=1", 408 | "$(inherited)", 409 | ); 410 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 411 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 412 | GCC_WARN_UNDECLARED_SELECTOR = YES; 413 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 414 | GCC_WARN_UNUSED_FUNCTION = YES; 415 | GCC_WARN_UNUSED_VARIABLE = YES; 416 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 417 | MTL_ENABLE_DEBUG_INFO = YES; 418 | ONLY_ACTIVE_ARCH = YES; 419 | SDKROOT = iphoneos; 420 | TARGETED_DEVICE_FAMILY = "1,2"; 421 | }; 422 | name = Debug; 423 | }; 424 | 460B27C61D6EC7DF00E5FB1D /* Release */ = { 425 | isa = XCBuildConfiguration; 426 | buildSettings = { 427 | ALWAYS_SEARCH_USER_PATHS = NO; 428 | CLANG_ANALYZER_NONNULL = YES; 429 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 430 | CLANG_CXX_LIBRARY = "libc++"; 431 | CLANG_ENABLE_MODULES = YES; 432 | CLANG_ENABLE_OBJC_ARC = YES; 433 | CLANG_WARN_BOOL_CONVERSION = YES; 434 | CLANG_WARN_CONSTANT_CONVERSION = YES; 435 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 436 | CLANG_WARN_EMPTY_BODY = YES; 437 | CLANG_WARN_ENUM_CONVERSION = YES; 438 | CLANG_WARN_INT_CONVERSION = YES; 439 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 440 | CLANG_WARN_UNREACHABLE_CODE = YES; 441 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 442 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 443 | COPY_PHASE_STRIP = NO; 444 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 445 | ENABLE_NS_ASSERTIONS = NO; 446 | ENABLE_STRICT_OBJC_MSGSEND = YES; 447 | GCC_C_LANGUAGE_STANDARD = gnu99; 448 | GCC_NO_COMMON_BLOCKS = YES; 449 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 450 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 451 | GCC_WARN_UNDECLARED_SELECTOR = YES; 452 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 453 | GCC_WARN_UNUSED_FUNCTION = YES; 454 | GCC_WARN_UNUSED_VARIABLE = YES; 455 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 456 | MTL_ENABLE_DEBUG_INFO = NO; 457 | SDKROOT = iphoneos; 458 | TARGETED_DEVICE_FAMILY = "1,2"; 459 | VALIDATE_PRODUCT = YES; 460 | }; 461 | name = Release; 462 | }; 463 | 460B27C81D6EC7DF00E5FB1D /* Debug */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 467 | CODE_SIGN_IDENTITY = "iPhone Developer"; 468 | INFOPLIST_FILE = BarrageKit/Info.plist; 469 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 470 | PRODUCT_BUNDLE_IDENTIFIER = ManoBoo.Inc.BarrageKit; 471 | PRODUCT_NAME = "$(TARGET_NAME)"; 472 | }; 473 | name = Debug; 474 | }; 475 | 460B27C91D6EC7DF00E5FB1D /* Release */ = { 476 | isa = XCBuildConfiguration; 477 | buildSettings = { 478 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 479 | CODE_SIGN_IDENTITY = "iPhone Developer"; 480 | INFOPLIST_FILE = BarrageKit/Info.plist; 481 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 482 | PRODUCT_BUNDLE_IDENTIFIER = ManoBoo.Inc.BarrageKit; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | }; 485 | name = Release; 486 | }; 487 | 460B27CB1D6EC7DF00E5FB1D /* Debug */ = { 488 | isa = XCBuildConfiguration; 489 | buildSettings = { 490 | BUNDLE_LOADER = "$(TEST_HOST)"; 491 | INFOPLIST_FILE = BarrageKitTests/Info.plist; 492 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 493 | PRODUCT_BUNDLE_IDENTIFIER = ManoBoo.Inc.BarrageKitTests; 494 | PRODUCT_NAME = "$(TARGET_NAME)"; 495 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BarrageKit.app/BarrageKit"; 496 | }; 497 | name = Debug; 498 | }; 499 | 460B27CC1D6EC7DF00E5FB1D /* Release */ = { 500 | isa = XCBuildConfiguration; 501 | buildSettings = { 502 | BUNDLE_LOADER = "$(TEST_HOST)"; 503 | INFOPLIST_FILE = BarrageKitTests/Info.plist; 504 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 505 | PRODUCT_BUNDLE_IDENTIFIER = ManoBoo.Inc.BarrageKitTests; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BarrageKit.app/BarrageKit"; 508 | }; 509 | name = Release; 510 | }; 511 | 460B27CE1D6EC7DF00E5FB1D /* Debug */ = { 512 | isa = XCBuildConfiguration; 513 | buildSettings = { 514 | INFOPLIST_FILE = BarrageKitUITests/Info.plist; 515 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 516 | PRODUCT_BUNDLE_IDENTIFIER = ManoBoo.Inc.BarrageKitUITests; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | TEST_TARGET_NAME = BarrageKit; 519 | }; 520 | name = Debug; 521 | }; 522 | 460B27CF1D6EC7DF00E5FB1D /* Release */ = { 523 | isa = XCBuildConfiguration; 524 | buildSettings = { 525 | INFOPLIST_FILE = BarrageKitUITests/Info.plist; 526 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 527 | PRODUCT_BUNDLE_IDENTIFIER = ManoBoo.Inc.BarrageKitUITests; 528 | PRODUCT_NAME = "$(TARGET_NAME)"; 529 | TEST_TARGET_NAME = BarrageKit; 530 | }; 531 | name = Release; 532 | }; 533 | /* End XCBuildConfiguration section */ 534 | 535 | /* Begin XCConfigurationList section */ 536 | 460B27951D6EC7DE00E5FB1D /* Build configuration list for PBXProject "BarrageKit" */ = { 537 | isa = XCConfigurationList; 538 | buildConfigurations = ( 539 | 460B27C51D6EC7DF00E5FB1D /* Debug */, 540 | 460B27C61D6EC7DF00E5FB1D /* Release */, 541 | ); 542 | defaultConfigurationIsVisible = 0; 543 | defaultConfigurationName = Release; 544 | }; 545 | 460B27C71D6EC7DF00E5FB1D /* Build configuration list for PBXNativeTarget "BarrageKit" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 460B27C81D6EC7DF00E5FB1D /* Debug */, 549 | 460B27C91D6EC7DF00E5FB1D /* Release */, 550 | ); 551 | defaultConfigurationIsVisible = 0; 552 | defaultConfigurationName = Release; 553 | }; 554 | 460B27CA1D6EC7DF00E5FB1D /* Build configuration list for PBXNativeTarget "BarrageKitTests" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 460B27CB1D6EC7DF00E5FB1D /* Debug */, 558 | 460B27CC1D6EC7DF00E5FB1D /* Release */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | 460B27CD1D6EC7DF00E5FB1D /* Build configuration list for PBXNativeTarget "BarrageKitUITests" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 460B27CE1D6EC7DF00E5FB1D /* Debug */, 567 | 460B27CF1D6EC7DF00E5FB1D /* Release */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | /* End XCConfigurationList section */ 573 | }; 574 | rootObject = 460B27921D6EC7DE00E5FB1D /* Project object */; 575 | } 576 | -------------------------------------------------------------------------------- /BarrageKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BarrageKit/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. 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 | -------------------------------------------------------------------------------- /BarrageKit/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | #import "FirstViewController.h" 12 | 13 | @interface AppDelegate () 14 | 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 21 | // Override point for customization after application launch. 22 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 23 | [self.window makeKeyAndVisible]; 24 | 25 | FirstViewController *firstVC = [[FirstViewController alloc] init]; 26 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:firstVC]; 27 | self.window.rootViewController = nav; 28 | 29 | return YES; 30 | } 31 | 32 | - (void)applicationWillResignActive:(UIApplication *)application { 33 | // 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. 34 | // 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. 35 | } 36 | 37 | - (void)applicationDidEnterBackground:(UIApplication *)application { 38 | // 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. 39 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 40 | } 41 | 42 | - (void)applicationWillEnterForeground:(UIApplication *)application { 43 | // 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. 44 | } 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application { 47 | // 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. 48 | } 49 | 50 | - (void)applicationWillTerminate:(UIApplication *)application { 51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /BarrageKit/Assets.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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Barrage_close.imageset/Barrage_close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiachenmu/BarrageKit/86d3996f06308af70a6df7580ea8127e1370541b/BarrageKit/Assets.xcassets/Barrage_close.imageset/Barrage_close@2x.png -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Barrage_close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Barrage_close@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Barrage_pause.imageset/Barrage_pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiachenmu/BarrageKit/86d3996f06308af70a6df7580ea8127e1370541b/BarrageKit/Assets.xcassets/Barrage_pause.imageset/Barrage_pause@2x.png -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Barrage_pause.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Barrage_pause@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Barrage_scroll.imageset/Barrage_scroll@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiachenmu/BarrageKit/86d3996f06308af70a6df7580ea8127e1370541b/BarrageKit/Assets.xcassets/Barrage_scroll.imageset/Barrage_scroll@2x.png -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Barrage_scroll.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Barrage_scroll@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Barrage_show.imageset/Barrage_show@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiachenmu/BarrageKit/86d3996f06308af70a6df7580ea8127e1370541b/BarrageKit/Assets.xcassets/Barrage_show.imageset/Barrage_show@2x.png -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Barrage_show.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Barrage_show@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BarrageKit/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /BarrageKit/BarrageEnum.h: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageEnum.h 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/29. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #ifndef BarrageEnum_h 10 | #define BarrageEnum_h 11 | 12 | #define Weakself __weak typeof(self) weakSelf = self 13 | 14 | typedef NS_ENUM(NSInteger, BarrageStatusType) { 15 | BarrageStatusTypeNormal = 0, 16 | BarrageStatusTypePause, 17 | BarrageStatusTypeClose, 18 | }; 19 | 20 | // 弹幕的滚动速度,单位为秒 21 | // scroll speed of barrage,in seconds 22 | typedef NS_ENUM(NSInteger, BarrageDisplaySpeedType) { 23 | BarrageDisplaySpeedTypeDefault = 10, 24 | BarrageDisplaySpeedTypeFast = 20, 25 | BarrageDisplaySpeedTypeFaster = 40, 26 | BarrageDisplaySpeedTypeMostFast = 60, 27 | }; 28 | 29 | // 弹幕滚动的方向 30 | typedef NS_ENUM(NSInteger, BarrageScrollDirection) { 31 | BarrageScrollDirectRightToLeft = 0, /* <<<<< */ 32 | BarrageScrollDirectLeftToRight = 1, /* >>>>> */ 33 | BarrageScrollDirectBottomToTop = 2, /* ↑↑↑↑ */ 34 | BarrageScrollDirectTopToBottom = 3, /* ↓↓↓↓ */ 35 | }; 36 | 37 | // 弹幕显示的位置 `default` 为页面全局 38 | // location of barrage, `default` is global page 39 | typedef NS_ENUM(NSInteger, BarrageDisplayLocationType) { 40 | BarrageDisplayLocationTypeDefault = 0, 41 | BarrageDisplayLocationTypeTop = 1, 42 | BarrageDisplayLocationTypeCenter = 2, 43 | BarrageDisplayLocationTypeBottom = 3, 44 | BarrageDisplayLocationTypeHidden, 45 | }; 46 | 47 | // 弹幕的类型 48 | // type of barrage 49 | typedef NS_ENUM(NSInteger, BarrageDisplayType) { 50 | BarrageDisplayTypeDefault = 0, /* only text (纯文字弹幕) */ 51 | BarrageDisplayTypeVote, /* text and vote(文字加投票) */ 52 | BarrageDisplayTypeOther, /* other */ 53 | }; 54 | 55 | // 收到内存警告的清除策略 56 | typedef NS_ENUM(NSInteger, BarrageMemoryWarningMode) { 57 | BarrageMemoryWarningModeHalf = 0, //清除一半 58 | BarrageMemoryWarningModeAll, //清空缓冲池 59 | }; 60 | #endif /* BarrageEnum_h */ 61 | -------------------------------------------------------------------------------- /BarrageKit/BarrageKit/BarrageKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageKit.h 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #ifndef BarrageKit_h 10 | #define BarrageKit_h 11 | 12 | #import "BarrageManager.h" 13 | 14 | 15 | 16 | #endif /* BarrageKit_h */ 17 | -------------------------------------------------------------------------------- /BarrageKit/BarrageKit/BarrageManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageManager.h 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import "BarrageEnum.h" 13 | 14 | #import "BarrageModel.h" 15 | #import "BarrageScene.h" 16 | 17 | 18 | @protocol BarrageManagerDelegate 19 | 20 | // 弹幕的数据来源,来源可以是数组或者 类型的 21 | // Data Sources of barrage, type can be ` NSArray ` or a BarrageModel 22 | - (id)barrageManagerDataSource; 23 | 24 | @end 25 | 26 | @interface BarrageManager : NSObject 27 | 28 | // 弹幕单例 29 | // barrage's singleton 30 | + (instancetype)shareManager; 31 | 32 | + (instancetype)manager; 33 | 34 | // 弹幕缓存池,下次可以从中取出来使用 barrage's cache 35 | - (NSMutableArray *)barrageCache; 36 | 37 | // 正在屏幕上显示的弹幕 38 | - (NSMutableArray *)barrageScenes; 39 | 40 | // 弹幕出现的ViewController, barrage's ViewController 41 | @property (weak, nonatomic) id delegate; 42 | 43 | // the view of show barrage (显示弹幕的View) 44 | @property (weak, nonatomic) UIView *bindingView; 45 | 46 | @property (assign, nonatomic) NSInteger scrollSpeed; 47 | 48 | // 获取弹幕的间隔, get barrages on time, 49 | @property (assign, nonatomic) NSTimeInterval refreshInterval; 50 | 51 | @property (assign, nonatomic) NSInteger displayLocation; 52 | 53 | @property (assign, nonatomic) NSInteger scrollDirection; 54 | 55 | // 文字限长 56 | @property (assign, nonatomic) NSInteger textLengthLimit; 57 | 58 | // 收到内存警告的清除策略 59 | @property (assign, nonatomic) BarrageMemoryWarningMode memoryMode; 60 | 61 | //主动获取弹幕 ,Take the initiative to obtain barrage 62 | - (void)startScroll; 63 | 64 | //被动接收弹幕数据,生成弹幕 ,Passive receiving a barrage of data 65 | /* data's type is ` BarrageModel ` or ` NSArray `*/ 66 | - (void)showBarrageWithDataSource:(id)data; 67 | 68 | // pause or resume barrage 69 | - (void)pauseScroll; 70 | 71 | // show or close barrage 72 | - (void)closeBarrage; 73 | 74 | // 收到内存警告时,可以清除弹幕缓冲池,也可以清除一半的缓冲池 75 | - (void)didReceiveMemoryWarning; 76 | 77 | // 防止内存泄露 78 | - (void)toDealloc; 79 | @end 80 | -------------------------------------------------------------------------------- /BarrageKit/BarrageKit/BarrageManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageManager.m 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import "BarrageManager.h" 10 | 11 | @interface BarrageManager () 12 | 13 | @property (assign, nonatomic) BarrageStatusType currentStatus; 14 | 15 | @property (strong, nonatomic) NSMutableArray *cachePool; 16 | 17 | @property (strong, nonatomic) NSMutableArray *barrageScene; 18 | 19 | @property (strong, nonatomic) NSTimer *timer; 20 | 21 | @end 22 | 23 | @implementation BarrageManager 24 | 25 | #pragma mark - create manager(初始化方法) 26 | 27 | // singleton 单例 28 | static BarrageManager *instance; 29 | //+ (instancetype)shareManager { 30 | // static BarrageManager *manager; 31 | // static dispatch_once_t onceToken; 32 | // dispatch_once(&onceToken, ^{ 33 | // manager = [[BarrageManager alloc] init]; 34 | // }); 35 | // return manager; 36 | //} 37 | 38 | + (instancetype)shareManager { 39 | if (!instance) { 40 | instance = [[BarrageManager alloc] init]; 41 | } 42 | return instance; 43 | } 44 | 45 | + (instancetype)manager { 46 | return [[BarrageManager alloc] init]; 47 | } 48 | 49 | - (instancetype)init 50 | { 51 | self = [super init]; 52 | if (self) { 53 | _scrollSpeed = BarrageDisplaySpeedTypeDefault; 54 | _displayLocation = BarrageDisplayLocationTypeDefault; 55 | _cachePool = [NSMutableArray array]; 56 | _barrageScene = [NSMutableArray array]; 57 | } 58 | return self; 59 | } 60 | 61 | - (void)dealloc { 62 | [_timer invalidate]; 63 | _timer = nil; 64 | [_cachePool removeAllObjects]; 65 | [_barrageScene removeAllObjects]; 66 | NSLog(@"BarrageManager dealloc~"); 67 | } 68 | 69 | - (NSMutableArray *)barrageCache { 70 | return _cachePool; 71 | } 72 | 73 | - (NSMutableArray *)barrageScenes { 74 | return _barrageScene; 75 | } 76 | 77 | - (void)setRefreshInterval:(NSTimeInterval)refreshInterval { 78 | _refreshInterval = refreshInterval; 79 | _timer = [NSTimer scheduledTimerWithTimeInterval:_refreshInterval target:self selector:@selector(buildBarrageScene) userInfo:nil repeats:true]; 80 | [_timer setFireDate:[NSDate distantFuture]]; 81 | } 82 | 83 | 84 | #pragma mark - method(调用方法) 85 | - (void)buildBarrageScene { 86 | /* build barrage model */ 87 | if (![_delegate respondsToSelector:@selector(barrageManagerDataSource)]) { 88 | return; 89 | } 90 | id data = [_delegate barrageManagerDataSource]; 91 | if (!data) { 92 | return; 93 | } 94 | [self showWithData:data]; 95 | } 96 | 97 | - (void)showBarrageWithDataSource:(id)data { 98 | if (!data) { 99 | return; 100 | } 101 | [self showWithData:data]; 102 | } 103 | 104 | - (void)showWithData:(id)data { 105 | /* 106 | 1. determine receiver's type (查看接受对象是否为单个对象或者对象数组) 107 | 2. determine build a new scene OR Taken from the buffer pool inside (查看是否需要新建弹幕) 108 | */ 109 | _currentStatus = BarrageStatusTypeNormal; 110 | @autoreleasepool { 111 | dispatch_async(dispatch_get_main_queue(), ^{ 112 | if ([data isKindOfClass:[BarrageModel class]]) { 113 | //only a BarrageModel 114 | BarrageModel *model = data; 115 | model = [self sync_BarrageManagerConfigure:model]; 116 | //先查看缓冲池是否为空 117 | if (_cachePool.count < 1) { 118 | // nil 119 | BarrageScene *scene = [[BarrageScene alloc] initWithFrame:CGRectZero Model:model]; 120 | [_barrageScene addObject:scene]; 121 | [_bindingView addSubview:scene]; 122 | Weakself; 123 | scene.animationDidStopBlock = ^(BarrageScene *scene_){ 124 | [weakSelf.cachePool addObject:scene_]; 125 | [weakSelf.barrageScene removeObject:scene_]; 126 | [scene_ removeFromSuperview]; 127 | }; 128 | [scene scroll]; 129 | 130 | }else { 131 | //从缓冲池获取到Scene后,将其从缓冲池中移除 132 | // NSLog(@"get from cache"); 133 | BarrageScene *scene = _cachePool.firstObject; 134 | [_barrageScene addObject:scene]; 135 | [_cachePool removeObjectAtIndex:0]; 136 | scene.model = model; 137 | 138 | [_bindingView addSubview:scene]; 139 | [scene scroll]; 140 | } 141 | }else { 142 | // more than one barrage 143 | NSArray *modelArray = data; 144 | [modelArray enumerateObjectsUsingBlock:^(BarrageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 145 | BarrageModel *model = [self sync_BarrageManagerConfigure:obj]; 146 | //先查看缓冲池是否为空 147 | if (_cachePool.count < 1) { 148 | // nil 149 | BarrageScene *scene = [[BarrageScene alloc] initWithFrame:CGRectZero Model:model]; 150 | [_barrageScene addObject:scene]; 151 | [_bindingView addSubview:scene]; 152 | Weakself; 153 | scene.animationDidStopBlock = ^(BarrageScene *scene_){ 154 | [weakSelf.cachePool addObject:scene_]; 155 | [weakSelf.barrageScene removeObject:scene_]; 156 | [scene_ removeFromSuperview]; 157 | }; 158 | [scene scroll]; 159 | 160 | }else { 161 | //从缓冲池获取到Scene后,将其从缓冲池中移除 162 | // NSLog(@"get from cache"); 163 | BarrageScene *scene = _cachePool.firstObject; 164 | [_barrageScene addObject:scene]; 165 | [_cachePool removeObjectAtIndex:0]; 166 | scene.model = model; 167 | 168 | [_bindingView addSubview:scene]; 169 | [scene scroll]; 170 | } 171 | }]; 172 | } 173 | }); 174 | } 175 | } 176 | 177 | -(BarrageModel *) sync_BarrageManagerConfigure:(BarrageModel *)model { 178 | model.speed = _scrollSpeed; 179 | model.direction = _scrollDirection; 180 | model.bindView = _bindingView; 181 | return model; 182 | } 183 | 184 | #pragma mark - Barrage Scroll / Pause / Cloese 185 | 186 | - (void)startScroll { 187 | [_timer setFireDate:[NSDate date]]; 188 | } 189 | 190 | - (void)pauseScroll { 191 | if (_currentStatus == BarrageStatusTypeClose) { 192 | return; 193 | } 194 | if (_currentStatus == BarrageStatusTypeNormal) { 195 | //将屏幕上的弹幕都暂停下来,并且停止获取新的弹幕 196 | [_timer setFireDate:[NSDate distantFuture]]; 197 | 198 | [self.barrageScenes enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 199 | BarrageScene *scene = obj; 200 | [scene pause]; 201 | }]; 202 | _currentStatus = BarrageStatusTypePause; 203 | }else if (_currentStatus == BarrageStatusTypePause) { 204 | [_timer setFireDate:[NSDate date]]; 205 | [self.barrageScenes enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 206 | BarrageScene *scene = obj; 207 | [scene resume]; 208 | }]; 209 | _currentStatus = BarrageStatusTypeNormal; 210 | } 211 | } 212 | 213 | - (void)closeBarrage { 214 | if (_currentStatus == BarrageStatusTypeNormal) { 215 | _currentStatus = BarrageStatusTypeClose; 216 | [_timer setFireDate:[NSDate distantFuture]]; 217 | 218 | [self.barrageScenes enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 219 | BarrageScene *scene = obj; 220 | [scene close]; 221 | }]; 222 | 223 | }else if (_currentStatus == BarrageStatusTypeClose || _currentStatus == BarrageStatusTypePause) { 224 | _currentStatus = BarrageStatusTypeNormal; 225 | [_timer setFireDate:[NSDate date]]; 226 | } 227 | } 228 | 229 | - (void)toDealloc { 230 | 231 | [_timer invalidate]; 232 | _timer = nil; 233 | [_cachePool removeAllObjects]; 234 | [_barrageScene removeAllObjects]; 235 | } 236 | 237 | - (void)didReceiveMemoryWarning { 238 | switch (_memoryMode) { 239 | case BarrageMemoryWarningModeHalf: 240 | [self cleanHalfCache]; 241 | break; 242 | case BarrageMemoryWarningModeAll: 243 | [self cleanAllCache]; 244 | break; 245 | default: 246 | break; 247 | } 248 | } 249 | 250 | - (void)cleanHalfCache { 251 | NSRange range = NSMakeRange(0, _cachePool.count / 2); 252 | [_cachePool removeObjectsInRange:range]; 253 | } 254 | 255 | - (void)cleanAllCache { 256 | [_cachePool removeAllObjects]; 257 | } 258 | 259 | @end 260 | -------------------------------------------------------------------------------- /BarrageKit/BarrageKit/BarrageModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageModel.h 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 弹幕Model 8 | 9 | #import 10 | #import 11 | 12 | #import "BarrageEnum.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface BarrageModel : NSObject 17 | 18 | //MARK: 弹幕Model属性 19 | 20 | //弹幕ID barrage's id 21 | @property (assign, nonatomic) NSInteger numberID; 22 | 23 | //弹幕时间 barrage;s time 24 | @property (strong, nonatomic) NSString *time; 25 | 26 | //弹幕类型 barrage's type 27 | @property (assign, nonatomic) BarrageDisplayType barrageType; 28 | 29 | //弹幕速度 barrage's speed 30 | @property (assign, nonatomic) BarrageDisplaySpeedType speed; 31 | 32 | //弹幕滚动方向 barrage's direction 33 | @property (assign, nonatomic) BarrageScrollDirection direction; 34 | 35 | //弹幕位置 barage's location 36 | @property (assign, nonatomic) BarrageDisplayLocationType displayLocation; 37 | 38 | //弹幕所属的父View barrage's superView 39 | @property (weak, nonatomic) UIView *bindView; 40 | 41 | //弹幕内容 barrage's content 42 | @property (strong, nonatomic, nonnull) NSMutableAttributedString *message; 43 | 44 | //弹幕作者 barrage's author 45 | @property (strong, nonatomic, nullable) id author; 46 | 47 | //弹幕对象 goal object 48 | @property (strong, nonatomic, nullable) id object; 49 | 50 | //弹幕字体 barrage's textfont 51 | @property (copy, nonatomic) UIFont *font; 52 | 53 | //弹幕字体颜色 barrage's textColor 54 | @property (copy, nonatomic) UIColor *textColor; 55 | 56 | //MARK: 弹幕初始化方法 57 | 58 | - (instancetype)initWithNumberID:(NSInteger)numID BarrageContent:(NSMutableAttributedString *)message Author:(nullable id)author Object:(nullable id)object; 59 | 60 | - (instancetype)initWithNumberID:(NSInteger)numID BarrageContent:(NSMutableAttributedString *)message; 61 | 62 | - (instancetype)initWithBarrageContent:(NSMutableAttributedString *)message; 63 | 64 | @end 65 | 66 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /BarrageKit/BarrageKit/BarrageModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageModel.m 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import "BarrageModel.h" 10 | 11 | @implementation BarrageModel 12 | 13 | #pragma mark - initialize(初始化) 14 | 15 | - (instancetype)init { 16 | if (self = [super init]) { 17 | self.numberID = 0; 18 | self.message = [[NSMutableAttributedString alloc] initWithString:@""]; 19 | self.author = nil; 20 | self.object = nil; 21 | } 22 | return self; 23 | } 24 | 25 | - (instancetype)initWithNumberID:(NSInteger)numID BarrageContent:(NSMutableAttributedString *)message Author:(nullable id)author Object:(nullable id)object { 26 | BarrageModel *model = [[BarrageModel alloc] init]; 27 | model.numberID = numID; 28 | model.message = message; 29 | model.author = author; 30 | model.object = object; 31 | 32 | return model; 33 | } 34 | 35 | - (instancetype)initWithNumberID:(NSInteger)numID BarrageContent:(NSMutableAttributedString *)message { 36 | return [self initWithNumberID:numID BarrageContent:message Author:nil Object:nil]; 37 | } 38 | 39 | - (instancetype)initWithBarrageContent:(NSMutableAttributedString *)message { 40 | return [self initWithNumberID:0 BarrageContent:message]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /BarrageKit/BarrageKit/BarrageScene.h: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageScene.h 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/26. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 弹幕View 8 | 9 | #import 10 | 11 | #import "BarrageModel.h" 12 | 13 | @class BarrageManager; 14 | 15 | @class BarrageScene; 16 | 17 | typedef void(^AnimationDidStopBlock)(BarrageScene *scene); 18 | 19 | @interface BarrageScene : UIView 20 | 21 | @property (copy, nonatomic) BarrageModel *model; 22 | 23 | @property (strong, nonatomic) UILabel *titleLabel; 24 | 25 | @property (strong, nonatomic) UIButton *voteButton; 26 | 27 | @property (copy, nonatomic) AnimationDidStopBlock animationDidStopBlock; 28 | 29 | - (void)scroll; 30 | 31 | - (void)pause; 32 | 33 | - (void)resume; 34 | 35 | - (void)close; 36 | 37 | - (instancetype)initWithFrame:(CGRect)frame Model:(BarrageModel *)model; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /BarrageKit/BarrageKit/BarrageScene.m: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageScene.m 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/26. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | #import 9 | 10 | #import "BarrageManager.h" 11 | #import "BarrageScene.h" 12 | 13 | @implementation BarrageScene 14 | 15 | - (instancetype)initWithModel:(BarrageModel *)model { 16 | self = [super init]; 17 | if (self) { 18 | [self setupUI]; 19 | } 20 | return self; 21 | } 22 | 23 | - (instancetype)initWithFrame:(CGRect)frame Model:(BarrageModel *)model{ 24 | self = [super initWithFrame:frame]; 25 | if (self) { 26 | [self setupUI]; 27 | self.model = model; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)dealloc { 33 | // NSLog(@"scene dealloc"); 34 | } 35 | 36 | - (void)setupUI { 37 | // text 38 | _titleLabel = [[UILabel alloc] initWithFrame:self.bounds]; 39 | _titleLabel.font = [UIFont systemFontOfSize:12.0]; 40 | [self addSubview:_titleLabel]; 41 | 42 | // button 43 | _voteButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 0, 12)]; 44 | _voteButton.hidden = true; 45 | _voteButton.titleLabel.font = [UIFont systemFontOfSize:12.0]; 46 | [_voteButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 47 | [_voteButton setTitle:@"Vote" forState:UIControlStateNormal]; 48 | [self addSubview:_voteButton]; 49 | } 50 | 51 | // 加入到SuperView后 开始滚动 52 | - (void)scroll { 53 | 54 | //计算动画距离、时间 calculate time of scroll barrage 55 | CGFloat distance = 0.0; 56 | CGPoint goalPoint = CGPointZero; 57 | switch (_model.direction) { 58 | case BarrageScrollDirectRightToLeft: 59 | distance = CGRectGetWidth(_model.bindView.bounds); 60 | goalPoint = CGPointMake(-CGRectGetWidth(self.frame), CGRectGetMinY(self.frame)); 61 | break; 62 | case BarrageScrollDirectLeftToRight: 63 | distance = CGRectGetWidth(_model.bindView.bounds); 64 | goalPoint = CGPointMake(CGRectGetWidth(_model.bindView.bounds) + CGRectGetWidth(self.frame), CGRectGetMinY(self.frame)); 65 | break; 66 | case BarrageScrollDirectBottomToTop: 67 | distance = CGRectGetHeight(_model.bindView.bounds); 68 | goalPoint = CGPointMake(CGRectGetMinX(self.frame), -CGRectGetHeight(self.frame)); 69 | break; 70 | case BarrageScrollDirectTopToBottom: 71 | distance = CGRectGetHeight(_model.bindView.bounds); 72 | goalPoint = CGPointMake(CGRectGetMinX(self.frame), CGRectGetHeight(self.frame) + CGRectGetMaxY(_model.bindView.bounds)); 73 | break; 74 | default: 75 | break; 76 | } 77 | NSTimeInterval time = distance / _model.speed; 78 | 79 | CGRect goalFrame = self.frame; 80 | goalFrame.origin = goalPoint; 81 | 82 | // layer执行动画 83 | 84 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 85 | animation.delegate = self; 86 | animation.removedOnCompletion = true; 87 | animation.autoreverses = false; 88 | animation.fillMode = kCAFillModeForwards; 89 | 90 | [animation setToValue:[NSValue valueWithCGPoint:CenterPoint(goalFrame)]]; 91 | [animation setDuration:time]; 92 | [self.layer addAnimation:animation forKey:@"kAnimation_BarrageScene"]; 93 | } 94 | 95 | - (void)setModel:(BarrageModel *)model { 96 | _model = model; 97 | [self calculateFrame]; 98 | } 99 | 100 | - (void)pause { 101 | CFTimeInterval pausedTime = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil]; 102 | self.layer.speed = 0.0; 103 | self.layer.timeOffset = pausedTime; 104 | } 105 | 106 | - (void)resume { 107 | CFTimeInterval pausedTime = [self.layer timeOffset]; 108 | self.layer.timeOffset = 0.0; 109 | self.layer.beginTime = 0.0; 110 | self.layer.speed = 1.0; 111 | CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; 112 | self.layer.beginTime = timeSincePause; 113 | } 114 | 115 | - (void)close { 116 | [self.layer removeAllAnimations]; 117 | [self removeFromSuperview]; 118 | } 119 | 120 | #pragma mark - 计算Frame 121 | - (void)calculateFrame { 122 | /* 1. setup UI (基础UI设置) */ 123 | _titleLabel.attributedText = _model.message; 124 | // _titleLabel.textColor = _model.textColor; 125 | 126 | /* 2. determine barrage's type (判断弹幕类型) */ 127 | switch (_model.barrageType) { 128 | case BarrageDisplayTypeVote: 129 | // --投票类型-- 130 | [_titleLabel sizeToFit]; 131 | _voteButton.hidden = false; 132 | [_voteButton sizeToFit]; 133 | CGRect frame = _voteButton.frame; 134 | frame.origin.x = CGRectGetMaxX(_titleLabel.frame); 135 | frame.origin.y = CGRectGetMinY(_titleLabel.frame); 136 | frame.size.height = CGRectGetHeight(_titleLabel.frame); 137 | _voteButton.frame = frame; 138 | self.bounds = CGRectMake(0, 0, CGRectGetWidth(_titleLabel.frame) + CGRectGetWidth(_voteButton.frame), CGRectGetHeight(_titleLabel.frame)); 139 | break; 140 | case BarrageDisplayTypeOther: 141 | // --其他类型-- 142 | 143 | break; 144 | default: 145 | // --BarrageDisplayTypeDefault-- 146 | _voteButton.hidden = true; 147 | [_titleLabel sizeToFit]; 148 | self.bounds = _titleLabel.bounds; 149 | break; 150 | } 151 | 152 | //计算弹幕随机位置 153 | self.frame = [self calculateBarrageSceneFrameWithModel:_model]; 154 | } 155 | 156 | //MARK: 随机计算弹幕的Frame 157 | -(CGRect) calculateBarrageSceneFrameWithModel:(BarrageModel *)model { 158 | CGPoint originPoint; 159 | CGRect sourceFrame = CGRectZero; 160 | switch (model.displayLocation) { 161 | case BarrageDisplayLocationTypeDefault: 162 | sourceFrame = model.bindView.bounds; 163 | break; 164 | case BarrageDisplayLocationTypeTop: 165 | sourceFrame = CGRectMake(0, 0, CGRectGetWidth(model.bindView.bounds), CGRectGetHeight(model.bindView.bounds) / 3.0); 166 | break; 167 | case BarrageDisplayLocationTypeCenter: 168 | sourceFrame = CGRectMake(0, CGRectGetHeight(model.bindView.bounds) / 3.0, CGRectGetWidth(model.bindView.bounds), CGRectGetHeight(model.bindView.bounds) / 3.0); 169 | break; 170 | case BarrageDisplayLocationTypeBottom: 171 | sourceFrame = CGRectMake(0, CGRectGetHeight(model.bindView.bounds) / 3.0 * 2.0, CGRectGetWidth(model.bindView.bounds), CGRectGetHeight(model.bindView.bounds) / 3.0); 172 | break; 173 | default: 174 | break; 175 | } 176 | switch (model.direction) { 177 | case BarrageScrollDirectRightToLeft: 178 | originPoint = CGPointMake(CGRectGetMaxX(sourceFrame), RandomBetween(0, CGRectGetHeight(sourceFrame) - CGRectGetHeight(self.bounds))); 179 | break; 180 | case BarrageScrollDirectLeftToRight: 181 | originPoint = CGPointMake(-CGRectGetWidth(self.bounds), RandomBetween(0, CGRectGetHeight(sourceFrame) - CGRectGetHeight(self.bounds))); 182 | break; 183 | case BarrageScrollDirectBottomToTop: 184 | originPoint = CGPointMake(RandomBetween(0, CGRectGetWidth(sourceFrame)), CGRectGetMaxY(sourceFrame) + CGRectGetHeight(self.bounds)); 185 | break; 186 | case BarrageScrollDirectTopToBottom: 187 | originPoint = CGPointMake(RandomBetween(0, CGRectGetWidth(sourceFrame)), -CGRectGetHeight(self.bounds)); 188 | break; 189 | default: 190 | break; 191 | } 192 | 193 | CGRect frame = self.frame; 194 | frame.origin = originPoint; 195 | 196 | return frame; 197 | } 198 | 199 | #pragma mark - AnimatonDelegate 200 | 201 | // stop 202 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 203 | if (flag) { 204 | __weak typeof(self) SELF = self; 205 | 206 | if (_animationDidStopBlock) { 207 | _animationDidStopBlock(SELF); 208 | } 209 | } 210 | } 211 | 212 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 213 | 214 | if ([_voteButton pointInside:point withEvent:event]) { 215 | NSLog(@"click~~~"); 216 | } 217 | return [super hitTest:point withEvent:event]; 218 | } 219 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 220 | NSLog(@"touchesBegan"); 221 | } 222 | 223 | #pragma mark - Other Method 224 | //随机返回某个区间范围内的值 return a ` float ` Between `smallerNumber ` and ` largerNumber ` 225 | float RandomBetween(float smallerNumber, float largerNumber) { 226 | //设置精确的位数 227 | int precision = 100; 228 | //先取得他们之间的差值 229 | float subtraction = largerNumber - smallerNumber; 230 | //取绝对值 231 | subtraction = ABS(subtraction); 232 | //乘以精度的位数 233 | subtraction *= precision; 234 | //在差值间随机 235 | float randomNumber = arc4random() % ((int)subtraction+1); 236 | //随机的结果除以精度的位数 237 | randomNumber /= precision; 238 | //将随机的值加到较小的值上 239 | float result = MIN(smallerNumber, largerNumber) + randomNumber; 240 | //返回结果 241 | return result; 242 | } 243 | 244 | //返回一个Frame的中心点 245 | CGPoint CenterPoint(CGRect rect) { 246 | return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); 247 | } 248 | @end 249 | -------------------------------------------------------------------------------- /BarrageKit/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /BarrageKit/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /BarrageKit/FirstViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.h 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/9/1. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FirstViewController : UIViewController 12 | 13 | @end 14 | 15 | @interface Person : NSObject 16 | 17 | @property (strong, nonatomic) NSString *name; 18 | @property (assign, nonatomic) NSString *age; 19 | 20 | @end 21 | 22 | @interface Son : Person 23 | 24 | @end -------------------------------------------------------------------------------- /BarrageKit/FirstViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.m 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/9/1. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import "FirstViewController.h" 10 | #import "ViewController.h" 11 | 12 | @interface FirstViewController () 13 | 14 | @property (strong, nonatomic) NSString *str; 15 | 16 | @end 17 | 18 | @implementation FirstViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | } 23 | 24 | - (void)didReceiveMemoryWarning { 25 | [super didReceiveMemoryWarning]; 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | /* 30 | #pragma mark - Navigation 31 | 32 | // In a storyboard-based application, you will often want to do a little preparation before navigation 33 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 34 | // Get the new view controller using [segue destinationViewController]. 35 | // Pass the selected object to the new view controller. 36 | } 37 | */ 38 | - (IBAction)startBarrage:(id)sender { 39 | [self.navigationController pushViewController:[[ViewController alloc] init] animated:true]; 40 | } 41 | 42 | @end 43 | 44 | -------------------------------------------------------------------------------- /BarrageKit/FirstViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /BarrageKit/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 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /BarrageKit/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /BarrageKit/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "BarrageKit.h" 11 | 12 | 13 | typedef void(^MultiParmsBlock)(NSString *p1, ...); 14 | 15 | @interface ViewController () 16 | 17 | @property (strong, nonatomic) BarrageManager *manager; 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view, typically from a nib. 25 | 26 | self.view.backgroundColor = [UIColor whiteColor]; 27 | 28 | UIButton *showBtn = [[UIButton alloc] initWithFrame:CGRectMake(20, self.view.frame.size.height - 100, 30, 30)]; 29 | [showBtn setImage:[UIImage imageNamed:@"Barrage_close"] forState:UIControlStateNormal]; 30 | [showBtn setImage:[UIImage imageNamed:@"Barrage_show"] forState:UIControlStateSelected]; 31 | [showBtn addTarget:self action:@selector(showBarrage:) forControlEvents:UIControlEventTouchUpInside]; 32 | [showBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 33 | [self.view addSubview:showBtn]; 34 | 35 | UIButton *pauseBtn = [[UIButton alloc] initWithFrame:CGRectMake(150, self.view.frame.size.height - 100, 30, 30)]; 36 | [pauseBtn setImage:[UIImage imageNamed:@"Barrage_pause"] forState:UIControlStateNormal]; 37 | [pauseBtn setImage:[UIImage imageNamed:@"Barrage_scroll"] forState:UIControlStateSelected]; 38 | [pauseBtn addTarget:self action:@selector(pauseBarrage:) forControlEvents:UIControlEventTouchUpInside]; 39 | [pauseBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 40 | [self.view addSubview:pauseBtn]; 41 | 42 | _manager = [BarrageManager manager]; 43 | 44 | //出现的View 45 | _manager.bindingView = self.view; 46 | 47 | //delegate 48 | _manager.delegate = self; 49 | 50 | //弹幕显示位置 51 | _manager.displayLocation = BarrageDisplayLocationTypeDefault; 52 | 53 | //滚动方向 54 | _manager.scrollDirection = BarrageScrollDirectRightToLeft; 55 | 56 | //滚动速度 57 | _manager.scrollSpeed = 30; 58 | 59 | //收到内存警告的处理方式 60 | _manager.memoryMode = BarrageMemoryWarningModeHalf; 61 | 62 | //刷新时间 63 | _manager.refreshInterval = 1.0; 64 | 65 | //开始滚动 manager主动获取弹幕,另外一种方式,`[_manager showBarrageWithDataSource:m]` 退出弹幕即可 66 | [_manager startScroll]; 67 | 68 | 69 | //_manager被动接收弹幕 70 | // int a = arc4random() % 10000; 71 | // NSString *str = [NSString stringWithFormat:@"%d 呵呵哒",a]; 72 | // 73 | // NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:str]; 74 | // [attr addAttribute:NSForegroundColorAttributeName value:RandomColor() range:NSMakeRange(0, str.length)]; 75 | // 76 | // BarrageModel *m = [[BarrageModel alloc] initWithBarrageContent:attr]; 77 | // m.displayLocation = BarrageDisplayLocationTypeDefault; 78 | // m.barrageType = BarrageDisplayTypeVote; 79 | 80 | // [_manager showBarrageWithDataSource:m]; 81 | } 82 | 83 | - (void)viewWillDisappear:(BOOL)animated { 84 | [_manager closeBarrage]; 85 | [super viewWillDisappear:animated]; 86 | } 87 | 88 | - (void)didReceiveMemoryWarning { 89 | [super didReceiveMemoryWarning]; 90 | // Dispose of any resources that can be recreated. 91 | 92 | // 收到内存警告时,清楚弹幕缓冲池 When you receive a memory warning,clean the barrage's cache 93 | [_manager didReceiveMemoryWarning]; 94 | } 95 | 96 | - (void)dealloc { 97 | [_manager toDealloc]; 98 | } 99 | 100 | UIColor * RandomColor() { 101 | float a = (arc4random() % 255) / 255.0; 102 | float b = (arc4random() % 255) / 255.0; 103 | float c = (arc4random() % 255) / 255.0; 104 | 105 | return [UIColor colorWithRed:a green:b blue:c alpha:1.0]; 106 | } 107 | 108 | 109 | #pragma mark - BarrageManagerDelegate 110 | - (id)barrageManagerDataSource { 111 | 112 | //演示需要:随机弹幕方向 113 | int barrageScrollDierct = arc4random() % 4; 114 | _manager.scrollDirection = (NSInteger)barrageScrollDierct; 115 | 116 | //生成弹幕信息 117 | int a = arc4random() % 10000; 118 | NSString *str = [NSString stringWithFormat:@"%d 呵呵哒",a]; 119 | 120 | NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:str]; 121 | [attr addAttribute:NSForegroundColorAttributeName value:RandomColor() range:NSMakeRange(0, str.length)]; 122 | 123 | BarrageModel *m = [[BarrageModel alloc] initWithBarrageContent:attr]; 124 | m.displayLocation = BarrageDisplayLocationTypeDefault; 125 | m.barrageType = BarrageDisplayTypeVote; 126 | return m; 127 | } 128 | 129 | - (void)showBarrage:(UIButton *)showBtn { 130 | showBtn.selected = !showBtn.isSelected; 131 | [_manager closeBarrage]; 132 | } 133 | 134 | - (void)pauseBarrage:(UIButton *)pauseBtn{ 135 | pauseBtn.selected = !pauseBtn.isSelected; 136 | [_manager pauseScroll]; 137 | } 138 | 139 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 140 | UITouch *touch = [touches anyObject]; 141 | CGPoint touchPoint = [touch locationInView:self.view]; 142 | [[_manager barrageScenes] enumerateObjectsUsingBlock:^(BarrageScene * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 143 | if ([obj.layer.presentationLayer hitTest:touchPoint]) { 144 | //弹幕类型为投票类型时,为弹幕添加点击事件,请在此处添加 145 | /* if barrage's type is ` BarrageDisplayTypeVote `, add your code here*/ 146 | NSLog(@"message = %@",obj.model.message.string); 147 | } 148 | }]; 149 | } 150 | @end 151 | 152 | -------------------------------------------------------------------------------- /BarrageKit/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // BarrageKit 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. 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 | -------------------------------------------------------------------------------- /BarrageKitTests/BarrageKitTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageKitTests.m 3 | // BarrageKitTests 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BarrageKitTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation BarrageKitTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /BarrageKitTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /BarrageKitUITests/BarrageKitUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // BarrageKitUITests.m 3 | // BarrageKitUITests 4 | // 5 | // Created by jiachenmu on 16/8/25. 6 | // Copyright © 2016年 ManoBoo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BarrageKitUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation BarrageKitUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /BarrageKitUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 ManoBoo 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BarrageKit 2 | ManoBoo所写的一个弹幕小插件 3 | # BarrageKit弹幕插件 4 | 5 | 标签(空格分隔): BarrageKit 6 | 7 | --- 8 | 9 | ##前言 10 | --- 11 | ####现在直播这么火,好多直播框架,诸如[IJKMediaFramework](https://github.com/Bilibili/ijkplayer)等更是方便了直播类APP的开发,那我们也一起给它添砖加瓦吧。 12 | 13 | ##插件介绍 14 | --- 15 | 插件是基于Objective-C编写的,整体思路较为简单,功能包括: 16 | 17 | 1. 弹幕的滚动方向、滚动速度 18 | 2. 弹幕的暂停与恢复、显示与隐藏 19 | 3. 弹幕的类型:纯文字弹幕、投票类弹幕、其他等(可自定义) 20 | 4. 弹幕的推送方式:主动获取弹幕源、被动接收 21 | 5. 弹幕的缓存,避免大量弹幕出现时内存Boom 22 | 23 | **还有一些不完善的地方(比如弹幕的轨道现为随机生成,可能会重叠)及一些新的功能欢迎[issue](https://github.com/jiachenmu/BarrageKit/issues)** 24 | ##插件展示 25 | --- 26 | 27 | - 四个方向的弹幕滚动 28 | 29 | ![四个方向的弹幕滚动](http://i4.buimg.com/567571/7b178d34b7c65e87.gif) 30 | 31 | - 弹幕的暂停与恢复 32 | ![弹幕的暂停与恢复](http://i2.piimg.com/567571/3bc4ab4b9a15dc49.gif) 33 | --- 34 | ##核心代码介绍 35 | --- 36 | ###①`BarrageManager`弹幕管理者 37 | **数组介绍:** 38 | - `_cachePool`为弹幕单元的缓冲数组,从屏幕上移除的`BarrageScene`会加入到该数组中 39 | - `_barrageScene`为当前屏幕上正在显示的弹幕显示单元的数组,弹幕显示的时候会加入到该数组中 40 | 41 | 42 | `BarrageManager` 作为弹幕的管理者,**流程**为: 43 | 44 | - 初始化弹幕manager,启动`timer`,主动拉取弹幕数据,调用`-delegate barrageManagerDataSource`返回数据。 45 | - 调用`- (void)showWithData:(id)data`方法显示弹幕,`data`可以为`BarrageModel`或`NSArray`格式 46 | - 判断缓冲池是否为空,为空则新建弹幕显示单元`BarrageScene`,并加入到`_barrageScene`数组中。若不为空,则取出`_cachePool`的`firstObject`进行重用并从`_cachePool`中移除,添加到`_barrageScene`中来 47 | 48 | ### ②`BarrageModel`弹幕模型 49 | ####`message`弹幕信息为`NSMutableAttributedString`类型,图片、表情等都可以使用了 50 | **模型属性:** 51 | ``` 52 | //弹幕ID barrage's id 53 | @property (assign, nonatomic) NSInteger numberID; 54 | 55 | //弹幕时间 barrage;s time 56 | @property (strong, nonatomic) NSString *time; 57 | 58 | //弹幕类型 barrage's type 59 | @property (assign, nonatomic) BarrageDisplayType barrageType; 60 | 61 | //弹幕速度 barrage's speed 62 | @property (assign, nonatomic) BarrageDisplaySpeedType speed; 63 | 64 | //弹幕滚动方向 barrage's direction 65 | @property (assign, nonatomic) BarrageScrollDirection direction; 66 | 67 | //弹幕位置 barage's location 68 | @property (assign, nonatomic) BarrageDisplayLocationType displayLocation; 69 | 70 | //弹幕所属的父View barrage's superView 71 | @property (weak, nonatomic) UIView *bindView; 72 | 73 | //弹幕内容 barrage's content 74 | @property (strong, nonatomic, nonnull) NSMutableAttributedString *message; 75 | 76 | //弹幕作者 barrage's author 77 | @property (strong, nonatomic, nullable) id author; 78 | 79 | //弹幕对象 goal object 80 | @property (strong, nonatomic, nullable) id object; 81 | 82 | //弹幕字体 barrage's textfont 83 | @property (copy, nonatomic) UIFont *font; 84 | 85 | //弹幕字体颜色 barrage's textColor 86 | @property (copy, nonatomic) UIColor *textColor; 87 | ``` 88 | 89 | ###③`BarrageScene`弹幕显示单元 90 | 91 | - 滚动Scroll 92 | - ①根据弹幕的滚动速度和滚动方向计算弹幕的滚动距离和所需要的时间 93 | - ②使用CABasicAnimation完成动画,后期弹幕的暂停和回复比较方便 94 | - ③弹幕滚动完毕后,执行`_animationDidStopBlock`,将该scene加入到`manager`的`cachePool`中等待被重用 95 | 96 | - 重用 97 | - 弹幕从`BarrageManager`的`cachePool`中取出来,根据`BarrageModel`弹幕信息重新初始化frame且重新开始动画 98 | - 暂停 pause 99 | - 暂停layer动画即可 100 | 101 | ``` 102 | CFTimeInterval pausedTime = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil]; 103 | self.layer.speed = 0.0; 104 | self.layer.timeOffset = pausedTime; 105 | ``` 106 | 107 | - 恢复 resume 108 | - 恢复layer动画即可 109 | 110 | ``` 111 | CFTimeInterval pausedTime = [self.layer timeOffset]; 112 | self.layer.timeOffset = 0.0; 113 | self.layer.beginTime = 0.0; 114 | self.layer.speed = 1.0; 115 | CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; 116 | self.layer.beginTime = timeSincePause; 117 | ``` 118 | 119 | 120 | - 关闭 close 121 | ``` 122 | [self.layer removeAllAnimations]; 123 | [self removeFromSuperview]; 124 | ``` 125 | 126 | ##插件使用 127 | [下载项目]()导入到项目中,使用时`import 'BarrageKit.h'`即可 128 | ``` 129 | _manager = [BarrageManager manager]; 130 | 131 | //出现的View 132 | _manager.bindingView = self.view; 133 | 134 | //delegate 135 | _manager.delegate = self; 136 | 137 | //弹幕显示位置 138 | _manager.displayLocation = BarrageDisplayLocationTypeDefault; 139 | 140 | //滚动方向 141 | _manager.scrollDirection = BarrageScrollDirectRightToLeft; 142 | 143 | //滚动速度 144 | _manager.scrollSpeed = 30; 145 | 146 | //收到内存警告的处理方式 147 | _manager.memoryMode = BarrageMemoryWarningModeHalf; 148 | 149 | //刷新时间 150 | _manager.refreshInterval = 1.0; 151 | 152 | //开始滚动 manager主动获取弹幕,另外一种方式,`[_manager showBarrageWithDataSource:m]` 退出弹幕即可 153 | [_manager startScroll]; 154 | ``` 155 | **Tips:** 156 | - ①如果弹幕为投票类型的弹幕时,请重写`ViewController`的`touchesBegan` 方法 157 | 158 | ``` 159 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 160 | UITouch *touch = [touches anyObject]; 161 | CGPoint touchPoint = [touch locationInView:self.view]; 162 | [[_manager barrageScenes] enumerateObjectsUsingBlock:^(BarrageScene * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 163 | if ([obj.layer.presentationLayer hitTest:touchPoint]) { 164 | //弹幕类型为投票类型时,为弹幕添加点击事件,请在此处添加 165 | /* if barrage's type is ` BarrageDisplayTypeVote `, add your code here*/ 166 | NSLog(@"message = %@",obj.model.message.string); 167 | } 168 | }]; 169 | } 170 | ``` 171 | 172 | - ②重写`ViewController`的`dealloc`方法,执行`BarrageManager`的`toDealloc` 173 | - ③ViewController收到内存警告时,`[_manager didReceiveMemoryWarning];`将会按照`memoryMode`指定的方法清楚缓冲池 174 | ``` 175 | - (void)didReceiveMemoryWarning { 176 | [super didReceiveMemoryWarning]; 177 | // Dispose of any resources that can be recreated. 178 | 179 | // 收到内存警告时,清楚弹幕缓冲池 When you receive a memory warning,clean the barrage's cache 180 | [_manager didReceiveMemoryWarning]; 181 | } 182 | ``` 183 | - ④如果要高度自定义弹幕显示,可以修改`BarrageScene`中的初始化代码 184 | 185 | 186 | ##最后 187 | ###大家查看项目后,欢迎[issue](https://github.com/jiachenmu/BarrageKit/issues),同时有其他的功能或建议欢迎提出来,简书、github均可。 188 | - [项目及Demo下载地址](https://github.com/jiachenmu/BarrageKit) 189 | - ![IOS开发交流群](http://i2.buimg.com/567571/cd9b3c8edd6f164c.png) 190 | --------------------------------------------------------------------------------