├── JSMatrixCodeRainView.swift ├── LICENSE.md ├── NFILicense.txt ├── README.md ├── demo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── showing.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── showing.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── shuozhang.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── demo.xcscheme │ └── xcschememanagement.plist ├── demo ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── JSMatrixCodeRainView.swift ├── ViewController.swift └── matrixcode.ttf └── matrixcode.ttf /JSMatrixCodeRainView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSMatrixCodeRainView.swift 3 | // matrixCodeRain 4 | // 5 | // Created by Shuo Zhang on 2016/9/30. 6 | // Copyright © 2016年 Jon Showing. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import QuartzCore 12 | //import CoreMotion 13 | 14 | fileprivate class PingThread: Thread{ 15 | var pingTaskIsRunning = false 16 | var semaphore = DispatchSemaphore(value: 0) 17 | override func main(){ 18 | while !self.isCancelled{ 19 | pingTaskIsRunning = true 20 | DispatchQueue.main.async { 21 | self.pingTaskIsRunning = false 22 | self.semaphore.signal() 23 | } 24 | Thread.sleep(forTimeInterval: 1/30.0) 25 | if pingTaskIsRunning { 26 | NSLog("Delayed!") 27 | } 28 | _ = semaphore.wait(timeout: DispatchTime.distantFuture) 29 | } 30 | } 31 | } 32 | 33 | fileprivate struct JSMatrixConstants { 34 | static let maxGlowLength: Int = 3 // Characters 35 | static let minTrackLength: Int = 8 // Characters 36 | static let maxTrackLength: Int = 40 // Characters 37 | static let charactersSpacing: CGFloat = 0.0 // pixel 38 | static let characterChangeRate = 0.9 39 | static let firstDropShowTime = 2.0 // Time between the First drop and the later 40 | 41 | // Configurable 42 | static let speed: TimeInterval = 0.15 // Seconds that new character pop up 43 | static let newTrackComingLap: TimeInterval = 0.4 44 | static let tracksSpacing: Int = 5 45 | } 46 | 47 | fileprivate class JSMatrixTrack: Hashable, Equatable { 48 | var glowLength: Int 49 | var fadeLength: Int 50 | var totalLength: Int 51 | var positionY: Int{ 52 | didSet{ 53 | if positionY > totalLength { 54 | topY += (positionY - oldValue) 55 | } 56 | } 57 | } 58 | var trackNum: Int 59 | var positionX: CGFloat{ 60 | return CGFloat(trackNum) * (JSMatrixDataSource.characterSize.width + 2.0) 61 | } 62 | var hashValue: Int{ 63 | return trackNum 64 | } 65 | var topY: Int = 0 66 | weak var layer: JSMatrixTrackLayer? 67 | weak var datasource: JSMatrixDataSource? 68 | var timer: Timer? 69 | 70 | static func ==(lhs: JSMatrixTrack, rhs: JSMatrixTrack) -> Bool{ 71 | return lhs.trackNum == rhs.trackNum 72 | } 73 | 74 | func getBrightness(currentTopY: Int, currentBottomY: Int) -> CGFloat{ 75 | let index = currentBottomY - currentTopY - 1 76 | let rawData = 1 - CGFloat(index) / CGFloat(totalLength) // index = 0 is the brightest case 77 | if index < glowLength { 78 | return rawData * 1.2 79 | }else if index > totalLength - fadeLength{ 80 | return rawData * 0.4 81 | }else{ 82 | return rawData 83 | } 84 | } 85 | 86 | init(length: Int, trackNum _trackNum: Int) { 87 | self.positionY = 0 88 | self.totalLength = length 89 | self.trackNum = _trackNum 90 | 91 | glowLength = Int(arc4random_uniform(UInt32(min(JSMatrixConstants.maxGlowLength, length)))) 92 | let fadeLengthRange = length - glowLength 93 | fadeLength = Int(arc4random_uniform(UInt32(fadeLengthRange))) 94 | 95 | timer = Timer.scheduledTimer(timeInterval: JSMatrixDataSource.sharedDataSource.speed, target: self, selector: #selector(self.drop), userInfo: nil, repeats: true) 96 | } 97 | 98 | @objc func drop(){ 99 | self.positionY += 1 100 | if self.topY >= JSMatrixDataSource.maxNum{ 101 | self.layer?.removeFromSuperlayer() 102 | timer?.invalidate() 103 | }else{ 104 | self.layer?.drawAsync() 105 | } 106 | } 107 | } 108 | 109 | fileprivate class JSMatrixDataSource{ 110 | static let sharedDataSource: JSMatrixDataSource = JSMatrixDataSource() 111 | 112 | static let trackNum: Int = Int(ceilf(Float(UIScreen.main.bounds.width / JSMatrixDataSource.characterSize.width))) 113 | static let maxNum: Int = Int(ceilf(Float(UIScreen.main.bounds.height / JSMatrixDataSource.characterSize.height))) 114 | 115 | static let characterSet = "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890" 116 | static let characterSize = "T".size(attributes: JSMatrixDataSource.getBrightnessAttributes(brightness: 1.0)) 117 | static func getCharacter() -> String{ 118 | let randomNum = Int(arc4random_uniform(UInt32(characterSet.characters.count))) 119 | let randomIndex = characterSet.index(characterSet.startIndex, offsetBy: randomNum) 120 | return String(characterSet[randomIndex]) 121 | } 122 | static func getBrightnessAttributes(brightness: CGFloat) -> [String: Any]{ 123 | return [NSForegroundColorAttributeName: UIColor.init(hue: 127.0/360.0, saturation: 97.0/100.0, brightness: brightness, alpha: 1.0), 124 | NSFontAttributeName: UIFont(name: "Matrix Code NFI", size: 17)!, 125 | NSShadowAttributeName: { 126 | let shadow = NSShadow() 127 | shadow.shadowBlurRadius = 2.0 128 | shadow.shadowColor = UIColor.init(white: 1.0, alpha: brightness) 129 | shadow.shadowOffset = CGSize.zero 130 | return shadow 131 | }()] 132 | } 133 | 134 | var characters: [[String]] = [] 135 | var currentTracks: Set = Set() 136 | 137 | var speed: TimeInterval = JSMatrixConstants.speed 138 | var newTrackComingLap: TimeInterval = JSMatrixConstants.newTrackComingLap 139 | var trackSpacing: Int = JSMatrixConstants.tracksSpacing 140 | 141 | init() { 142 | for _ in 0.. [Int]{ 162 | var availiableTracks: [Int] = [Int](0...JSMatrixDataSource.trackNum) 163 | for track in currentTracks{ 164 | if track.topY > JSMatrixDataSource.sharedDataSource.trackSpacing{ // All the track have been shown(and the gap is enough), new ones is free to go from above 165 | continue 166 | }else{ 167 | availiableTracks = availiableTracks.filter({ (trackNum) -> Bool in 168 | if trackNum == track.trackNum{ 169 | return false 170 | } 171 | return true 172 | }) 173 | } 174 | } 175 | return availiableTracks 176 | } 177 | 178 | @objc func changeCharacter(){ 179 | for track in 0.. [Int] 191 | } 192 | fileprivate protocol JSMatrixTrackGeneratorDelegate: class { 193 | func didGeneratedNewTrack(newTrack: JSMatrixTrack) 194 | } 195 | 196 | fileprivate class JSMatrixTrackGenerator{ 197 | weak var delegate: JSMatrixTrackGeneratorDelegate? 198 | weak var datasource: JSMatrixTrackGeneratorDataSource? 199 | 200 | func getTrack() -> JSMatrixTrack{ 201 | if let availableTracks = datasource?.availiableTracks(){ 202 | let randomNum = Int(arc4random_uniform(UInt32(availableTracks.count - 1))) 203 | let track = JSMatrixTrack(length: Int(arc4random_uniform(UInt32(JSMatrixDataSource.maxNum - JSMatrixConstants.minTrackLength))) + JSMatrixConstants.minTrackLength, 204 | trackNum: availableTracks[randomNum]) 205 | return track 206 | }else{ 207 | return JSMatrixTrack(length: Int(arc4random_uniform(UInt32(JSMatrixDataSource.maxNum - JSMatrixConstants.minTrackLength))) + JSMatrixConstants.minTrackLength, 208 | trackNum: 0) 209 | } 210 | } 211 | 212 | func getTrack(trackNumber: Int) -> JSMatrixTrack { 213 | return JSMatrixTrack(length: Int(arc4random_uniform(UInt32(JSMatrixDataSource.maxNum - JSMatrixConstants.minTrackLength))) + JSMatrixConstants.minTrackLength, 214 | trackNum: trackNumber) 215 | } 216 | 217 | func begin(){ 218 | Timer.scheduledTimer(timeInterval: JSMatrixDataSource.sharedDataSource.newTrackComingLap, target: self, selector: #selector(self.produceTrack), userInfo: nil, repeats: true) 219 | } 220 | 221 | @objc func produceTrack(){ 222 | self.delegate?.didGeneratedNewTrack(newTrack: self.getTrack()) 223 | } 224 | } 225 | 226 | fileprivate class JSMatrixTrackLayer: CALayer { 227 | var track: JSMatrixTrack 228 | 229 | init(track _track: JSMatrixTrack) { 230 | track = _track 231 | 232 | super.init() 233 | 234 | self.contentsScale = UIScreen.main.scale 235 | 236 | self.drawsAsynchronously = true 237 | } 238 | 239 | required init?(coder aDecoder: NSCoder) { 240 | fatalError("init(coder:) has not been implemented") 241 | } 242 | 243 | func drawAsync() { 244 | 245 | let track = self.track 246 | DispatchQueue.global().async { 247 | let size = self.bounds.size 248 | UIGraphicsBeginImageContext(size) 249 | let colorSpace = CGColorSpaceCreateDeviceRGB() 250 | let createdContext = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) 251 | 252 | if let context = createdContext{ 253 | context.saveGState() 254 | 255 | let positionY: Int = track.positionY 256 | var topY: Int = track.topY 257 | 258 | if let col = track.datasource?.characters[track.trackNum]{ 259 | var range = 0.. track.totalLength { 261 | if positionY < JSMatrixDataSource.maxNum{ // track have fully shown 262 | range = topY.. [Int] { 387 | return datasource.getAvailiableTracks() 388 | } 389 | 390 | fileprivate func didGeneratedNewTrack(newTrack: JSMatrixTrack) { 391 | newTrack.datasource = datasource 392 | datasource.addTrack(track: newTrack) 393 | let layer = JSMatrixTrackLayer(track: newTrack) 394 | layer.frame = CGRect(origin: CGPoint(x: newTrack.positionX, y: 0), 395 | size: CGSize(width: JSMatrixDataSource.characterSize.width, height: UIScreen.main.bounds.height)) 396 | containerLayer.addSublayer(layer) 397 | layer.drawAsync() 398 | newTrack.layer = layer 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2016 Jon Showing 5 | ---------------------------------- 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | -------------------------------------------------------------------------------- /NFILicense.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zshowing/JSMatrixCodeRainView/4e811957cb81b164dc9018ec161533042ec5580c/NFILicense.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSMatrixCodeRainView 2 | 3 | **Code Rain**, or [Digital Rain](https://en.wikipedia.org/wiki/Matrix_digital_rain), is a famous visual effect from the Matrix movie trilogy. This view is a Swift implementation to it. 4 | 5 | ![](http://ww3.sinaimg.cn/large/5613ec79jw1f8hmevmxy9g20a00hsb29.gif) 6 | 7 | # Requirements 8 | 9 | JSMatrixCodeRainView works on iOS8.0+(Tested) with ARC projects. 10 | 11 | It uses the following frameworks: 12 | - Foundation.framework 13 | - UIKit.framework 14 | - QuartzCore.framework 15 | 16 | # Usage 17 | 18 | 1. Modify your Info.plist; 19 | 20 | The view used a customized font called 'Matrix Code NFI', to include that you need to add an entry, `Fonts provided by application` in the `Info.plist`. 21 | 22 | ![](http://ww4.sinaimg.cn/large/5613ec79jw1f8hm06k2djj212m0meth1.jpg) 23 | 24 | 2. Copy `JSMatrixCodeRainView.swift` and the font file to your project; 25 | 3. Configure if needed. There are three configurable variables now: 26 | - `speed`, a `CGFloat` value, the unit is in `second`, controls the speed of the animation. The value is the time interval of the characters coming from above. 27 | For example, the default value is 0.15, which means in every 0.15s, a new character will drop (if reasonable). 28 | - `newTrackComingLap`, also a `CGFloat` value, the unit is `second`, generally control the number of the tracks. 29 | Again as an example, the default value is 0.4, means that in every 0.4s, a new track will drop down from the available space (that is, there's no others occupying.) 30 | - `tracksSpacing`, an `Int` value, the unit is `character`, control the spacing of two tracks. 31 | The default value is 5, therefore after a track is completely shown, there will be no other track from the same line unless 5 characters passed. 32 | 33 | 34 | You can manually set it or set it via Interface Builder, as shown below: 35 | 36 | ![](http://ww1.sinaimg.cn/large/5613ec79jw1f8hq5majxfj20du050jrx.jpg) 37 | 38 | 4. That's all. Use it as any `UIView`s. 39 | 40 | # Special Thanks 41 | 42 | The font `Matrix Code NFI` is from Norfok® Inc. software. 43 | 44 | # License 45 | 46 | This code is distributed under the terms and conditions of the [MIT license](./LICENSE.md). 47 | Also check the [`Matrix Code NFI` license](./NFILicense.txt) if you're intended to commercial use. -------------------------------------------------------------------------------- /demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 54A511161DA401AE00BE5C34 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A511151DA401AE00BE5C34 /* AppDelegate.swift */; }; 11 | 54A511181DA401AE00BE5C34 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A511171DA401AE00BE5C34 /* ViewController.swift */; }; 12 | 54A5111B1DA401AE00BE5C34 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54A511191DA401AE00BE5C34 /* Main.storyboard */; }; 13 | 54A5111D1DA401AE00BE5C34 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54A5111C1DA401AE00BE5C34 /* Assets.xcassets */; }; 14 | 54A511201DA401AE00BE5C34 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54A5111E1DA401AE00BE5C34 /* LaunchScreen.storyboard */; }; 15 | 54A511281DA4021500BE5C34 /* JSMatrixCodeRainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A511271DA4021500BE5C34 /* JSMatrixCodeRainView.swift */; }; 16 | 54A5112A1DA4031500BE5C34 /* matrixcode.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 54A511291DA4031500BE5C34 /* matrixcode.ttf */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 54A511121DA401AE00BE5C34 /* demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 54A511151DA401AE00BE5C34 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | 54A511171DA401AE00BE5C34 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 23 | 54A5111A1DA401AE00BE5C34 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | 54A5111C1DA401AE00BE5C34 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | 54A5111F1DA401AE00BE5C34 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | 54A511211DA401AE00BE5C34 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 54A511271DA4021500BE5C34 /* JSMatrixCodeRainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSMatrixCodeRainView.swift; sourceTree = ""; }; 28 | 54A511291DA4031500BE5C34 /* matrixcode.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = matrixcode.ttf; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 54A5110F1DA401AE00BE5C34 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 54A511091DA401AE00BE5C34 = { 43 | isa = PBXGroup; 44 | children = ( 45 | 54A511141DA401AE00BE5C34 /* demo */, 46 | 54A511131DA401AE00BE5C34 /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | 54A511131DA401AE00BE5C34 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 54A511121DA401AE00BE5C34 /* demo.app */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | 54A511141DA401AE00BE5C34 /* demo */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 54A511291DA4031500BE5C34 /* matrixcode.ttf */, 62 | 54A511271DA4021500BE5C34 /* JSMatrixCodeRainView.swift */, 63 | 54A511151DA401AE00BE5C34 /* AppDelegate.swift */, 64 | 54A511171DA401AE00BE5C34 /* ViewController.swift */, 65 | 54A511191DA401AE00BE5C34 /* Main.storyboard */, 66 | 54A5111C1DA401AE00BE5C34 /* Assets.xcassets */, 67 | 54A5111E1DA401AE00BE5C34 /* LaunchScreen.storyboard */, 68 | 54A511211DA401AE00BE5C34 /* Info.plist */, 69 | ); 70 | path = demo; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | 54A511111DA401AE00BE5C34 /* demo */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = 54A511241DA401AE00BE5C34 /* Build configuration list for PBXNativeTarget "demo" */; 79 | buildPhases = ( 80 | 54A5110E1DA401AE00BE5C34 /* Sources */, 81 | 54A5110F1DA401AE00BE5C34 /* Frameworks */, 82 | 54A511101DA401AE00BE5C34 /* Resources */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = demo; 89 | productName = demo; 90 | productReference = 54A511121DA401AE00BE5C34 /* demo.app */; 91 | productType = "com.apple.product-type.application"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | 54A5110A1DA401AE00BE5C34 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastSwiftUpdateCheck = 0800; 100 | LastUpgradeCheck = 1120; 101 | ORGANIZATIONNAME = "Jon Showing"; 102 | TargetAttributes = { 103 | 54A511111DA401AE00BE5C34 = { 104 | CreatedOnToolsVersion = 8.0; 105 | DevelopmentTeam = TWSN29Y9H7; 106 | LastSwiftMigration = 1010; 107 | ProvisioningStyle = Automatic; 108 | }; 109 | }; 110 | }; 111 | buildConfigurationList = 54A5110D1DA401AE00BE5C34 /* Build configuration list for PBXProject "demo" */; 112 | compatibilityVersion = "Xcode 3.2"; 113 | developmentRegion = English; 114 | hasScannedForEncodings = 0; 115 | knownRegions = ( 116 | English, 117 | en, 118 | Base, 119 | ); 120 | mainGroup = 54A511091DA401AE00BE5C34; 121 | productRefGroup = 54A511131DA401AE00BE5C34 /* Products */; 122 | projectDirPath = ""; 123 | projectRoot = ""; 124 | targets = ( 125 | 54A511111DA401AE00BE5C34 /* demo */, 126 | ); 127 | }; 128 | /* End PBXProject section */ 129 | 130 | /* Begin PBXResourcesBuildPhase section */ 131 | 54A511101DA401AE00BE5C34 /* Resources */ = { 132 | isa = PBXResourcesBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | 54A511201DA401AE00BE5C34 /* LaunchScreen.storyboard in Resources */, 136 | 54A5111D1DA401AE00BE5C34 /* Assets.xcassets in Resources */, 137 | 54A5112A1DA4031500BE5C34 /* matrixcode.ttf in Resources */, 138 | 54A5111B1DA401AE00BE5C34 /* Main.storyboard in Resources */, 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | /* End PBXResourcesBuildPhase section */ 143 | 144 | /* Begin PBXSourcesBuildPhase section */ 145 | 54A5110E1DA401AE00BE5C34 /* Sources */ = { 146 | isa = PBXSourcesBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | 54A511181DA401AE00BE5C34 /* ViewController.swift in Sources */, 150 | 54A511161DA401AE00BE5C34 /* AppDelegate.swift in Sources */, 151 | 54A511281DA4021500BE5C34 /* JSMatrixCodeRainView.swift in Sources */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | /* End PBXSourcesBuildPhase section */ 156 | 157 | /* Begin PBXVariantGroup section */ 158 | 54A511191DA401AE00BE5C34 /* Main.storyboard */ = { 159 | isa = PBXVariantGroup; 160 | children = ( 161 | 54A5111A1DA401AE00BE5C34 /* Base */, 162 | ); 163 | name = Main.storyboard; 164 | sourceTree = ""; 165 | }; 166 | 54A5111E1DA401AE00BE5C34 /* LaunchScreen.storyboard */ = { 167 | isa = PBXVariantGroup; 168 | children = ( 169 | 54A5111F1DA401AE00BE5C34 /* Base */, 170 | ); 171 | name = LaunchScreen.storyboard; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXVariantGroup section */ 175 | 176 | /* Begin XCBuildConfiguration section */ 177 | 54A511221DA401AE00BE5C34 /* Debug */ = { 178 | isa = XCBuildConfiguration; 179 | buildSettings = { 180 | ALWAYS_SEARCH_USER_PATHS = NO; 181 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 182 | CLANG_ANALYZER_NONNULL = YES; 183 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 184 | CLANG_CXX_LIBRARY = "libc++"; 185 | CLANG_ENABLE_MODULES = YES; 186 | CLANG_ENABLE_OBJC_ARC = YES; 187 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_COMMA = YES; 190 | CLANG_WARN_CONSTANT_CONVERSION = YES; 191 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 192 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 193 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 194 | CLANG_WARN_EMPTY_BODY = YES; 195 | CLANG_WARN_ENUM_CONVERSION = YES; 196 | CLANG_WARN_INFINITE_RECURSION = YES; 197 | CLANG_WARN_INT_CONVERSION = YES; 198 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 199 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 200 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 201 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 202 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 203 | CLANG_WARN_STRICT_PROTOTYPES = YES; 204 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 205 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 206 | CLANG_WARN_UNREACHABLE_CODE = YES; 207 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 208 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 209 | COPY_PHASE_STRIP = NO; 210 | DEBUG_INFORMATION_FORMAT = dwarf; 211 | ENABLE_STRICT_OBJC_MSGSEND = YES; 212 | ENABLE_TESTABILITY = YES; 213 | GCC_C_LANGUAGE_STANDARD = gnu99; 214 | GCC_DYNAMIC_NO_PIC = NO; 215 | GCC_NO_COMMON_BLOCKS = YES; 216 | GCC_OPTIMIZATION_LEVEL = 0; 217 | GCC_PREPROCESSOR_DEFINITIONS = ( 218 | "DEBUG=1", 219 | "$(inherited)", 220 | ); 221 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 222 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 223 | GCC_WARN_UNDECLARED_SELECTOR = YES; 224 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 225 | GCC_WARN_UNUSED_FUNCTION = YES; 226 | GCC_WARN_UNUSED_VARIABLE = YES; 227 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 228 | MTL_ENABLE_DEBUG_INFO = YES; 229 | ONLY_ACTIVE_ARCH = YES; 230 | SDKROOT = iphoneos; 231 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 232 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 233 | TARGETED_DEVICE_FAMILY = "1,2"; 234 | }; 235 | name = Debug; 236 | }; 237 | 54A511231DA401AE00BE5C34 /* Release */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ALWAYS_SEARCH_USER_PATHS = NO; 241 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 242 | CLANG_ANALYZER_NONNULL = YES; 243 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 244 | CLANG_CXX_LIBRARY = "libc++"; 245 | CLANG_ENABLE_MODULES = YES; 246 | CLANG_ENABLE_OBJC_ARC = YES; 247 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 248 | CLANG_WARN_BOOL_CONVERSION = YES; 249 | CLANG_WARN_COMMA = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 252 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 253 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 254 | CLANG_WARN_EMPTY_BODY = YES; 255 | CLANG_WARN_ENUM_CONVERSION = YES; 256 | CLANG_WARN_INFINITE_RECURSION = YES; 257 | CLANG_WARN_INT_CONVERSION = YES; 258 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 259 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 260 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 261 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 262 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 263 | CLANG_WARN_STRICT_PROTOTYPES = YES; 264 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 265 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 266 | CLANG_WARN_UNREACHABLE_CODE = YES; 267 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 268 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 269 | COPY_PHASE_STRIP = NO; 270 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 271 | ENABLE_NS_ASSERTIONS = NO; 272 | ENABLE_STRICT_OBJC_MSGSEND = YES; 273 | GCC_C_LANGUAGE_STANDARD = gnu99; 274 | GCC_NO_COMMON_BLOCKS = YES; 275 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 276 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 277 | GCC_WARN_UNDECLARED_SELECTOR = YES; 278 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 279 | GCC_WARN_UNUSED_FUNCTION = YES; 280 | GCC_WARN_UNUSED_VARIABLE = YES; 281 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 282 | MTL_ENABLE_DEBUG_INFO = NO; 283 | SDKROOT = iphoneos; 284 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 285 | TARGETED_DEVICE_FAMILY = "1,2"; 286 | VALIDATE_PRODUCT = YES; 287 | }; 288 | name = Release; 289 | }; 290 | 54A511251DA401AE00BE5C34 /* Debug */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 294 | CODE_SIGN_IDENTITY = "Apple Development"; 295 | CODE_SIGN_STYLE = Automatic; 296 | DEVELOPMENT_TEAM = TWSN29Y9H7; 297 | INFOPLIST_FILE = demo/Info.plist; 298 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 299 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 300 | PRODUCT_BUNDLE_IDENTIFIER = com.qiyu.demo; 301 | PRODUCT_NAME = "$(TARGET_NAME)"; 302 | PROVISIONING_PROFILE_SPECIFIER = ""; 303 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 304 | SWIFT_VERSION = 4.2; 305 | }; 306 | name = Debug; 307 | }; 308 | 54A511261DA401AE00BE5C34 /* Release */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 312 | CODE_SIGN_IDENTITY = "Apple Development"; 313 | CODE_SIGN_STYLE = Automatic; 314 | DEVELOPMENT_TEAM = TWSN29Y9H7; 315 | INFOPLIST_FILE = demo/Info.plist; 316 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 317 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 318 | PRODUCT_BUNDLE_IDENTIFIER = com.qiyu.demo; 319 | PRODUCT_NAME = "$(TARGET_NAME)"; 320 | PROVISIONING_PROFILE_SPECIFIER = ""; 321 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 322 | SWIFT_VERSION = 4.2; 323 | }; 324 | name = Release; 325 | }; 326 | /* End XCBuildConfiguration section */ 327 | 328 | /* Begin XCConfigurationList section */ 329 | 54A5110D1DA401AE00BE5C34 /* Build configuration list for PBXProject "demo" */ = { 330 | isa = XCConfigurationList; 331 | buildConfigurations = ( 332 | 54A511221DA401AE00BE5C34 /* Debug */, 333 | 54A511231DA401AE00BE5C34 /* Release */, 334 | ); 335 | defaultConfigurationIsVisible = 0; 336 | defaultConfigurationName = Release; 337 | }; 338 | 54A511241DA401AE00BE5C34 /* Build configuration list for PBXNativeTarget "demo" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | 54A511251DA401AE00BE5C34 /* Debug */, 342 | 54A511261DA401AE00BE5C34 /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | /* End XCConfigurationList section */ 348 | }; 349 | rootObject = 54A5110A1DA401AE00BE5C34 /* Project object */; 350 | } 351 | -------------------------------------------------------------------------------- /demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo.xcodeproj/project.xcworkspace/xcuserdata/showing.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zshowing/JSMatrixCodeRainView/4e811957cb81b164dc9018ec161533042ec5580c/demo.xcodeproj/project.xcworkspace/xcuserdata/showing.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /demo.xcodeproj/xcuserdata/showing.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 14 | 15 | 16 | 18 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /demo.xcodeproj/xcuserdata/showing.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | demo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo.xcodeproj/xcuserdata/shuozhang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /demo.xcodeproj/xcuserdata/shuozhang.xcuserdatad/xcschemes/demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /demo.xcodeproj/xcuserdata/shuozhang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | demo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 54A511111DA401AE00BE5C34 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // demo 4 | // 5 | // Created by Shuo Zhang on 2016/10/4. 6 | // Copyright © 2016年 Jon Showing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /demo/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 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /demo/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 | 60 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /demo/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIAppFonts 24 | 25 | matrixcode.ttf 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /demo/JSMatrixCodeRainView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSMatrixCodeRainView.swift 3 | // matrixCodeRain 4 | // 5 | // Created by Shuo Zhang on 2016/9/30. 6 | // Copyright © 2016年 Jon Showing. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import QuartzCore 12 | 13 | fileprivate struct JSMatrixConstants { 14 | static let maxGlowLength: Int = 3 // Characters 15 | static let minTrackLength: Int = 8 // Characters 16 | static let maxTrackLength: Int = 40 // Characters 17 | static let charactersSpacing: CGFloat = 0.0 // pixel 18 | static let characterChangeRate = 0.9 19 | static let firstDropShowTime = 1.0 // Time between the First drop and the later 20 | 21 | // Configurable 22 | static let speed: TimeInterval = 0.1 // Seconds that new character pop up 23 | static let newTrackComingLap: TimeInterval = 3 24 | static let tracksSpacing: Int = 5 25 | } 26 | 27 | fileprivate class JSMatrixTrack { 28 | var glowLength: Int = 0 29 | var fadeLength: Int = 0 30 | var totalLength: Int{ 31 | didSet{ 32 | self.glowLength = Int(arc4random_uniform(UInt32(min(JSMatrixConstants.maxGlowLength, totalLength)))) 33 | let fadeLengthRange = totalLength - glowLength 34 | self.fadeLength = Int(arc4random_uniform(UInt32(fadeLengthRange))) 35 | } 36 | } 37 | var layer: JSMatrixTrackLayer 38 | var characters: [String] 39 | 40 | func setupPulse(length: Int) { 41 | self.layer.setupPulse(length: self.totalLength, fadeLength: self.fadeLength) 42 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(JSMatrixDataSource.maxNum + length) * JSMatrixDataSource.sharedDataSource.speed + Double(arc4random_uniform(UInt32(JSMatrixConstants.newTrackComingLap))) , execute: { 43 | self.totalLength = Int(arc4random_uniform(UInt32(JSMatrixDataSource.maxNum - JSMatrixConstants.minTrackLength))) + JSMatrixConstants.minTrackLength 44 | self.setupPulse(length: self.totalLength) 45 | }) 46 | } 47 | 48 | @objc func changeCharacter(){ 49 | if arc4random_uniform(10) < UInt32(JSMatrixConstants.characterChangeRate * 10){ 50 | let randomIndex = Int(arc4random_uniform(UInt32(self.characters.count))) 51 | self.layer.updateCharacter(JSMatrixDataSource.getCharacter(), atIndex: randomIndex) 52 | } 53 | } 54 | 55 | init(layer _layer: JSMatrixTrackLayer, length _length: Int, characters _characters: [String]) { 56 | self.layer = _layer 57 | self.characters = _characters 58 | self.totalLength = _length 59 | 60 | Timer.scheduledTimer(timeInterval: 0.5, target: self, 61 | selector: #selector(self.changeCharacter), 62 | userInfo: nil, repeats: true) 63 | 64 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + JSMatrixConstants.firstDropShowTime + Double(arc4random_uniform(UInt32(JSMatrixDataSource.trackNum))) + Double(arc4random_uniform(UInt32(JSMatrixDataSource.sharedDataSource.newTrackComingLap)))) { 65 | self.setupPulse(length: self.totalLength) 66 | } 67 | } 68 | } 69 | 70 | fileprivate class JSMatrixDataSource{ 71 | static let sharedDataSource: JSMatrixDataSource = JSMatrixDataSource() 72 | 73 | /// Max track number 74 | static let trackNum: Int = Int(ceilf(Float(UIScreen.main.bounds.width / JSMatrixDataSource.characterSize.width))) 75 | 76 | /// Max character number vertically 77 | static let maxNum: Int = Int(ceilf(Float(UIScreen.main.bounds.height / JSMatrixDataSource.characterSize.height))) 78 | 79 | /// Character set. 80 | private static let characterSet = "abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890" 81 | 82 | /// Character size. 83 | static let characterSize = "T".size(withAttributes: JSMatrixDataSource.getBrightnessAttributes(brightness: 1.0)) 84 | 85 | /// Get One character. 86 | static func getCharacter() -> String{ 87 | let randomNum = Int(arc4random_uniform(UInt32(characterSet.count))) 88 | let randomIndex = characterSet.index(characterSet.startIndex, offsetBy: randomNum) 89 | return String(characterSet[randomIndex]) 90 | } 91 | 92 | /// Get characters for the given length. 93 | static func getCharacter(length: Int) -> [String]{ 94 | var charArray: [String] = [] 95 | for _ in 0.. [NSAttributedString.Key: Any]{ 103 | return [NSAttributedString.Key.foregroundColor: UIColor.init(hue: 127.0/360.0, saturation: 97.0/100.0, brightness: brightness, alpha: 1.0), 104 | NSAttributedString.Key.font: UIFont(name: "Matrix Code NFI", size: 17)!, 105 | NSAttributedString.Key.shadow: { 106 | let shadow = NSShadow() 107 | shadow.shadowBlurRadius = 2.0 108 | shadow.shadowColor = UIColor.init(white: 1.0, alpha: brightness) 109 | shadow.shadowOffset = CGSize.zero 110 | return shadow 111 | }()] 112 | } 113 | 114 | var characters: [[String]] = [] 115 | var tracks: [JSMatrixTrack] = [] 116 | 117 | var speed: TimeInterval = JSMatrixConstants.speed 118 | var newTrackComingLap: TimeInterval = JSMatrixConstants.newTrackComingLap 119 | var trackSpacing: Int = JSMatrixConstants.tracksSpacing 120 | 121 | func addTrack(track: JSMatrixTrack) { 122 | tracks.append(track) 123 | } 124 | 125 | init() { 126 | for _ in 0..<(JSMatrixDataSource.trackNum + 1){ 127 | var track: [String] = [] 128 | for _ in 0.. = [] 153 | 154 | required init(trackNum _trackNum: Int) { 155 | self.trackNum = _trackNum 156 | 157 | super.init() 158 | 159 | self.frame = CGRect(origin: CGPoint(x: CGFloat(_trackNum) * (JSMatrixDataSource.characterSize.width + 2.0), y: 0), 160 | size: CGSize(width: JSMatrixDataSource.characterSize.width, height: UIScreen.main.bounds.height)) 161 | } 162 | 163 | required init?(coder aDecoder: NSCoder) { 164 | fatalError("init(coder:) has not been implemented") 165 | } 166 | 167 | func setupCharacters(characters: [String]) { 168 | for i in 0.. duration = length * speed 190 | for (index, layer) in layers.enumerated() { 191 | DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * JSMatrixDataSource.sharedDataSource.speed) { 192 | let animation = CABasicAnimation(keyPath: "opacity") 193 | animation.duration = Double(length) * JSMatrixDataSource.sharedDataSource.speed 194 | animation.fillMode = CAMediaTimingFillMode.forwards 195 | animation.fromValue = 1 196 | animation.toValue = 0 197 | animation.beginTime = CACurrentMediaTime() 198 | animation.isRemovedOnCompletion = false 199 | layer.add(animation, forKey: "opacity") 200 | } 201 | } 202 | } 203 | } 204 | 205 | class JSMatrixCodeRainView: UIView { 206 | fileprivate lazy var datasource: JSMatrixDataSource = JSMatrixDataSource.sharedDataSource 207 | 208 | fileprivate lazy var containerLayer: CALayer = { 209 | $0.frame = self.bounds//.insetBy(dx: -200, dy: -200) 210 | return $0 211 | }(CALayer()) 212 | 213 | @IBInspectable var speed: CGFloat = CGFloat(JSMatrixConstants.speed){ 214 | didSet{ 215 | datasource.speed = TimeInterval(speed) 216 | } 217 | } 218 | @IBInspectable var newTrackComingLap: CGFloat = CGFloat(JSMatrixConstants.newTrackComingLap){ 219 | didSet{ 220 | datasource.newTrackComingLap = TimeInterval(newTrackComingLap) 221 | } 222 | } 223 | @IBInspectable var trackSpacing: Int = JSMatrixConstants.tracksSpacing{ 224 | didSet{ 225 | datasource.trackSpacing = trackSpacing 226 | } 227 | } 228 | 229 | override init(frame: CGRect) { 230 | super.init(frame: frame) 231 | 232 | self.backgroundColor = UIColor.black 233 | self.layer.addSublayer(containerLayer) 234 | self.fillTracks() 235 | } 236 | 237 | required init?(coder aDecoder: NSCoder) { 238 | super.init(coder: aDecoder) 239 | 240 | self.backgroundColor = UIColor.black 241 | self.layer.addSublayer(containerLayer) 242 | self.fillTracks() 243 | } 244 | 245 | func fillTracks() { 246 | for i in 0..