├── .gitignore ├── Fireworks.podspec ├── LICENSE.md ├── README.md ├── classic.gif ├── fireworks.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── tomkowz.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── tomkowz.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── fireworks ├── Custom Fireworks │ ├── Duck Fireworks │ │ ├── DuckFountainFirework.swift │ │ ├── DuckFountainFireworkController.swift │ │ └── DuckSparkViewFactory.swift │ └── Pusheen Fireworks │ │ ├── PusheenFountainFirework.swift │ │ ├── PusheenFountainFireworkController.swift │ │ └── PusheenSparkViewFactory.swift ├── Demo App │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── duck.imageset │ │ │ ├── Contents.json │ │ │ └── duck.png │ │ ├── pusheen │ │ │ ├── Contents.json │ │ │ ├── pusheen-1.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pusheen-1.png │ │ │ ├── pusheen-2.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pusheen-2.png │ │ │ ├── pusheen-3.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pusheen-3.png │ │ │ ├── pusheen-4.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pusheen-4.png │ │ │ ├── pusheen-5.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pusheen-5.png │ │ │ ├── pusheen-6.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pusheen-6.png │ │ │ └── pusheen-7.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pusheen-7.png │ │ └── star.imageset │ │ │ ├── Contents.json │ │ │ └── star.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── ClassicFireworkDemoViewController.swift │ └── FountainFireworkDemoViewController.swift ├── Fireworks │ ├── Classic Firework │ │ ├── ClassicFirework.swift │ │ ├── ClassicFireworkAnimator.swift │ │ ├── ClassicFireworkController.swift │ │ ├── ClassicSparkTrajectoryFactory.swift │ │ └── ClassicSparkTrajectoryFactoryProtocol.swift │ ├── Fountain Firework │ │ ├── FountainFirework.swift │ │ ├── FountainFireworkAnimator.swift │ │ ├── FountainFireworkController.swift │ │ └── FountainSparkTrajectoryFactory.swift │ ├── Model │ │ ├── Firework.swift │ │ ├── FireworkSpark.swift │ │ ├── FireworkSparkScheduler.swift │ │ ├── SparkTrajectory.swift │ │ ├── SparkTrajectoryFactory.swift │ │ ├── SparkView.swift │ │ ├── SparkViewAnimator.swift │ │ └── SparkViewFactory.swift │ ├── Spark Views │ │ ├── CircleColorSparkView.swift │ │ ├── CircleColorSparkViewFactory.swift │ │ ├── ImageSparkView.swift │ │ └── ImageSparkViewFactory.swift │ └── System Extensions │ │ └── CGPoint+Extensions.swift └── Info.plist └── fountain.gif /.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 | xcshareddata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | *.orig 27 | .DS_Store 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | -------------------------------------------------------------------------------- /Fireworks.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Fireworks' 3 | s.version = '1.0.0' 4 | s.summary = 'A framework for generating firework-like particle effects.' 5 | s.author = { 'Tomasz Szulc' => 'mail@szulctomasz.com' } 6 | s.homepage = 'http://szulctomasz.com' 7 | s.license = { :type => 'MIT' } 8 | s.swift_version = '4.0' 9 | s.source = { :git => 'https://github.com/tomkowz/fireworks.git', :tag => '1.0.0' } 10 | s.source_files = 'fireworks/Fireworks/**/*.swift' 11 | s.ios.deployment_target = '11.0' 12 | end 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Tomasz Szulc 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fireworks 2 | 3 | Effect you can add to a `UIView`. 4 | 5 | More in [article on szulctomasz.com](http://szulctomasz.com/programming-blog/2018/09/add-fireworks-and-sparks-to-a-uiview/). 6 | 7 | # Demo 8 | 9 | ![Classic Firework](https://raw.githubusercontent.com/tomkowz/Fireworks/master/classic.gif) 10 | 11 | ![Fountain Firework](https://raw.githubusercontent.com/tomkowz/Fireworks/master/fountain.gif) 12 | 13 | # others 14 | 15 | Object-C version : [SparkDemo](https://github.com/wangyingbo/SparkDemo) 16 | -------------------------------------------------------------------------------- /classic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/classic.gif -------------------------------------------------------------------------------- /fireworks.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6D0933E12151215400190290 /* SparkTrajectory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D0933E02151215400190290 /* SparkTrajectory.swift */; }; 11 | 6D47CE2E2152331E000B3639 /* Firework.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE2D2152331E000B3639 /* Firework.swift */; }; 12 | 6D47CE3021523331000B3639 /* ClassicFirework.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE2F21523331000B3639 /* ClassicFirework.swift */; }; 13 | 6D47CE3221523C9B000B3639 /* ClassicFireworkAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE3121523C9B000B3639 /* ClassicFireworkAnimator.swift */; }; 14 | 6D47CE3421523CFF000B3639 /* SparkViewAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE3321523CFF000B3639 /* SparkViewAnimator.swift */; }; 15 | 6D47CE39215240ED000B3639 /* SparkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE38215240ED000B3639 /* SparkView.swift */; }; 16 | 6D47CE3B215242A6000B3639 /* FountainFireworkAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE3A215242A6000B3639 /* FountainFireworkAnimator.swift */; }; 17 | 6D47CE3D215242E7000B3639 /* FountainFirework.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE3C215242E7000B3639 /* FountainFirework.swift */; }; 18 | 6D47CE3F215243AC000B3639 /* FountainFireworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE3E215243AC000B3639 /* FountainFireworkController.swift */; }; 19 | 6D47CE4121524A40000B3639 /* FireworkSparkScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE4021524A40000B3639 /* FireworkSparkScheduler.swift */; }; 20 | 6D47CE4F21525EFA000B3639 /* CGPoint+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE4E21525EFA000B3639 /* CGPoint+Extensions.swift */; }; 21 | 6D47CE5221525F3A000B3639 /* FireworkSpark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE5121525F3A000B3639 /* FireworkSpark.swift */; }; 22 | 6D47CE582152744B000B3639 /* ClassicSparkTrajectoryFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE572152744B000B3639 /* ClassicSparkTrajectoryFactoryProtocol.swift */; }; 23 | 6D47CE5A2152751A000B3639 /* SparkTrajectoryFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D47CE592152751A000B3639 /* SparkTrajectoryFactory.swift */; }; 24 | 6D4EBAA8215223C500A2B836 /* ClassicFireworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EBAA7215223C500A2B836 /* ClassicFireworkController.swift */; }; 25 | 6D4EBAAA215223D900A2B836 /* CircleColorSparkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EBAA9215223D900A2B836 /* CircleColorSparkView.swift */; }; 26 | 6DA9633C214FF79E000F51AE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DA9633B214FF79E000F51AE /* AppDelegate.swift */; }; 27 | 6DA96341214FF79E000F51AE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6DA9633F214FF79E000F51AE /* Main.storyboard */; }; 28 | 6DA96343214FF79F000F51AE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6DA96342214FF79F000F51AE /* Assets.xcassets */; }; 29 | 6DA96346214FF79F000F51AE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6DA96344214FF79F000F51AE /* LaunchScreen.storyboard */; }; 30 | 6DA963512150242D000F51AE /* ClassicSparkTrajectoryFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DA963502150242D000F51AE /* ClassicSparkTrajectoryFactory.swift */; }; 31 | 6DB1AD3F2152B3A300169C39 /* FountainFireworkDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DB1AD3E2152B3A300169C39 /* FountainFireworkDemoViewController.swift */; }; 32 | 6DF25D5421527DA800AD30F7 /* SparkViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D5321527DA800AD30F7 /* SparkViewFactory.swift */; }; 33 | 6DF25D5621527E2900AD30F7 /* CircleColorSparkViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D5521527E2900AD30F7 /* CircleColorSparkViewFactory.swift */; }; 34 | 6DF25D5921528CEE00AD30F7 /* FountainSparkTrajectoryFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D5821528CEE00AD30F7 /* FountainSparkTrajectoryFactory.swift */; }; 35 | 6DF25D5B2152932B00AD30F7 /* ImageSparkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D5A2152932B00AD30F7 /* ImageSparkView.swift */; }; 36 | 6DF25D5D2152939300AD30F7 /* ImageSparkViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D5C2152939300AD30F7 /* ImageSparkViewFactory.swift */; }; 37 | 6DF25D632152987700AD30F7 /* DuckSparkViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D622152987700AD30F7 /* DuckSparkViewFactory.swift */; }; 38 | 6DF25D65215298C100AD30F7 /* DuckFountainFirework.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D64215298C100AD30F7 /* DuckFountainFirework.swift */; }; 39 | 6DF25D67215298F800AD30F7 /* DuckFountainFireworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D66215298F800AD30F7 /* DuckFountainFireworkController.swift */; }; 40 | 6DF25D6A21529B3100AD30F7 /* PusheenSparkViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D6921529B3100AD30F7 /* PusheenSparkViewFactory.swift */; }; 41 | 6DF25D6C21529C5E00AD30F7 /* PusheenFountainFirework.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D6B21529C5E00AD30F7 /* PusheenFountainFirework.swift */; }; 42 | 6DF25D6E21529C8400AD30F7 /* PusheenFountainFireworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D6D21529C8400AD30F7 /* PusheenFountainFireworkController.swift */; }; 43 | 6DF25D732152A99500AD30F7 /* ClassicFireworkDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF25D722152A99500AD30F7 /* ClassicFireworkDemoViewController.swift */; }; 44 | /* End PBXBuildFile section */ 45 | 46 | /* Begin PBXFileReference section */ 47 | 6D0933E02151215400190290 /* SparkTrajectory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SparkTrajectory.swift; sourceTree = ""; }; 48 | 6D47CE2D2152331E000B3639 /* Firework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firework.swift; sourceTree = ""; }; 49 | 6D47CE2F21523331000B3639 /* ClassicFirework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicFirework.swift; sourceTree = ""; }; 50 | 6D47CE3121523C9B000B3639 /* ClassicFireworkAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicFireworkAnimator.swift; sourceTree = ""; }; 51 | 6D47CE3321523CFF000B3639 /* SparkViewAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SparkViewAnimator.swift; sourceTree = ""; }; 52 | 6D47CE38215240ED000B3639 /* SparkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SparkView.swift; sourceTree = ""; }; 53 | 6D47CE3A215242A6000B3639 /* FountainFireworkAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FountainFireworkAnimator.swift; sourceTree = ""; }; 54 | 6D47CE3C215242E7000B3639 /* FountainFirework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FountainFirework.swift; sourceTree = ""; }; 55 | 6D47CE3E215243AC000B3639 /* FountainFireworkController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FountainFireworkController.swift; sourceTree = ""; }; 56 | 6D47CE4021524A40000B3639 /* FireworkSparkScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireworkSparkScheduler.swift; sourceTree = ""; }; 57 | 6D47CE4E21525EFA000B3639 /* CGPoint+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGPoint+Extensions.swift"; sourceTree = ""; }; 58 | 6D47CE5121525F3A000B3639 /* FireworkSpark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireworkSpark.swift; sourceTree = ""; }; 59 | 6D47CE572152744B000B3639 /* ClassicSparkTrajectoryFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicSparkTrajectoryFactoryProtocol.swift; sourceTree = ""; }; 60 | 6D47CE592152751A000B3639 /* SparkTrajectoryFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SparkTrajectoryFactory.swift; sourceTree = ""; }; 61 | 6D4EBAA7215223C500A2B836 /* ClassicFireworkController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicFireworkController.swift; sourceTree = ""; }; 62 | 6D4EBAA9215223D900A2B836 /* CircleColorSparkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleColorSparkView.swift; sourceTree = ""; }; 63 | 6DA96338214FF79E000F51AE /* fireworks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = fireworks.app; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 6DA9633B214FF79E000F51AE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 65 | 6DA96340214FF79E000F51AE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 66 | 6DA96342214FF79F000F51AE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 67 | 6DA96345214FF79F000F51AE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 68 | 6DA96347214FF79F000F51AE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | 6DA963502150242D000F51AE /* ClassicSparkTrajectoryFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicSparkTrajectoryFactory.swift; sourceTree = ""; }; 70 | 6DB1AD3E2152B3A300169C39 /* FountainFireworkDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FountainFireworkDemoViewController.swift; sourceTree = ""; }; 71 | 6DF25D5321527DA800AD30F7 /* SparkViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SparkViewFactory.swift; sourceTree = ""; }; 72 | 6DF25D5521527E2900AD30F7 /* CircleColorSparkViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleColorSparkViewFactory.swift; sourceTree = ""; }; 73 | 6DF25D5821528CEE00AD30F7 /* FountainSparkTrajectoryFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FountainSparkTrajectoryFactory.swift; sourceTree = ""; }; 74 | 6DF25D5A2152932B00AD30F7 /* ImageSparkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSparkView.swift; sourceTree = ""; }; 75 | 6DF25D5C2152939300AD30F7 /* ImageSparkViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSparkViewFactory.swift; sourceTree = ""; }; 76 | 6DF25D622152987700AD30F7 /* DuckSparkViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckSparkViewFactory.swift; sourceTree = ""; }; 77 | 6DF25D64215298C100AD30F7 /* DuckFountainFirework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckFountainFirework.swift; sourceTree = ""; }; 78 | 6DF25D66215298F800AD30F7 /* DuckFountainFireworkController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckFountainFireworkController.swift; sourceTree = ""; }; 79 | 6DF25D6921529B3100AD30F7 /* PusheenSparkViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PusheenSparkViewFactory.swift; sourceTree = ""; }; 80 | 6DF25D6B21529C5E00AD30F7 /* PusheenFountainFirework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PusheenFountainFirework.swift; sourceTree = ""; }; 81 | 6DF25D6D21529C8400AD30F7 /* PusheenFountainFireworkController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PusheenFountainFireworkController.swift; sourceTree = ""; }; 82 | 6DF25D722152A99500AD30F7 /* ClassicFireworkDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicFireworkDemoViewController.swift; sourceTree = ""; }; 83 | /* End PBXFileReference section */ 84 | 85 | /* Begin PBXFrameworksBuildPhase section */ 86 | 6DA96335214FF79E000F51AE /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | /* End PBXFrameworksBuildPhase section */ 94 | 95 | /* Begin PBXGroup section */ 96 | 6D47CE4D21525EDA000B3639 /* Model */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 6D47CE2D2152331E000B3639 /* Firework.swift */, 100 | 6D47CE5121525F3A000B3639 /* FireworkSpark.swift */, 101 | 6D47CE4021524A40000B3639 /* FireworkSparkScheduler.swift */, 102 | 6D0933E02151215400190290 /* SparkTrajectory.swift */, 103 | 6D47CE592152751A000B3639 /* SparkTrajectoryFactory.swift */, 104 | 6D47CE38215240ED000B3639 /* SparkView.swift */, 105 | 6D47CE3321523CFF000B3639 /* SparkViewAnimator.swift */, 106 | 6DF25D5321527DA800AD30F7 /* SparkViewFactory.swift */, 107 | ); 108 | path = Model; 109 | sourceTree = ""; 110 | }; 111 | 6D47CE5021525F1A000B3639 /* System Extensions */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 6D47CE4E21525EFA000B3639 /* CGPoint+Extensions.swift */, 115 | ); 116 | path = "System Extensions"; 117 | sourceTree = ""; 118 | }; 119 | 6D47CE532152630E000B3639 /* Classic Firework */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 6D47CE2F21523331000B3639 /* ClassicFirework.swift */, 123 | 6D47CE3121523C9B000B3639 /* ClassicFireworkAnimator.swift */, 124 | 6D4EBAA7215223C500A2B836 /* ClassicFireworkController.swift */, 125 | 6DA963502150242D000F51AE /* ClassicSparkTrajectoryFactory.swift */, 126 | 6D47CE572152744B000B3639 /* ClassicSparkTrajectoryFactoryProtocol.swift */, 127 | ); 128 | path = "Classic Firework"; 129 | sourceTree = ""; 130 | }; 131 | 6D47CE5621526B3D000B3639 /* Spark Views */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 6D4EBAA9215223D900A2B836 /* CircleColorSparkView.swift */, 135 | 6DF25D5521527E2900AD30F7 /* CircleColorSparkViewFactory.swift */, 136 | 6DF25D5A2152932B00AD30F7 /* ImageSparkView.swift */, 137 | 6DF25D5C2152939300AD30F7 /* ImageSparkViewFactory.swift */, 138 | ); 139 | path = "Spark Views"; 140 | sourceTree = ""; 141 | }; 142 | 6DA9632F214FF79E000F51AE = { 143 | isa = PBXGroup; 144 | children = ( 145 | 6DA9633A214FF79E000F51AE /* fireworks */, 146 | 6DA96339214FF79E000F51AE /* Products */, 147 | ); 148 | sourceTree = ""; 149 | }; 150 | 6DA96339214FF79E000F51AE /* Products */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 6DA96338214FF79E000F51AE /* fireworks.app */, 154 | ); 155 | name = Products; 156 | sourceTree = ""; 157 | }; 158 | 6DA9633A214FF79E000F51AE /* fireworks */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 6DF25D6F2152A64500AD30F7 /* Demo App */, 162 | 6DF25D60215297F200AD30F7 /* Custom Fireworks */, 163 | 6DA9634D214FF9B4000F51AE /* Fireworks */, 164 | 6DA96347214FF79F000F51AE /* Info.plist */, 165 | ); 166 | path = fireworks; 167 | sourceTree = ""; 168 | }; 169 | 6DA9634D214FF9B4000F51AE /* Fireworks */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 6D47CE5621526B3D000B3639 /* Spark Views */, 173 | 6D47CE5021525F1A000B3639 /* System Extensions */, 174 | 6D47CE4D21525EDA000B3639 /* Model */, 175 | 6D47CE532152630E000B3639 /* Classic Firework */, 176 | 6DF25D5721528CD100AD30F7 /* Fountain Firework */, 177 | ); 178 | path = Fireworks; 179 | sourceTree = ""; 180 | }; 181 | 6DF25D5721528CD100AD30F7 /* Fountain Firework */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 6D47CE3C215242E7000B3639 /* FountainFirework.swift */, 185 | 6D47CE3A215242A6000B3639 /* FountainFireworkAnimator.swift */, 186 | 6D47CE3E215243AC000B3639 /* FountainFireworkController.swift */, 187 | 6DF25D5821528CEE00AD30F7 /* FountainSparkTrajectoryFactory.swift */, 188 | ); 189 | path = "Fountain Firework"; 190 | sourceTree = ""; 191 | }; 192 | 6DF25D60215297F200AD30F7 /* Custom Fireworks */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 6DF25D6821529A4600AD30F7 /* Pusheen Fireworks */, 196 | 6DF25D61215297FB00AD30F7 /* Duck Fireworks */, 197 | ); 198 | path = "Custom Fireworks"; 199 | sourceTree = ""; 200 | }; 201 | 6DF25D61215297FB00AD30F7 /* Duck Fireworks */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 6DF25D622152987700AD30F7 /* DuckSparkViewFactory.swift */, 205 | 6DF25D64215298C100AD30F7 /* DuckFountainFirework.swift */, 206 | 6DF25D66215298F800AD30F7 /* DuckFountainFireworkController.swift */, 207 | ); 208 | path = "Duck Fireworks"; 209 | sourceTree = ""; 210 | }; 211 | 6DF25D6821529A4600AD30F7 /* Pusheen Fireworks */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 6DF25D6921529B3100AD30F7 /* PusheenSparkViewFactory.swift */, 215 | 6DF25D6B21529C5E00AD30F7 /* PusheenFountainFirework.swift */, 216 | 6DF25D6D21529C8400AD30F7 /* PusheenFountainFireworkController.swift */, 217 | ); 218 | path = "Pusheen Fireworks"; 219 | sourceTree = ""; 220 | }; 221 | 6DF25D6F2152A64500AD30F7 /* Demo App */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | 6DA9633B214FF79E000F51AE /* AppDelegate.swift */, 225 | 6DF25D722152A99500AD30F7 /* ClassicFireworkDemoViewController.swift */, 226 | 6DB1AD3E2152B3A300169C39 /* FountainFireworkDemoViewController.swift */, 227 | 6DA9633F214FF79E000F51AE /* Main.storyboard */, 228 | 6DA96344214FF79F000F51AE /* LaunchScreen.storyboard */, 229 | 6DA96342214FF79F000F51AE /* Assets.xcassets */, 230 | ); 231 | path = "Demo App"; 232 | sourceTree = ""; 233 | }; 234 | /* End PBXGroup section */ 235 | 236 | /* Begin PBXNativeTarget section */ 237 | 6DA96337214FF79E000F51AE /* fireworks */ = { 238 | isa = PBXNativeTarget; 239 | buildConfigurationList = 6DA9634A214FF79F000F51AE /* Build configuration list for PBXNativeTarget "fireworks" */; 240 | buildPhases = ( 241 | 6DA96334214FF79E000F51AE /* Sources */, 242 | 6DA96335214FF79E000F51AE /* Frameworks */, 243 | 6DA96336214FF79E000F51AE /* Resources */, 244 | ); 245 | buildRules = ( 246 | ); 247 | dependencies = ( 248 | ); 249 | name = fireworks; 250 | productName = fireworks; 251 | productReference = 6DA96338214FF79E000F51AE /* fireworks.app */; 252 | productType = "com.apple.product-type.application"; 253 | }; 254 | /* End PBXNativeTarget section */ 255 | 256 | /* Begin PBXProject section */ 257 | 6DA96330214FF79E000F51AE /* Project object */ = { 258 | isa = PBXProject; 259 | attributes = { 260 | LastSwiftUpdateCheck = 0940; 261 | LastUpgradeCheck = 0940; 262 | ORGANIZATIONNAME = "Tomasz Szulc"; 263 | TargetAttributes = { 264 | 6DA96337214FF79E000F51AE = { 265 | CreatedOnToolsVersion = 9.4.1; 266 | }; 267 | }; 268 | }; 269 | buildConfigurationList = 6DA96333214FF79E000F51AE /* Build configuration list for PBXProject "fireworks" */; 270 | compatibilityVersion = "Xcode 9.3"; 271 | developmentRegion = en; 272 | hasScannedForEncodings = 0; 273 | knownRegions = ( 274 | en, 275 | Base, 276 | ); 277 | mainGroup = 6DA9632F214FF79E000F51AE; 278 | productRefGroup = 6DA96339214FF79E000F51AE /* Products */; 279 | projectDirPath = ""; 280 | projectRoot = ""; 281 | targets = ( 282 | 6DA96337214FF79E000F51AE /* fireworks */, 283 | ); 284 | }; 285 | /* End PBXProject section */ 286 | 287 | /* Begin PBXResourcesBuildPhase section */ 288 | 6DA96336214FF79E000F51AE /* Resources */ = { 289 | isa = PBXResourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 6DA96346214FF79F000F51AE /* LaunchScreen.storyboard in Resources */, 293 | 6DA96343214FF79F000F51AE /* Assets.xcassets in Resources */, 294 | 6DA96341214FF79E000F51AE /* Main.storyboard in Resources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXResourcesBuildPhase section */ 299 | 300 | /* Begin PBXSourcesBuildPhase section */ 301 | 6DA96334214FF79E000F51AE /* Sources */ = { 302 | isa = PBXSourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | 6DF25D67215298F800AD30F7 /* DuckFountainFireworkController.swift in Sources */, 306 | 6D47CE4121524A40000B3639 /* FireworkSparkScheduler.swift in Sources */, 307 | 6D47CE3221523C9B000B3639 /* ClassicFireworkAnimator.swift in Sources */, 308 | 6D47CE3D215242E7000B3639 /* FountainFirework.swift in Sources */, 309 | 6DA963512150242D000F51AE /* ClassicSparkTrajectoryFactory.swift in Sources */, 310 | 6D4EBAA8215223C500A2B836 /* ClassicFireworkController.swift in Sources */, 311 | 6DF25D732152A99500AD30F7 /* ClassicFireworkDemoViewController.swift in Sources */, 312 | 6DF25D6E21529C8400AD30F7 /* PusheenFountainFireworkController.swift in Sources */, 313 | 6D4EBAAA215223D900A2B836 /* CircleColorSparkView.swift in Sources */, 314 | 6DF25D65215298C100AD30F7 /* DuckFountainFirework.swift in Sources */, 315 | 6D47CE582152744B000B3639 /* ClassicSparkTrajectoryFactoryProtocol.swift in Sources */, 316 | 6D47CE4F21525EFA000B3639 /* CGPoint+Extensions.swift in Sources */, 317 | 6D47CE39215240ED000B3639 /* SparkView.swift in Sources */, 318 | 6D0933E12151215400190290 /* SparkTrajectory.swift in Sources */, 319 | 6D47CE5A2152751A000B3639 /* SparkTrajectoryFactory.swift in Sources */, 320 | 6DF25D5421527DA800AD30F7 /* SparkViewFactory.swift in Sources */, 321 | 6D47CE2E2152331E000B3639 /* Firework.swift in Sources */, 322 | 6DF25D6A21529B3100AD30F7 /* PusheenSparkViewFactory.swift in Sources */, 323 | 6DF25D5921528CEE00AD30F7 /* FountainSparkTrajectoryFactory.swift in Sources */, 324 | 6D47CE3B215242A6000B3639 /* FountainFireworkAnimator.swift in Sources */, 325 | 6DF25D5621527E2900AD30F7 /* CircleColorSparkViewFactory.swift in Sources */, 326 | 6DF25D632152987700AD30F7 /* DuckSparkViewFactory.swift in Sources */, 327 | 6DB1AD3F2152B3A300169C39 /* FountainFireworkDemoViewController.swift in Sources */, 328 | 6D47CE5221525F3A000B3639 /* FireworkSpark.swift in Sources */, 329 | 6D47CE3021523331000B3639 /* ClassicFirework.swift in Sources */, 330 | 6D47CE3F215243AC000B3639 /* FountainFireworkController.swift in Sources */, 331 | 6DA9633C214FF79E000F51AE /* AppDelegate.swift in Sources */, 332 | 6DF25D5D2152939300AD30F7 /* ImageSparkViewFactory.swift in Sources */, 333 | 6D47CE3421523CFF000B3639 /* SparkViewAnimator.swift in Sources */, 334 | 6DF25D6C21529C5E00AD30F7 /* PusheenFountainFirework.swift in Sources */, 335 | 6DF25D5B2152932B00AD30F7 /* ImageSparkView.swift in Sources */, 336 | ); 337 | runOnlyForDeploymentPostprocessing = 0; 338 | }; 339 | /* End PBXSourcesBuildPhase section */ 340 | 341 | /* Begin PBXVariantGroup section */ 342 | 6DA9633F214FF79E000F51AE /* Main.storyboard */ = { 343 | isa = PBXVariantGroup; 344 | children = ( 345 | 6DA96340214FF79E000F51AE /* Base */, 346 | ); 347 | name = Main.storyboard; 348 | sourceTree = ""; 349 | }; 350 | 6DA96344214FF79F000F51AE /* LaunchScreen.storyboard */ = { 351 | isa = PBXVariantGroup; 352 | children = ( 353 | 6DA96345214FF79F000F51AE /* Base */, 354 | ); 355 | name = LaunchScreen.storyboard; 356 | sourceTree = ""; 357 | }; 358 | /* End PBXVariantGroup section */ 359 | 360 | /* Begin XCBuildConfiguration section */ 361 | 6DA96348214FF79F000F51AE /* Debug */ = { 362 | isa = XCBuildConfiguration; 363 | buildSettings = { 364 | ALWAYS_SEARCH_USER_PATHS = NO; 365 | CLANG_ANALYZER_NONNULL = YES; 366 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 367 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 368 | CLANG_CXX_LIBRARY = "libc++"; 369 | CLANG_ENABLE_MODULES = YES; 370 | CLANG_ENABLE_OBJC_ARC = YES; 371 | CLANG_ENABLE_OBJC_WEAK = YES; 372 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 373 | CLANG_WARN_BOOL_CONVERSION = YES; 374 | CLANG_WARN_COMMA = YES; 375 | CLANG_WARN_CONSTANT_CONVERSION = YES; 376 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 377 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 378 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INFINITE_RECURSION = YES; 382 | CLANG_WARN_INT_CONVERSION = YES; 383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 388 | CLANG_WARN_STRICT_PROTOTYPES = YES; 389 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 390 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 391 | CLANG_WARN_UNREACHABLE_CODE = YES; 392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 393 | CODE_SIGN_IDENTITY = "iPhone Developer"; 394 | COPY_PHASE_STRIP = NO; 395 | DEBUG_INFORMATION_FORMAT = dwarf; 396 | ENABLE_STRICT_OBJC_MSGSEND = YES; 397 | ENABLE_TESTABILITY = YES; 398 | GCC_C_LANGUAGE_STANDARD = gnu11; 399 | GCC_DYNAMIC_NO_PIC = NO; 400 | GCC_NO_COMMON_BLOCKS = YES; 401 | GCC_OPTIMIZATION_LEVEL = 0; 402 | GCC_PREPROCESSOR_DEFINITIONS = ( 403 | "DEBUG=1", 404 | "$(inherited)", 405 | ); 406 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 407 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 408 | GCC_WARN_UNDECLARED_SELECTOR = YES; 409 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 410 | GCC_WARN_UNUSED_FUNCTION = YES; 411 | GCC_WARN_UNUSED_VARIABLE = YES; 412 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 413 | MTL_ENABLE_DEBUG_INFO = YES; 414 | ONLY_ACTIVE_ARCH = YES; 415 | SDKROOT = iphoneos; 416 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 417 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 418 | }; 419 | name = Debug; 420 | }; 421 | 6DA96349214FF79F000F51AE /* Release */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ALWAYS_SEARCH_USER_PATHS = NO; 425 | CLANG_ANALYZER_NONNULL = YES; 426 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 427 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 428 | CLANG_CXX_LIBRARY = "libc++"; 429 | CLANG_ENABLE_MODULES = YES; 430 | CLANG_ENABLE_OBJC_ARC = YES; 431 | CLANG_ENABLE_OBJC_WEAK = YES; 432 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 433 | CLANG_WARN_BOOL_CONVERSION = YES; 434 | CLANG_WARN_COMMA = YES; 435 | CLANG_WARN_CONSTANT_CONVERSION = YES; 436 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 437 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 438 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 439 | CLANG_WARN_EMPTY_BODY = YES; 440 | CLANG_WARN_ENUM_CONVERSION = YES; 441 | CLANG_WARN_INFINITE_RECURSION = YES; 442 | CLANG_WARN_INT_CONVERSION = YES; 443 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 444 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 445 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 446 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 447 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 448 | CLANG_WARN_STRICT_PROTOTYPES = YES; 449 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 450 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 451 | CLANG_WARN_UNREACHABLE_CODE = YES; 452 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 453 | CODE_SIGN_IDENTITY = "iPhone Developer"; 454 | COPY_PHASE_STRIP = NO; 455 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 456 | ENABLE_NS_ASSERTIONS = NO; 457 | ENABLE_STRICT_OBJC_MSGSEND = YES; 458 | GCC_C_LANGUAGE_STANDARD = gnu11; 459 | GCC_NO_COMMON_BLOCKS = YES; 460 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 461 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 462 | GCC_WARN_UNDECLARED_SELECTOR = YES; 463 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 464 | GCC_WARN_UNUSED_FUNCTION = YES; 465 | GCC_WARN_UNUSED_VARIABLE = YES; 466 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 467 | MTL_ENABLE_DEBUG_INFO = NO; 468 | SDKROOT = iphoneos; 469 | SWIFT_COMPILATION_MODE = wholemodule; 470 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 471 | VALIDATE_PRODUCT = YES; 472 | }; 473 | name = Release; 474 | }; 475 | 6DA9634B214FF79F000F51AE /* Debug */ = { 476 | isa = XCBuildConfiguration; 477 | buildSettings = { 478 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 479 | CODE_SIGN_STYLE = Automatic; 480 | DEVELOPMENT_TEAM = VD7YURQERW; 481 | INFOPLIST_FILE = fireworks/Info.plist; 482 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 483 | LD_RUNPATH_SEARCH_PATHS = ( 484 | "$(inherited)", 485 | "@executable_path/Frameworks", 486 | ); 487 | PRODUCT_BUNDLE_IDENTIFIER = com.szulctomasz.fireworks; 488 | PRODUCT_NAME = "$(TARGET_NAME)"; 489 | SWIFT_VERSION = 4.0; 490 | TARGETED_DEVICE_FAMILY = "1,2"; 491 | }; 492 | name = Debug; 493 | }; 494 | 6DA9634C214FF79F000F51AE /* Release */ = { 495 | isa = XCBuildConfiguration; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CODE_SIGN_STYLE = Automatic; 499 | DEVELOPMENT_TEAM = VD7YURQERW; 500 | INFOPLIST_FILE = fireworks/Info.plist; 501 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 502 | LD_RUNPATH_SEARCH_PATHS = ( 503 | "$(inherited)", 504 | "@executable_path/Frameworks", 505 | ); 506 | PRODUCT_BUNDLE_IDENTIFIER = com.szulctomasz.fireworks; 507 | PRODUCT_NAME = "$(TARGET_NAME)"; 508 | SWIFT_VERSION = 4.0; 509 | TARGETED_DEVICE_FAMILY = "1,2"; 510 | }; 511 | name = Release; 512 | }; 513 | /* End XCBuildConfiguration section */ 514 | 515 | /* Begin XCConfigurationList section */ 516 | 6DA96333214FF79E000F51AE /* Build configuration list for PBXProject "fireworks" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 6DA96348214FF79F000F51AE /* Debug */, 520 | 6DA96349214FF79F000F51AE /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | 6DA9634A214FF79F000F51AE /* Build configuration list for PBXNativeTarget "fireworks" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 6DA9634B214FF79F000F51AE /* Debug */, 529 | 6DA9634C214FF79F000F51AE /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | /* End XCConfigurationList section */ 535 | }; 536 | rootObject = 6DA96330214FF79E000F51AE /* Project object */; 537 | } 538 | -------------------------------------------------------------------------------- /fireworks.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fireworks.xcodeproj/project.xcworkspace/xcuserdata/tomkowz.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks.xcodeproj/project.xcworkspace/xcuserdata/tomkowz.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /fireworks.xcodeproj/xcuserdata/tomkowz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /fireworks.xcodeproj/xcuserdata/tomkowz.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | fireworks.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /fireworks/Custom Fireworks/Duck Fireworks/DuckFountainFirework.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class DuckFountainFirework: FountainFirework { 4 | 5 | override var sparkViewFactory: SparkViewFactory { 6 | return DuckSparkViewFactory() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /fireworks/Custom Fireworks/Duck Fireworks/DuckFountainFireworkController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class DuckFountainFireworkController: FountainFireworkController { 4 | 5 | override func createFirework(at origin: CGPoint, sparkSize: CGSize, scale: CGFloat) -> Firework { 6 | return DuckFountainFirework(origin: origin, sparkSize: sparkSize, scale: scale) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /fireworks/Custom Fireworks/Duck Fireworks/DuckSparkViewFactory.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class DuckSparkViewFactory: SparkViewFactory { 4 | 5 | func create(with data: SparkViewFactoryData) -> SparkView { 6 | guard let image = UIImage(named: "duck") else { 7 | fatalError("Couldn't find a duck!") 8 | } 9 | 10 | return ImageSparkView(image: image, size: data.size) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /fireworks/Custom Fireworks/Pusheen Fireworks/PusheenFountainFirework.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class PusheenFountainFirework: FountainFirework { 4 | 5 | override var sparkViewFactory: SparkViewFactory { 6 | return PusheenSparkViewFactory() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /fireworks/Custom Fireworks/Pusheen Fireworks/PusheenFountainFireworkController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class PusheenFountainFireworkController: FountainFireworkController { 4 | 5 | override func createFirework(at origin: CGPoint, sparkSize: CGSize, scale: CGFloat) -> Firework { 6 | return PusheenFountainFirework(origin: origin, sparkSize: sparkSize, scale: scale) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /fireworks/Custom Fireworks/Pusheen Fireworks/PusheenSparkViewFactory.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class PusheenSparkViewFactory: SparkViewFactory { 4 | 5 | private lazy var imageNames: [String] = { 6 | var names = [String]() 7 | 8 | for i in 1...7 { 9 | names.append("pusheen-\(i)") 10 | } 11 | 12 | return names 13 | }() 14 | 15 | func create(with data: SparkViewFactoryData) -> SparkView { 16 | let name = self.imageNames[data.index % self.imageNames.count] 17 | guard let image = UIImage(named: name) else { 18 | fatalError("Couldn't find a pusheen :c") 19 | } 20 | 21 | return ImageSparkView(image: image, size: data.size) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /fireworks/Demo App/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // fireworks 4 | // 5 | // Created by Tomasz Szulc on 17.09.2018. 6 | // Copyright © 2018 Tomasz Szulc. 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: [UIApplicationLaunchOptionsKey: 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 | -------------------------------------------------------------------------------- /fireworks/Demo App/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 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/duck.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "duck.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/duck.imageset/duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/duck.imageset/duck.png -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pusheen-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-1.imageset/pusheen-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/pusheen/pusheen-1.imageset/pusheen-1.png -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pusheen-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-2.imageset/pusheen-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/pusheen/pusheen-2.imageset/pusheen-2.png -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pusheen-3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-3.imageset/pusheen-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/pusheen/pusheen-3.imageset/pusheen-3.png -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pusheen-4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-4.imageset/pusheen-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/pusheen/pusheen-4.imageset/pusheen-4.png -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pusheen-5.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-5.imageset/pusheen-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/pusheen/pusheen-5.imageset/pusheen-5.png -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pusheen-6.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-6.imageset/pusheen-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/pusheen/pusheen-6.imageset/pusheen-6.png -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pusheen-7.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/pusheen/pusheen-7.imageset/pusheen-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/pusheen/pusheen-7.imageset/pusheen-7.png -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "star.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fireworks/Demo App/Assets.xcassets/star.imageset/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fireworks/Demo App/Assets.xcassets/star.imageset/star.png -------------------------------------------------------------------------------- /fireworks/Demo App/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 | -------------------------------------------------------------------------------- /fireworks/Demo App/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 | 27 | 33 | 34 | 35 | 36 | 49 | 62 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 130 | 131 | 132 | 133 | 146 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /fireworks/Demo App/ClassicFireworkDemoViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class ClassicFireworkDemoViewController: UIViewController { 4 | 5 | @IBOutlet private var label: UILabel! 6 | 7 | @IBOutlet private var btn1: UIButton! { 8 | didSet { 9 | self.btn1.tag = 0 10 | self.btn1.style() 11 | } 12 | } 13 | 14 | @IBOutlet private var btn2: UIButton! { 15 | didSet { 16 | self.btn2.tag = 1 17 | self.btn2.style() 18 | } 19 | } 20 | 21 | @IBOutlet private var btn3: UIButton! { 22 | didSet { 23 | self.btn3.tag = 2 24 | self.btn3.style() 25 | } 26 | } 27 | 28 | private var correctSolutionIndex: Int = 0 29 | 30 | private let fireworkController = ClassicFireworkController() 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | self.refresh() 35 | } 36 | 37 | private func refresh() { 38 | let num1 = Int(arc4random_uniform(10)) 39 | let num2 = Int(arc4random_uniform(10)) 40 | 41 | let sol1 = num1 + num2 42 | var sol2 = sol1 + Int(arc4random_uniform(10)) - 5 43 | if sol2 == sol1 { 44 | sol2 += 1 45 | } 46 | 47 | var sol3 = sol1 + Int(arc4random_uniform(12)) - 4 48 | if sol3 == sol1 { 49 | sol3 -= 1 50 | } 51 | 52 | var solutions = [sol1, sol2, sol3] 53 | var shuffled = [Int]() 54 | 55 | for _ in 0.. SparkViewFactoryData { 61 | return DefaultSparkViewFactoryData(size: self.sparkSize, index: index) 62 | } 63 | 64 | public func sparkView(at index: Int) -> SparkView { 65 | return self.sparkViewFactory.create(with: self.sparkViewFactoryData(at: index)) 66 | } 67 | 68 | public func trajectory(at index: Int) -> SparkTrajectory { 69 | let quarter = self.quarters[index] 70 | let flipOptions = self.flipOptions(for: quarter) 71 | let changeVector = self.randomChangeVector(flipOptions: flipOptions, maxValue: self.maxChangeValue) 72 | let sparkOrigin = self.origin.adding(vector: changeVector) 73 | return self.randomTrajectory(flipOptions: flipOptions).scale(by: self.scale).shift(to: sparkOrigin) 74 | } 75 | 76 | private func flipOptions(`for` quarter: Quarter) -> FlipOptions { 77 | var flipOptions: FlipOptions = [] 78 | if quarter == .bottomLeft || quarter == .topLeft { 79 | flipOptions.insert(.horizontally) 80 | } 81 | 82 | if quarter == .bottomLeft || quarter == .bottomRight { 83 | flipOptions.insert(.vertically) 84 | } 85 | 86 | return flipOptions 87 | } 88 | 89 | private func shuffledQuarters() -> [Quarter] { 90 | return [ 91 | .topRight, .topRight, 92 | .bottomRight, .bottomRight, 93 | .bottomLeft, .bottomLeft, 94 | .topLeft, .topLeft 95 | ].shuffled() 96 | } 97 | 98 | private func randomTrajectory(flipOptions: FlipOptions) -> SparkTrajectory { 99 | var trajectory: SparkTrajectory 100 | 101 | if flipOptions.contains(.vertically) { 102 | trajectory = self.classicTrajectoryFactory.randomBottomRight() 103 | } else { 104 | trajectory = self.classicTrajectoryFactory.randomTopRight() 105 | } 106 | 107 | return flipOptions.contains(.horizontally) ? trajectory.flip() : trajectory 108 | } 109 | 110 | private func randomChangeVector(flipOptions: FlipOptions, maxValue: Int) -> CGVector { 111 | let values = (self.randomChange(maxValue), self.randomChange(maxValue)) 112 | let changeX = flipOptions.contains(.horizontally) ? -values.0 : values.0 113 | let changeY = flipOptions.contains(.vertically) ? values.1 : -values.0 114 | return CGVector(dx: changeX, dy: changeY) 115 | } 116 | 117 | private func randomChange(_ maxValue: Int) -> CGFloat { 118 | return CGFloat(arc4random_uniform(UInt32(maxValue))) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Classic Firework/ClassicFireworkAnimator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct ClassicFireworkAnimator: SparkViewAnimator { 4 | 5 | public init() {} 6 | 7 | public func animate(spark: FireworkSpark, duration: TimeInterval) { 8 | spark.sparkView.isHidden = false // show previously hidden spark view 9 | 10 | CATransaction.begin() 11 | 12 | // Position 13 | let positionAnim = CAKeyframeAnimation(keyPath: "position") 14 | positionAnim.path = spark.trajectory.path.cgPath 15 | positionAnim.calculationMode = kCAAnimationLinear 16 | positionAnim.rotationMode = kCAAnimationRotateAuto 17 | positionAnim.duration = duration 18 | 19 | // Scale 20 | let randomMaxScale = 1.0 + CGFloat(arc4random_uniform(7)) / 10.0 21 | let randomMinScale = 0.5 + CGFloat(arc4random_uniform(3)) / 10.0 22 | 23 | let fromTransform = CATransform3DIdentity 24 | let byTransform = CATransform3DScale(fromTransform, randomMaxScale, randomMaxScale, randomMaxScale) 25 | let toTransform = CATransform3DScale(CATransform3DIdentity, randomMinScale, randomMinScale, randomMinScale) 26 | let transformAnim = CAKeyframeAnimation(keyPath: "transform") 27 | 28 | transformAnim.values = [ 29 | NSValue(caTransform3D: fromTransform), 30 | NSValue(caTransform3D: byTransform), 31 | NSValue(caTransform3D: toTransform) 32 | ] 33 | 34 | transformAnim.duration = duration 35 | transformAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) 36 | spark.sparkView.layer.transform = toTransform 37 | 38 | // Opacity 39 | let opacityAnim = CAKeyframeAnimation(keyPath: "opacity") 40 | opacityAnim.values = [1.0, 0.0] 41 | opacityAnim.keyTimes = [0.95, 0.98] 42 | opacityAnim.duration = duration 43 | spark.sparkView.layer.opacity = 0.0 44 | 45 | // Group 46 | let groupAnimation = CAAnimationGroup() 47 | groupAnimation.animations = [positionAnim, transformAnim, opacityAnim] 48 | groupAnimation.duration = duration 49 | 50 | CATransaction.setCompletionBlock({ 51 | spark.sparkView.removeFromSuperview() 52 | }) 53 | 54 | spark.sparkView.layer.add(groupAnimation, forKey: "spark-animation") 55 | 56 | CATransaction.commit() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Classic Firework/ClassicFireworkController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public class ClassicFireworkController { 4 | 5 | public init() {} 6 | 7 | public var sparkAnimator: SparkViewAnimator { 8 | return ClassicFireworkAnimator() 9 | } 10 | 11 | public func createFirework(at origin: CGPoint, sparkSize: CGSize, scale: CGFloat) -> Firework { 12 | return ClassicFirework(origin: origin, sparkSize: sparkSize, scale: scale) 13 | } 14 | 15 | /// It allows fireworks to explodes in close range of corners of a source view 16 | public func addFireworks(count fireworksCount: Int = 1, 17 | sparks sparksCount: Int = 8, 18 | around sourceView: UIView, 19 | sparkSize: CGSize = CGSize(width: 7, height: 7), 20 | scale: CGFloat = 45.0, 21 | maxVectorChange: CGFloat = 15.0, 22 | animationDuration: TimeInterval = 0.4, 23 | canChangeZIndex: Bool = true) { 24 | guard let superview = sourceView.superview else { fatalError() } 25 | 26 | let origins = [ 27 | CGPoint(x: sourceView.frame.minX, y: sourceView.frame.minY), 28 | CGPoint(x: sourceView.frame.maxX, y: sourceView.frame.minY), 29 | CGPoint(x: sourceView.frame.minX, y: sourceView.frame.maxY), 30 | CGPoint(x: sourceView.frame.maxX, y: sourceView.frame.maxY), 31 | ] 32 | 33 | for _ in 0.. CGVector { 57 | return CGVector(dx: self.randomChange(max: max), dy: self.randomChange(max: max)) 58 | } 59 | 60 | private func randomChange(max: CGFloat) -> CGFloat { 61 | return CGFloat(arc4random_uniform(UInt32(max))) - (max / 2.0) 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Classic Firework/ClassicSparkTrajectoryFactory.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public final class ClassicSparkTrajectoryFactory: ClassicSparkTrajectoryFactoryProtocol { 4 | 5 | public init() {} 6 | 7 | private lazy var topRight: [SparkTrajectory] = { 8 | return [ 9 | CubicBezierTrajectory(0.00, 0.00, 0.31, -0.46, 0.74, -0.29, 0.99, 0.12), 10 | CubicBezierTrajectory(0.00, 0.00, 0.31, -0.46, 0.62, -0.49, 0.88, -0.19), 11 | CubicBezierTrajectory(0.00, 0.00, 0.10, -0.54, 0.44, -0.53, 0.66, -0.30), 12 | CubicBezierTrajectory(0.00, 0.00, 0.19, -0.46, 0.41, -0.53, 0.65, -0.45), 13 | ] 14 | }() 15 | 16 | private lazy var bottomRight: [SparkTrajectory] = { 17 | return [ 18 | CubicBezierTrajectory(0.00, 0.00, 0.42, -0.01, 0.68, 0.11, 0.87, 0.44), 19 | CubicBezierTrajectory(0.00, 0.00, 0.35, 0.00, 0.55, 0.12, 0.62, 0.45), 20 | CubicBezierTrajectory(0.00, 0.00, 0.21, 0.05, 0.31, 0.19, 0.32, 0.45), 21 | CubicBezierTrajectory(0.00, 0.00, 0.18, 0.00, 0.31, 0.11, 0.35, 0.25), 22 | ] 23 | }() 24 | 25 | public func randomTopRight() -> SparkTrajectory { 26 | return self.topRight[Int(arc4random_uniform(UInt32(self.topRight.count)))] 27 | } 28 | 29 | public func randomBottomRight() -> SparkTrajectory { 30 | return self.bottomRight[Int(arc4random_uniform(UInt32(self.bottomRight.count)))] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Classic Firework/ClassicSparkTrajectoryFactoryProtocol.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol ClassicSparkTrajectoryFactoryProtocol: SparkTrajectoryFactory { 4 | 5 | func randomTopRight() -> SparkTrajectory 6 | func randomBottomRight() -> SparkTrajectory 7 | } 8 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Fountain Firework/FountainFirework.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public class FountainFirework: Firework { 4 | 5 | /** 6 | x x x x x 7 | x x x 8 | x x x 9 | x x x x 10 | x x x 11 | x x 12 | x 13 | x 14 | ------------- 15 | **/ 16 | 17 | public var origin: CGPoint 18 | public var scale: CGFloat 19 | public var sparkSize: CGSize 20 | 21 | public var maxChangeValue: Int { 22 | return 10 23 | } 24 | 25 | public var trajectoryFactory: SparkTrajectoryFactory { 26 | return FountainSparkTrajectoryFactory() 27 | } 28 | 29 | private var defaultTrajectoryFactory: DefaultSparkTrajectoryFactory { 30 | return self.trajectoryFactory as! DefaultSparkTrajectoryFactory 31 | } 32 | 33 | public var sparkViewFactory: SparkViewFactory { 34 | return CircleColorSparkViewFactory() 35 | } 36 | 37 | public init(origin: CGPoint, sparkSize: CGSize, scale: CGFloat) { 38 | self.origin = origin 39 | self.sparkSize = sparkSize 40 | self.scale = scale 41 | } 42 | 43 | public func sparkViewFactoryData(at index: Int) -> SparkViewFactoryData { 44 | return DefaultSparkViewFactoryData(size: self.sparkSize, index: index) 45 | } 46 | 47 | public func sparkView(at index: Int) -> SparkView { 48 | return self.sparkViewFactory.create(with: self.sparkViewFactoryData(at: index)) 49 | } 50 | 51 | public func trajectory(at index: Int) -> SparkTrajectory { 52 | return self.defaultTrajectoryFactory.random().scale(by: self.scale).shift(to: self.origin) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Fountain Firework/FountainFireworkAnimator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct FountainFireworkAnimator: SparkViewAnimator { 4 | 5 | public init() {} 6 | 7 | public func animate(spark: FireworkSpark, duration: TimeInterval) { 8 | spark.sparkView.isHidden = false 9 | 10 | CATransaction.begin() 11 | 12 | // Position 13 | let positionAnim = CAKeyframeAnimation(keyPath: "position") 14 | positionAnim.path = spark.trajectory.path.cgPath 15 | positionAnim.calculationMode = kCAAnimationLinear 16 | positionAnim.rotationMode = kCAAnimationRotateAuto 17 | positionAnim.duration = duration 18 | 19 | // Scale 20 | let randomMinScale = 0.1 + CGFloat(arc4random_uniform(3)) / 10.0 21 | let randomMaxScale = 1.2 + CGFloat(arc4random_uniform(7)) / 10.0 22 | 23 | let fromTransform = CATransform3DScale(CATransform3DIdentity, randomMinScale, randomMinScale, randomMinScale) 24 | let byTransform = CATransform3DScale(CATransform3DIdentity, randomMaxScale, randomMaxScale, randomMaxScale) 25 | let toTransform = CATransform3DScale(CATransform3DIdentity, 0.2, 0.2, 0.2) 26 | 27 | let transformAnim = CAKeyframeAnimation(keyPath: "transform") 28 | transformAnim.values = [ 29 | NSValue(caTransform3D: fromTransform), 30 | NSValue(caTransform3D: byTransform), 31 | NSValue(caTransform3D: byTransform), 32 | NSValue(caTransform3D: toTransform), 33 | ] 34 | 35 | transformAnim.keyTimes = [0.0, 0.4, 0.9, 1.0] 36 | transformAnim.duration = duration 37 | transformAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) 38 | spark.sparkView.layer.transform = toTransform 39 | 40 | // Opacity 41 | let opacityAnim = CAKeyframeAnimation(keyPath: "opacity") 42 | opacityAnim.values = [1.0, 0.0] 43 | opacityAnim.keyTimes = [0.95, 0.98] 44 | opacityAnim.duration = duration 45 | spark.sparkView.layer.opacity = 0.0 46 | 47 | // Group 48 | let groupAnimation = CAAnimationGroup() 49 | groupAnimation.animations = [positionAnim, transformAnim, opacityAnim] 50 | groupAnimation.duration = duration 51 | 52 | CATransaction.setCompletionBlock({ 53 | spark.sparkView.removeFromSuperview() 54 | }) 55 | 56 | spark.sparkView.layer.add(groupAnimation, forKey: "spark-animation") 57 | 58 | CATransaction.commit() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Fountain Firework/FountainFireworkController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public class FountainFireworkController { 4 | 5 | public init() {} 6 | 7 | public var scheduler = FireworkSparkScheduler() 8 | 9 | public func createFirework(at origin: CGPoint, sparkSize: CGSize, scale: CGFloat) -> Firework { 10 | return FountainFirework(origin: origin, sparkSize: sparkSize, scale: scale) 11 | } 12 | 13 | public func addFirework(sparks sparksCount: Int, 14 | above sourceView: UIView, 15 | sparkSize: CGSize = CGSize(width: 9, height: 9), 16 | scale: CGFloat = 45.0, 17 | offsetY: CGFloat = 0, 18 | animationDuration: TimeInterval = 0.7) { 19 | guard let superview = sourceView.superview else { fatalError() } 20 | 21 | let origin = CGPoint(x: sourceView.frame.midX, y: sourceView.frame.minY - offsetY) 22 | let firework = self.createFirework(at: origin, sparkSize: sparkSize, scale: scale) 23 | 24 | for sparkIndex in 0.. SparkTrajectory { 9 | let y1 = 1.0 + (CGFloat(arc4random_uniform(6)) - 3) / 10.0 10 | let x2 = CGFloat(arc4random_uniform(4)) - 2.0 11 | let y2 = y1 + 2.0 + (CGFloat(arc4random_uniform(10)) - 5) / 10.0 12 | 13 | return QuadraticBezierTrajectory(0.0, 0.0, 0.0, -y1, x2, -y2) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Model/Firework.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol Firework { 4 | 5 | /// Defines origin of firework. 6 | var origin: CGPoint { get set } 7 | 8 | /// Defines trajectory scale. Trajectory is normalized so it needs to be scaled up 9 | /// before presenting on screen. 10 | var scale: CGFloat { get set } 11 | 12 | /// Defines size of a single spark. 13 | var sparkSize: CGSize { get set } 14 | 15 | /// Returns trajectories 16 | var trajectoryFactory: SparkTrajectoryFactory { get } 17 | 18 | /// Returns spark views 19 | var sparkViewFactory: SparkViewFactory { get } 20 | 21 | func sparkViewFactoryData(at index: Int) -> SparkViewFactoryData 22 | func sparkView(at index: Int) -> SparkView 23 | func trajectory(at index: Int) -> SparkTrajectory 24 | } 25 | 26 | extension Firework { 27 | 28 | /// Helper method that return spark view and corresponding trajectory. 29 | public func spark(at index: Int) -> FireworkSpark { 30 | return FireworkSpark(self.sparkView(at: index), self.trajectory(at: index)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Model/FireworkSpark.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public typealias FireworkSpark = (sparkView: SparkView, trajectory: SparkTrajectory) 4 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Model/FireworkSparkScheduler.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public class FireworkSparkScheduler { 4 | 5 | public var delay: TimeInterval = 0.05 6 | private var timer: Timer? 7 | private var queue = [Data]() 8 | 9 | private struct Data { 10 | 11 | weak var presenterView: UIView? 12 | let sparks: [FireworkSpark] 13 | let animator: SparkViewAnimator 14 | let animationDuration: TimeInterval 15 | } 16 | 17 | public func schedule(sparks: [FireworkSpark], 18 | in presenterView: UIView, 19 | with animator: SparkViewAnimator, 20 | animationDuration: TimeInterval) { 21 | let data = Data(presenterView: presenterView, 22 | sparks: sparks, 23 | animator: animator, 24 | animationDuration: animationDuration) 25 | 26 | self.queue.append(data) 27 | 28 | if self.timer == nil { 29 | self.scheduleTimer() 30 | } 31 | } 32 | 33 | public func cancel() { 34 | self.timer?.invalidate() 35 | self.timer = nil 36 | } 37 | 38 | private func scheduleTimer() { 39 | self.cancel() 40 | 41 | self.timer = Timer.scheduledTimer(timeInterval: self.delay, 42 | target: self, 43 | selector: #selector(timerDidFire), 44 | userInfo: nil, repeats: false) 45 | } 46 | 47 | @objc private func timerDidFire() { 48 | guard let data = self.queue.first else { 49 | self.cancel() 50 | return 51 | } 52 | 53 | guard let presenterView = data.presenterView else { 54 | self.cancel() 55 | return 56 | } 57 | 58 | for spark in data.sparks { 59 | presenterView.addSubview(spark.sparkView) 60 | data.animator.animate(spark: spark, duration: data.animationDuration) 61 | } 62 | 63 | self.queue.remove(at: 0) 64 | 65 | if self.queue.isEmpty { 66 | self.cancel() 67 | } else { 68 | self.scheduleTimer() 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Model/SparkTrajectory.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol SparkTrajectory { 4 | 5 | /// Stores all points that defines a trajectory. 6 | var points: [CGPoint] { get set } 7 | 8 | /// A path representing trajectory. 9 | var path: UIBezierPath { get } 10 | } 11 | 12 | public extension SparkTrajectory { 13 | 14 | /// Scales a trajectory so it fits to a UI requirements in terms of size of a trajectory. 15 | /// Use it after all other transforms have been applied and before `shift`. 16 | public func scale(by value: CGFloat) -> SparkTrajectory { 17 | var copy = self 18 | (0.. SparkTrajectory { 24 | var copy = self 25 | (0.. SparkTrajectory { 32 | var copy = self 33 | let vector = CGVector(dx: point.x, dy: point.y) 34 | (0.. SparkTrajectory 8 | } 9 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Model/SparkView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public class SparkView: UIView {} 4 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Model/SparkViewAnimator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol SparkViewAnimator { 4 | func animate(spark: FireworkSpark, duration: TimeInterval) 5 | } 6 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Model/SparkViewFactory.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol SparkViewFactoryData { 4 | 5 | var size: CGSize { get } 6 | var index: Int { get } 7 | } 8 | 9 | public protocol SparkViewFactory { 10 | 11 | func create(with data: SparkViewFactoryData) -> SparkView 12 | } 13 | 14 | public struct DefaultSparkViewFactoryData: SparkViewFactoryData { 15 | 16 | public let size: CGSize 17 | public let index: Int 18 | } 19 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Spark Views/CircleColorSparkView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public final class CircleColorSparkView: SparkView { 4 | 5 | public init(color: UIColor, size: CGSize) { 6 | super.init(frame: CGRect(origin: .zero, size: size)) 7 | self.backgroundColor = color 8 | self.layer.cornerRadius = self.frame.width / 2.0 9 | } 10 | 11 | required init?(coder aDecoder: NSCoder) { 12 | super.init(coder: aDecoder) 13 | } 14 | } 15 | 16 | extension UIColor { 17 | 18 | public static var sparkColorSet1: [UIColor] = { 19 | return [ 20 | UIColor(red:0.89, green:0.58, blue:0.70, alpha:1.00), 21 | UIColor(red:0.96, green:0.87, blue:0.62, alpha:1.00), 22 | UIColor(red:0.67, green:0.82, blue:0.94, alpha:1.00), 23 | UIColor(red:0.54, green:0.56, blue:0.94, alpha:1.00), 24 | ] 25 | }() 26 | } 27 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Spark Views/CircleColorSparkViewFactory.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public class CircleColorSparkViewFactory: SparkViewFactory { 4 | 5 | public var colors: [UIColor] { 6 | return UIColor.sparkColorSet1 7 | } 8 | 9 | public func create(with data: SparkViewFactoryData) -> SparkView { 10 | let color = self.colors[data.index % self.colors.count] 11 | return CircleColorSparkView(color: color, size: data.size) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Spark Views/ImageSparkView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public final class ImageSparkView: SparkView { 4 | 5 | public init(image: UIImage, size: CGSize) { 6 | super.init(frame: CGRect(origin: .zero, size: size)) 7 | 8 | let imageView = UIImageView(frame: self.bounds) 9 | self.addSubview(imageView) 10 | 11 | imageView.image = image 12 | } 13 | 14 | required init?(coder aDecoder: NSCoder) { 15 | super.init(coder: aDecoder) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fireworks/Fireworks/Spark Views/ImageSparkViewFactory.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct ImageSparkViewFactoryData: SparkViewFactoryData { 4 | 5 | public let image: UIImage 6 | public let size: CGSize 7 | public let index: Int 8 | } 9 | 10 | public struct ImageSparkViewFactory: SparkViewFactory { 11 | 12 | public func create(with data: SparkViewFactoryData) -> SparkView { 13 | guard let data = data as? ImageSparkViewFactoryData else { 14 | fatalError("Wrong data.") 15 | } 16 | 17 | return ImageSparkView(image: data.image, size: data.size) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /fireworks/Fireworks/System Extensions/CGPoint+Extensions.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension CGPoint { 4 | 5 | mutating func add(vector: CGVector) { 6 | self.x += vector.dx 7 | self.y += vector.dy 8 | } 9 | 10 | func adding(vector: CGVector) -> CGPoint { 11 | var copy = self 12 | copy.add(vector: vector) 13 | return copy 14 | } 15 | 16 | mutating func multiply(by value: CGFloat) { 17 | self.x *= value 18 | self.y *= value 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /fireworks/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /fountain.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkowz/fireworks/c54a983649510ddaf1ff315000626eb43486410b/fountain.gif --------------------------------------------------------------------------------