├── .gitignore ├── Gagat Example ├── AppDelegate.swift ├── ArchiveTableCellView.swift ├── ArchiveTableViewController.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── appodden.imageset │ │ ├── Contents.json │ │ └── appodden.png │ ├── ifiwereyou.imageset │ │ ├── Contents.json │ │ └── ifiwereyou.jpg │ ├── loveandradio.imageset │ │ ├── Contents.json │ │ └── loveandradio.jpg │ ├── nancy.imageset │ │ ├── Contents.json │ │ └── nancy.png │ └── radiolab.imageset │ │ ├── Contents.json │ │ └── radiolab.jpg ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist └── StyleableNavigationController.swift ├── Gagat.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── Gagat.xcscheme ├── Gagat ├── AnimationUtilities.swift ├── Gagat.h ├── Gagat.swift ├── Info.plist ├── PessimisticPanGestureRecognizer.swift └── TransitionCoordinator.swift ├── GagatObjectiveC Example ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist ├── StyleableNavigationController.h ├── StyleableNavigationController.m ├── StyleableViewController.h ├── StyleableViewController.m └── main.m ├── GagatObjectiveC ├── Gagat.swift ├── GagatObjectiveC.h └── Info.plist ├── LICENSE ├── README.md ├── gagat_example.gif └── gagat_logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Generic 2 | .DS_Store 3 | ._* 4 | __* 5 | *.gz 6 | *.tar 7 | *.tgz 8 | tmp* 9 | tmp/* 10 | tmp/**/* 11 | 12 | # XCode 13 | build/* 14 | *.pbxuser 15 | *.mode?v3 16 | *.perspectivev3 17 | xcuserdata 18 | *.moved-aside 19 | *.xccheckout 20 | 21 | # Temp nibs and swap files 22 | *~.nib 23 | *.swp 24 | *~ 25 | 26 | # Carthage 27 | Carthage 28 | *.framework.zip 29 | -------------------------------------------------------------------------------- /Gagat Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-02-17. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Gagat 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | private var transitionHandle: Gagat.TransitionHandle! 17 | 18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 19 | // Configure Gagat for the applications only window using a jelly factor that is 20 | // slightly larger than the default factor of 1.0. We'll use the root view controller 21 | // as the styleable object, but you can use any object that conforms to `GagatStyleable`. 22 | // 23 | // Note: Make sure you keep a reference to the value returned from `Gagat.configure(for:using:)`. 24 | // If this object is deallocated then the Gagat transition will no longer work. 25 | let configuration = Gagat.Configuration(jellyFactor: 1.5) 26 | let styleableNavigationController = window!.rootViewController as! StyleableNavigationController 27 | transitionHandle = Gagat.configure(for: window!, with: styleableNavigationController, using: configuration) 28 | 29 | return true 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Gagat Example/ArchiveTableCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArchiveTableCellView.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-06-03. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ArchiveTableCellView: UITableViewCell { 12 | 13 | struct Style { 14 | let backgroundColor: UIColor 15 | let titleTextColor: UIColor 16 | let descriptionTextColor: UIColor 17 | 18 | static let light = Style( 19 | backgroundColor: .white, 20 | titleTextColor: .black, 21 | descriptionTextColor: UIColor(white: 0.4, alpha: 1.0) 22 | ) 23 | 24 | static let dark = Style( 25 | backgroundColor: UIColor(white: 0.2, alpha: 1.0), 26 | titleTextColor: .white, 27 | descriptionTextColor: UIColor(white: 0.6, alpha: 1.0) 28 | ) 29 | } 30 | 31 | @IBOutlet private weak var titleLabel: UILabel! 32 | @IBOutlet private weak var artworkImageView: UIImageView! 33 | @IBOutlet private weak var descriptionLabel: UILabel! 34 | 35 | override func awakeFromNib() { 36 | super.awakeFromNib() 37 | artworkImageView.layer.cornerRadius = 10.0 38 | } 39 | 40 | func apply(style: Style) { 41 | backgroundColor = style.backgroundColor 42 | titleLabel.textColor = style.titleTextColor 43 | descriptionLabel.textColor = style.descriptionTextColor 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Gagat Example/ArchiveTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArchiveTableViewController.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-06-03. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Gagat 11 | 12 | class ArchiveTableViewController: UITableViewController, UIGestureRecognizerDelegate { 13 | 14 | // MARK: - UIViewController methods 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | apply(currentStyle) 19 | } 20 | 21 | // MARK: - UITableViewDelegate methods 22 | 23 | override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 24 | guard let archiveCell = cell as? ArchiveTableCellView else { 25 | return 26 | } 27 | 28 | archiveCell.apply(style: currentStyle.cellStyle) 29 | } 30 | 31 | // MARK: - Applying styles 32 | 33 | private struct Style { 34 | let backgroundColor: UIColor 35 | let separatorColor: UIColor? 36 | let cellStyle: ArchiveTableCellView.Style 37 | 38 | static let dark = Style( 39 | backgroundColor: UIColor(white: 0.15, alpha: 1.0), 40 | separatorColor: UIColor(white: 0.35, alpha: 1.0), 41 | cellStyle: .dark 42 | ) 43 | 44 | static let light = Style( 45 | backgroundColor: .groupTableViewBackground, 46 | separatorColor: UIColor(white: 0.81, alpha: 1.0), 47 | cellStyle: .light 48 | ) 49 | } 50 | 51 | private var currentStyle: Style { 52 | return useDarkMode ? .dark : .light 53 | } 54 | 55 | fileprivate var useDarkMode = false { 56 | didSet { apply(currentStyle) } 57 | } 58 | 59 | private func apply(_ style: Style) { 60 | tableView.backgroundColor = style.backgroundColor 61 | tableView.separatorColor = style.separatorColor 62 | apply(style.cellStyle, toCells: tableView.visibleCells) 63 | } 64 | 65 | private func apply(_ cellStyle: ArchiveTableCellView.Style, toCells cells: [UITableViewCell]) { 66 | for cell in cells { 67 | guard let archiveCell = cell as? ArchiveTableCellView else { 68 | continue 69 | } 70 | 71 | archiveCell.apply(style: cellStyle) 72 | } 73 | } 74 | 75 | } 76 | 77 | 78 | extension ArchiveTableViewController: GagatStyleable { 79 | 80 | func toggleActiveStyle() { 81 | useDarkMode = !useDarkMode 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Gagat Example/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 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/appodden.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "appodden.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/appodden.imageset/appodden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Boerworz/Gagat/2f61038d7eddb156636045c749877e56b1f51b12/Gagat Example/Assets.xcassets/appodden.imageset/appodden.png -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/ifiwereyou.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "ifiwereyou.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/ifiwereyou.imageset/ifiwereyou.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Boerworz/Gagat/2f61038d7eddb156636045c749877e56b1f51b12/Gagat Example/Assets.xcassets/ifiwereyou.imageset/ifiwereyou.jpg -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/loveandradio.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "loveandradio.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/loveandradio.imageset/loveandradio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Boerworz/Gagat/2f61038d7eddb156636045c749877e56b1f51b12/Gagat Example/Assets.xcassets/loveandradio.imageset/loveandradio.jpg -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/nancy.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "nancy.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 | } -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/nancy.imageset/nancy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Boerworz/Gagat/2f61038d7eddb156636045c749877e56b1f51b12/Gagat Example/Assets.xcassets/nancy.imageset/nancy.png -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/radiolab.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "radiolab.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Gagat Example/Assets.xcassets/radiolab.imageset/radiolab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Boerworz/Gagat/2f61038d7eddb156636045c749877e56b1f51b12/Gagat Example/Assets.xcassets/radiolab.imageset/radiolab.jpg -------------------------------------------------------------------------------- /Gagat Example/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 | -------------------------------------------------------------------------------- /Gagat Example/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /Gagat Example/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 | 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 | -------------------------------------------------------------------------------- /Gagat Example/StyleableNavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StyleableNavigationController.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-06-03. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Gagat 11 | 12 | class StyleableNavigationController: UINavigationController { 13 | 14 | fileprivate var useDarkMode = false { 15 | didSet { 16 | navigationBar.barStyle = useDarkMode ? .black : .default 17 | } 18 | } 19 | } 20 | 21 | extension StyleableNavigationController: GagatStyleable { 22 | 23 | func styleTransitionWillBegin() { 24 | // Do any work you might need to do before the transition snapshot is taken. 25 | if let styleableChildViewController = topViewController as? GagatStyleable { 26 | styleableChildViewController.styleTransitionWillBegin() 27 | } 28 | } 29 | 30 | func styleTransitionDidEnd() { 31 | // Do any work you might need to do once the transition has completed. 32 | if let styleableChildViewController = topViewController as? GagatStyleable { 33 | styleableChildViewController.styleTransitionDidEnd() 34 | } 35 | } 36 | 37 | func toggleActiveStyle() { 38 | useDarkMode = !useDarkMode 39 | 40 | // It's up to us to get any child view controllers to 41 | // toggle their active style. In this example application we've made 42 | // the child view controller also conform to `GagatStyleable`, but 43 | // this is not required by Gagat. 44 | if let styleableChildViewController = topViewController as? GagatStyleable { 45 | styleableChildViewController.toggleActiveStyle() 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Gagat.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9355030F1EF2F4CA00FD428F /* GagatObjectiveC.h in Headers */ = {isa = PBXBuildFile; fileRef = 9355030D1EF2F4CA00FD428F /* GagatObjectiveC.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 935503141EF2F4E900FD428F /* Gagat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 935503131EF2F4E900FD428F /* Gagat.swift */; }; 12 | 935503171EF2F4F800FD428F /* Gagat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9390063E1EE2038900BF6787 /* Gagat.framework */; }; 13 | 9390062D1EE2024700BF6787 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939006211EE2024700BF6787 /* AppDelegate.swift */; }; 14 | 9390062E1EE2024700BF6787 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 939006221EE2024700BF6787 /* Assets.xcassets */; }; 15 | 939006361EE2029E00BF6787 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 939006231EE2024700BF6787 /* LaunchScreen.storyboard */; }; 16 | 939006371EE2029E00BF6787 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 939006251EE2024700BF6787 /* Main.storyboard */; }; 17 | 939006421EE2038900BF6787 /* Gagat.h in Headers */ = {isa = PBXBuildFile; fileRef = 939006401EE2038900BF6787 /* Gagat.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 939006451EE2038900BF6787 /* Gagat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9390063E1EE2038900BF6787 /* Gagat.framework */; }; 19 | 939006471EE2038900BF6787 /* Gagat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9390063E1EE2038900BF6787 /* Gagat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20 | 9390064F1EE203BF00BF6787 /* AnimationUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9390064B1EE203BF00BF6787 /* AnimationUtilities.swift */; }; 21 | 939006501EE203BF00BF6787 /* Gagat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9390064C1EE203BF00BF6787 /* Gagat.swift */; }; 22 | 939006511EE203BF00BF6787 /* PessimisticPanGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9390064D1EE203BF00BF6787 /* PessimisticPanGestureRecognizer.swift */; }; 23 | 939006521EE203BF00BF6787 /* TransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9390064E1EE203BF00BF6787 /* TransitionCoordinator.swift */; }; 24 | 939006A51EE2B15D00BF6787 /* ArchiveTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939006A41EE2B15D00BF6787 /* ArchiveTableViewController.swift */; }; 25 | 939006A71EE2B17800BF6787 /* ArchiveTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939006A61EE2B17800BF6787 /* ArchiveTableCellView.swift */; }; 26 | 939006A91EE2B36100BF6787 /* StyleableNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939006A81EE2B36100BF6787 /* StyleableNavigationController.swift */; }; 27 | A5C93695217C82D100776DFC /* StyleableNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5C9368B217C82D100776DFC /* StyleableNavigationController.m */; }; 28 | A5C93697217C82D100776DFC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5C9368D217C82D100776DFC /* Assets.xcassets */; }; 29 | A5C93698217C82D100776DFC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A5C9368E217C82D100776DFC /* LaunchScreen.storyboard */; }; 30 | A5C9369A217C82D100776DFC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A5C93692217C82D100776DFC /* AppDelegate.m */; }; 31 | A5C9369D217C83DE00776DFC /* GagatObjectiveC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9355030B1EF2F4CA00FD428F /* GagatObjectiveC.framework */; }; 32 | A5C9369E217C83DE00776DFC /* GagatObjectiveC.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9355030B1EF2F4CA00FD428F /* GagatObjectiveC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 33 | A5C936C6217C84C500776DFC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A5C936C5217C84C500776DFC /* main.m */; }; 34 | A5C936CC217C8EC800776DFC /* StyleableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5C936CB217C8EC800776DFC /* StyleableViewController.m */; }; 35 | A5C936CE217C924A00776DFC /* Gagat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9390063E1EE2038900BF6787 /* Gagat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 36 | /* End PBXBuildFile section */ 37 | 38 | /* Begin PBXContainerItemProxy section */ 39 | 935503151EF2F4F300FD428F /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 9386EDD21E577236009079B6 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = 9390063D1EE2038900BF6787; 44 | remoteInfo = Gagat; 45 | }; 46 | 939006431EE2038900BF6787 /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = 9386EDD21E577236009079B6 /* Project object */; 49 | proxyType = 1; 50 | remoteGlobalIDString = 9390063D1EE2038900BF6787; 51 | remoteInfo = Gagat; 52 | }; 53 | A5C9369F217C83DE00776DFC /* PBXContainerItemProxy */ = { 54 | isa = PBXContainerItemProxy; 55 | containerPortal = 9386EDD21E577236009079B6 /* Project object */; 56 | proxyType = 1; 57 | remoteGlobalIDString = 9355030A1EF2F4CA00FD428F; 58 | remoteInfo = GagatObjectiveC; 59 | }; 60 | A5C936CF217C924A00776DFC /* PBXContainerItemProxy */ = { 61 | isa = PBXContainerItemProxy; 62 | containerPortal = 9386EDD21E577236009079B6 /* Project object */; 63 | proxyType = 1; 64 | remoteGlobalIDString = 9390063D1EE2038900BF6787; 65 | remoteInfo = Gagat; 66 | }; 67 | /* End PBXContainerItemProxy section */ 68 | 69 | /* Begin PBXCopyFilesBuildPhase section */ 70 | 939006461EE2038900BF6787 /* Embed Frameworks */ = { 71 | isa = PBXCopyFilesBuildPhase; 72 | buildActionMask = 2147483647; 73 | dstPath = ""; 74 | dstSubfolderSpec = 10; 75 | files = ( 76 | 939006471EE2038900BF6787 /* Gagat.framework in Embed Frameworks */, 77 | ); 78 | name = "Embed Frameworks"; 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | A5C936A1217C83DE00776DFC /* Embed Frameworks */ = { 82 | isa = PBXCopyFilesBuildPhase; 83 | buildActionMask = 2147483647; 84 | dstPath = ""; 85 | dstSubfolderSpec = 10; 86 | files = ( 87 | A5C9369E217C83DE00776DFC /* GagatObjectiveC.framework in Embed Frameworks */, 88 | A5C936CE217C924A00776DFC /* Gagat.framework in Embed Frameworks */, 89 | ); 90 | name = "Embed Frameworks"; 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | /* End PBXCopyFilesBuildPhase section */ 94 | 95 | /* Begin PBXFileReference section */ 96 | 9355030B1EF2F4CA00FD428F /* GagatObjectiveC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GagatObjectiveC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 97 | 9355030D1EF2F4CA00FD428F /* GagatObjectiveC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GagatObjectiveC.h; sourceTree = ""; }; 98 | 9355030E1EF2F4CA00FD428F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 99 | 935503131EF2F4E900FD428F /* Gagat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gagat.swift; sourceTree = ""; }; 100 | 9386EDDA1E577236009079B6 /* Gagat Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Gagat Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 101 | 939006211EE2024700BF6787 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 102 | 939006221EE2024700BF6787 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 103 | 939006241EE2024700BF6787 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 104 | 939006261EE2024700BF6787 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 105 | 939006281EE2024700BF6787 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 106 | 9390063E1EE2038900BF6787 /* Gagat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Gagat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 107 | 939006401EE2038900BF6787 /* Gagat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Gagat.h; sourceTree = ""; }; 108 | 939006411EE2038900BF6787 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 109 | 9390064B1EE203BF00BF6787 /* AnimationUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationUtilities.swift; sourceTree = ""; }; 110 | 9390064C1EE203BF00BF6787 /* Gagat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gagat.swift; sourceTree = ""; }; 111 | 9390064D1EE203BF00BF6787 /* PessimisticPanGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PessimisticPanGestureRecognizer.swift; sourceTree = ""; }; 112 | 9390064E1EE203BF00BF6787 /* TransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionCoordinator.swift; sourceTree = ""; }; 113 | 939006A41EE2B15D00BF6787 /* ArchiveTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArchiveTableViewController.swift; sourceTree = ""; }; 114 | 939006A61EE2B17800BF6787 /* ArchiveTableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArchiveTableCellView.swift; sourceTree = ""; }; 115 | 939006A81EE2B36100BF6787 /* StyleableNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleableNavigationController.swift; sourceTree = ""; }; 116 | A5C93685217C826A00776DFC /* GagatObjectiveC Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GagatObjectiveC Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 117 | A5C9368B217C82D100776DFC /* StyleableNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StyleableNavigationController.m; sourceTree = ""; }; 118 | A5C9368D217C82D100776DFC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 119 | A5C9368F217C82D100776DFC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 120 | A5C93692217C82D100776DFC /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 121 | A5C93693217C82D100776DFC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 122 | A5C936C4217C847200776DFC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 123 | A5C936C5217C84C500776DFC /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 124 | A5C936C9217C8A1B00776DFC /* StyleableNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StyleableNavigationController.h; sourceTree = ""; }; 125 | A5C936CA217C8EC800776DFC /* StyleableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StyleableViewController.h; sourceTree = ""; }; 126 | A5C936CB217C8EC800776DFC /* StyleableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StyleableViewController.m; sourceTree = ""; }; 127 | /* End PBXFileReference section */ 128 | 129 | /* Begin PBXFrameworksBuildPhase section */ 130 | 935503071EF2F4CA00FD428F /* Frameworks */ = { 131 | isa = PBXFrameworksBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | 935503171EF2F4F800FD428F /* Gagat.framework in Frameworks */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | 9386EDD71E577236009079B6 /* Frameworks */ = { 139 | isa = PBXFrameworksBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | 939006451EE2038900BF6787 /* Gagat.framework in Frameworks */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | 9390063A1EE2038900BF6787 /* Frameworks */ = { 147 | isa = PBXFrameworksBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | ); 151 | runOnlyForDeploymentPostprocessing = 0; 152 | }; 153 | A5C9367A217C826A00776DFC /* Frameworks */ = { 154 | isa = PBXFrameworksBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | A5C9369D217C83DE00776DFC /* GagatObjectiveC.framework in Frameworks */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXFrameworksBuildPhase section */ 162 | 163 | /* Begin PBXGroup section */ 164 | 9355030C1EF2F4CA00FD428F /* GagatObjectiveC */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 935503131EF2F4E900FD428F /* Gagat.swift */, 168 | 9355030D1EF2F4CA00FD428F /* GagatObjectiveC.h */, 169 | 9355030E1EF2F4CA00FD428F /* Info.plist */, 170 | ); 171 | path = GagatObjectiveC; 172 | sourceTree = ""; 173 | }; 174 | 9386EDD11E577236009079B6 = { 175 | isa = PBXGroup; 176 | children = ( 177 | 9390061F1EE2024700BF6787 /* Gagat Example */, 178 | 9390063F1EE2038900BF6787 /* Gagat */, 179 | A5C93689217C82D100776DFC /* GagatObjectiveC Example */, 180 | 9355030C1EF2F4CA00FD428F /* GagatObjectiveC */, 181 | 9386EDDB1E577236009079B6 /* Products */, 182 | ); 183 | sourceTree = ""; 184 | usesTabs = 1; 185 | }; 186 | 9386EDDB1E577236009079B6 /* Products */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 9386EDDA1E577236009079B6 /* Gagat Example.app */, 190 | 9390063E1EE2038900BF6787 /* Gagat.framework */, 191 | 9355030B1EF2F4CA00FD428F /* GagatObjectiveC.framework */, 192 | A5C93685217C826A00776DFC /* GagatObjectiveC Example.app */, 193 | ); 194 | name = Products; 195 | sourceTree = ""; 196 | }; 197 | 9390061F1EE2024700BF6787 /* Gagat Example */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | 939006211EE2024700BF6787 /* AppDelegate.swift */, 201 | 939006A41EE2B15D00BF6787 /* ArchiveTableViewController.swift */, 202 | 939006A61EE2B17800BF6787 /* ArchiveTableCellView.swift */, 203 | 939006A81EE2B36100BF6787 /* StyleableNavigationController.swift */, 204 | 939006251EE2024700BF6787 /* Main.storyboard */, 205 | 939006221EE2024700BF6787 /* Assets.xcassets */, 206 | 939006231EE2024700BF6787 /* LaunchScreen.storyboard */, 207 | 939006281EE2024700BF6787 /* Info.plist */, 208 | ); 209 | path = "Gagat Example"; 210 | sourceTree = ""; 211 | }; 212 | 9390063F1EE2038900BF6787 /* Gagat */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | 9390064B1EE203BF00BF6787 /* AnimationUtilities.swift */, 216 | 9390064C1EE203BF00BF6787 /* Gagat.swift */, 217 | 9390064D1EE203BF00BF6787 /* PessimisticPanGestureRecognizer.swift */, 218 | 9390064E1EE203BF00BF6787 /* TransitionCoordinator.swift */, 219 | 939006401EE2038900BF6787 /* Gagat.h */, 220 | 939006411EE2038900BF6787 /* Info.plist */, 221 | ); 222 | path = Gagat; 223 | sourceTree = ""; 224 | }; 225 | A5C93689217C82D100776DFC /* GagatObjectiveC Example */ = { 226 | isa = PBXGroup; 227 | children = ( 228 | A5C936C5217C84C500776DFC /* main.m */, 229 | A5C936C4217C847200776DFC /* AppDelegate.h */, 230 | A5C93692217C82D100776DFC /* AppDelegate.m */, 231 | A5C936C9217C8A1B00776DFC /* StyleableNavigationController.h */, 232 | A5C9368B217C82D100776DFC /* StyleableNavigationController.m */, 233 | A5C936CA217C8EC800776DFC /* StyleableViewController.h */, 234 | A5C936CB217C8EC800776DFC /* StyleableViewController.m */, 235 | A5C9368D217C82D100776DFC /* Assets.xcassets */, 236 | A5C9368E217C82D100776DFC /* LaunchScreen.storyboard */, 237 | A5C93693217C82D100776DFC /* Info.plist */, 238 | ); 239 | path = "GagatObjectiveC Example"; 240 | sourceTree = ""; 241 | }; 242 | /* End PBXGroup section */ 243 | 244 | /* Begin PBXHeadersBuildPhase section */ 245 | 935503081EF2F4CA00FD428F /* Headers */ = { 246 | isa = PBXHeadersBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | 9355030F1EF2F4CA00FD428F /* GagatObjectiveC.h in Headers */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | 9390063B1EE2038900BF6787 /* Headers */ = { 254 | isa = PBXHeadersBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 939006421EE2038900BF6787 /* Gagat.h in Headers */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXHeadersBuildPhase section */ 262 | 263 | /* Begin PBXNativeTarget section */ 264 | 9355030A1EF2F4CA00FD428F /* GagatObjectiveC */ = { 265 | isa = PBXNativeTarget; 266 | buildConfigurationList = 935503121EF2F4CA00FD428F /* Build configuration list for PBXNativeTarget "GagatObjectiveC" */; 267 | buildPhases = ( 268 | 935503061EF2F4CA00FD428F /* Sources */, 269 | 935503071EF2F4CA00FD428F /* Frameworks */, 270 | 935503081EF2F4CA00FD428F /* Headers */, 271 | 935503091EF2F4CA00FD428F /* Resources */, 272 | ); 273 | buildRules = ( 274 | ); 275 | dependencies = ( 276 | 935503161EF2F4F300FD428F /* PBXTargetDependency */, 277 | ); 278 | name = GagatObjectiveC; 279 | productName = GagatObjectiveC; 280 | productReference = 9355030B1EF2F4CA00FD428F /* GagatObjectiveC.framework */; 281 | productType = "com.apple.product-type.framework"; 282 | }; 283 | 9386EDD91E577236009079B6 /* Gagat Example */ = { 284 | isa = PBXNativeTarget; 285 | buildConfigurationList = 9386EDEC1E577236009079B6 /* Build configuration list for PBXNativeTarget "Gagat Example" */; 286 | buildPhases = ( 287 | 9386EDD61E577236009079B6 /* Sources */, 288 | 9386EDD71E577236009079B6 /* Frameworks */, 289 | 9386EDD81E577236009079B6 /* Resources */, 290 | 939006461EE2038900BF6787 /* Embed Frameworks */, 291 | ); 292 | buildRules = ( 293 | ); 294 | dependencies = ( 295 | 939006441EE2038900BF6787 /* PBXTargetDependency */, 296 | ); 297 | name = "Gagat Example"; 298 | productName = Gagat; 299 | productReference = 9386EDDA1E577236009079B6 /* Gagat Example.app */; 300 | productType = "com.apple.product-type.application"; 301 | }; 302 | 9390063D1EE2038900BF6787 /* Gagat */ = { 303 | isa = PBXNativeTarget; 304 | buildConfigurationList = 939006481EE2038900BF6787 /* Build configuration list for PBXNativeTarget "Gagat" */; 305 | buildPhases = ( 306 | 939006391EE2038900BF6787 /* Sources */, 307 | 9390063A1EE2038900BF6787 /* Frameworks */, 308 | 9390063B1EE2038900BF6787 /* Headers */, 309 | 9390063C1EE2038900BF6787 /* Resources */, 310 | ); 311 | buildRules = ( 312 | ); 313 | dependencies = ( 314 | ); 315 | name = Gagat; 316 | productName = Gagat; 317 | productReference = 9390063E1EE2038900BF6787 /* Gagat.framework */; 318 | productType = "com.apple.product-type.framework"; 319 | }; 320 | A5C93672217C826A00776DFC /* GagatObjectiveC Example */ = { 321 | isa = PBXNativeTarget; 322 | buildConfigurationList = A5C93682217C826A00776DFC /* Build configuration list for PBXNativeTarget "GagatObjectiveC Example" */; 323 | buildPhases = ( 324 | A5C93675217C826A00776DFC /* Sources */, 325 | A5C9367A217C826A00776DFC /* Frameworks */, 326 | A5C9367C217C826A00776DFC /* Resources */, 327 | A5C936A1217C83DE00776DFC /* Embed Frameworks */, 328 | ); 329 | buildRules = ( 330 | ); 331 | dependencies = ( 332 | A5C936A0217C83DE00776DFC /* PBXTargetDependency */, 333 | A5C936D0217C924A00776DFC /* PBXTargetDependency */, 334 | ); 335 | name = "GagatObjectiveC Example"; 336 | productName = Gagat; 337 | productReference = A5C93685217C826A00776DFC /* GagatObjectiveC Example.app */; 338 | productType = "com.apple.product-type.application"; 339 | }; 340 | /* End PBXNativeTarget section */ 341 | 342 | /* Begin PBXProject section */ 343 | 9386EDD21E577236009079B6 /* Project object */ = { 344 | isa = PBXProject; 345 | attributes = { 346 | LastSwiftUpdateCheck = 0820; 347 | LastUpgradeCheck = 0930; 348 | ORGANIZATIONNAME = "Cocoabeans Software"; 349 | TargetAttributes = { 350 | 9355030A1EF2F4CA00FD428F = { 351 | CreatedOnToolsVersion = 8.3.2; 352 | LastSwiftMigration = 1000; 353 | ProvisioningStyle = Automatic; 354 | }; 355 | 9386EDD91E577236009079B6 = { 356 | CreatedOnToolsVersion = 8.2; 357 | LastSwiftMigration = 1020; 358 | ProvisioningStyle = Automatic; 359 | }; 360 | 9390063D1EE2038900BF6787 = { 361 | CreatedOnToolsVersion = 8.3.2; 362 | LastSwiftMigration = 1020; 363 | ProvisioningStyle = Automatic; 364 | }; 365 | }; 366 | }; 367 | buildConfigurationList = 9386EDD51E577236009079B6 /* Build configuration list for PBXProject "Gagat" */; 368 | compatibilityVersion = "Xcode 3.2"; 369 | developmentRegion = en; 370 | hasScannedForEncodings = 0; 371 | knownRegions = ( 372 | en, 373 | Base, 374 | ); 375 | mainGroup = 9386EDD11E577236009079B6; 376 | productRefGroup = 9386EDDB1E577236009079B6 /* Products */; 377 | projectDirPath = ""; 378 | projectRoot = ""; 379 | targets = ( 380 | 9386EDD91E577236009079B6 /* Gagat Example */, 381 | 9390063D1EE2038900BF6787 /* Gagat */, 382 | A5C93672217C826A00776DFC /* GagatObjectiveC Example */, 383 | 9355030A1EF2F4CA00FD428F /* GagatObjectiveC */, 384 | ); 385 | }; 386 | /* End PBXProject section */ 387 | 388 | /* Begin PBXResourcesBuildPhase section */ 389 | 935503091EF2F4CA00FD428F /* Resources */ = { 390 | isa = PBXResourcesBuildPhase; 391 | buildActionMask = 2147483647; 392 | files = ( 393 | ); 394 | runOnlyForDeploymentPostprocessing = 0; 395 | }; 396 | 9386EDD81E577236009079B6 /* Resources */ = { 397 | isa = PBXResourcesBuildPhase; 398 | buildActionMask = 2147483647; 399 | files = ( 400 | 939006361EE2029E00BF6787 /* LaunchScreen.storyboard in Resources */, 401 | 939006371EE2029E00BF6787 /* Main.storyboard in Resources */, 402 | 9390062E1EE2024700BF6787 /* Assets.xcassets in Resources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | 9390063C1EE2038900BF6787 /* Resources */ = { 407 | isa = PBXResourcesBuildPhase; 408 | buildActionMask = 2147483647; 409 | files = ( 410 | ); 411 | runOnlyForDeploymentPostprocessing = 0; 412 | }; 413 | A5C9367C217C826A00776DFC /* Resources */ = { 414 | isa = PBXResourcesBuildPhase; 415 | buildActionMask = 2147483647; 416 | files = ( 417 | A5C93697217C82D100776DFC /* Assets.xcassets in Resources */, 418 | A5C93698217C82D100776DFC /* LaunchScreen.storyboard in Resources */, 419 | ); 420 | runOnlyForDeploymentPostprocessing = 0; 421 | }; 422 | /* End PBXResourcesBuildPhase section */ 423 | 424 | /* Begin PBXSourcesBuildPhase section */ 425 | 935503061EF2F4CA00FD428F /* Sources */ = { 426 | isa = PBXSourcesBuildPhase; 427 | buildActionMask = 2147483647; 428 | files = ( 429 | 935503141EF2F4E900FD428F /* Gagat.swift in Sources */, 430 | ); 431 | runOnlyForDeploymentPostprocessing = 0; 432 | }; 433 | 9386EDD61E577236009079B6 /* Sources */ = { 434 | isa = PBXSourcesBuildPhase; 435 | buildActionMask = 2147483647; 436 | files = ( 437 | 939006A51EE2B15D00BF6787 /* ArchiveTableViewController.swift in Sources */, 438 | 939006A71EE2B17800BF6787 /* ArchiveTableCellView.swift in Sources */, 439 | 9390062D1EE2024700BF6787 /* AppDelegate.swift in Sources */, 440 | 939006A91EE2B36100BF6787 /* StyleableNavigationController.swift in Sources */, 441 | ); 442 | runOnlyForDeploymentPostprocessing = 0; 443 | }; 444 | 939006391EE2038900BF6787 /* Sources */ = { 445 | isa = PBXSourcesBuildPhase; 446 | buildActionMask = 2147483647; 447 | files = ( 448 | 939006501EE203BF00BF6787 /* Gagat.swift in Sources */, 449 | 939006521EE203BF00BF6787 /* TransitionCoordinator.swift in Sources */, 450 | 939006511EE203BF00BF6787 /* PessimisticPanGestureRecognizer.swift in Sources */, 451 | 9390064F1EE203BF00BF6787 /* AnimationUtilities.swift in Sources */, 452 | ); 453 | runOnlyForDeploymentPostprocessing = 0; 454 | }; 455 | A5C93675217C826A00776DFC /* Sources */ = { 456 | isa = PBXSourcesBuildPhase; 457 | buildActionMask = 2147483647; 458 | files = ( 459 | A5C936CC217C8EC800776DFC /* StyleableViewController.m in Sources */, 460 | A5C936C6217C84C500776DFC /* main.m in Sources */, 461 | A5C9369A217C82D100776DFC /* AppDelegate.m in Sources */, 462 | A5C93695217C82D100776DFC /* StyleableNavigationController.m in Sources */, 463 | ); 464 | runOnlyForDeploymentPostprocessing = 0; 465 | }; 466 | /* End PBXSourcesBuildPhase section */ 467 | 468 | /* Begin PBXTargetDependency section */ 469 | 935503161EF2F4F300FD428F /* PBXTargetDependency */ = { 470 | isa = PBXTargetDependency; 471 | target = 9390063D1EE2038900BF6787 /* Gagat */; 472 | targetProxy = 935503151EF2F4F300FD428F /* PBXContainerItemProxy */; 473 | }; 474 | 939006441EE2038900BF6787 /* PBXTargetDependency */ = { 475 | isa = PBXTargetDependency; 476 | target = 9390063D1EE2038900BF6787 /* Gagat */; 477 | targetProxy = 939006431EE2038900BF6787 /* PBXContainerItemProxy */; 478 | }; 479 | A5C936A0217C83DE00776DFC /* PBXTargetDependency */ = { 480 | isa = PBXTargetDependency; 481 | target = 9355030A1EF2F4CA00FD428F /* GagatObjectiveC */; 482 | targetProxy = A5C9369F217C83DE00776DFC /* PBXContainerItemProxy */; 483 | }; 484 | A5C936D0217C924A00776DFC /* PBXTargetDependency */ = { 485 | isa = PBXTargetDependency; 486 | target = 9390063D1EE2038900BF6787 /* Gagat */; 487 | targetProxy = A5C936CF217C924A00776DFC /* PBXContainerItemProxy */; 488 | }; 489 | /* End PBXTargetDependency section */ 490 | 491 | /* Begin PBXVariantGroup section */ 492 | 939006231EE2024700BF6787 /* LaunchScreen.storyboard */ = { 493 | isa = PBXVariantGroup; 494 | children = ( 495 | 939006241EE2024700BF6787 /* Base */, 496 | ); 497 | name = LaunchScreen.storyboard; 498 | sourceTree = ""; 499 | }; 500 | 939006251EE2024700BF6787 /* Main.storyboard */ = { 501 | isa = PBXVariantGroup; 502 | children = ( 503 | 939006261EE2024700BF6787 /* Base */, 504 | ); 505 | name = Main.storyboard; 506 | sourceTree = ""; 507 | }; 508 | A5C9368E217C82D100776DFC /* LaunchScreen.storyboard */ = { 509 | isa = PBXVariantGroup; 510 | children = ( 511 | A5C9368F217C82D100776DFC /* Base */, 512 | ); 513 | name = LaunchScreen.storyboard; 514 | sourceTree = ""; 515 | }; 516 | /* End PBXVariantGroup section */ 517 | 518 | /* Begin XCBuildConfiguration section */ 519 | 935503101EF2F4CA00FD428F /* Debug */ = { 520 | isa = XCBuildConfiguration; 521 | buildSettings = { 522 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 523 | CLANG_ENABLE_MODULES = YES; 524 | CODE_SIGN_IDENTITY = ""; 525 | CURRENT_PROJECT_VERSION = 1; 526 | DEFINES_MODULE = YES; 527 | DEVELOPMENT_TEAM = ""; 528 | DYLIB_COMPATIBILITY_VERSION = 1; 529 | DYLIB_CURRENT_VERSION = 1; 530 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 531 | INFOPLIST_FILE = GagatObjectiveC/Info.plist; 532 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 533 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 534 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 535 | PRODUCT_BUNDLE_IDENTIFIER = se.cocoabeans.GagatObjectiveC; 536 | PRODUCT_NAME = "$(TARGET_NAME)"; 537 | SKIP_INSTALL = YES; 538 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 539 | SWIFT_VERSION = 4.2; 540 | VERSIONING_SYSTEM = "apple-generic"; 541 | VERSION_INFO_PREFIX = ""; 542 | }; 543 | name = Debug; 544 | }; 545 | 935503111EF2F4CA00FD428F /* Release */ = { 546 | isa = XCBuildConfiguration; 547 | buildSettings = { 548 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 549 | CLANG_ENABLE_MODULES = YES; 550 | CODE_SIGN_IDENTITY = ""; 551 | CURRENT_PROJECT_VERSION = 1; 552 | DEFINES_MODULE = YES; 553 | DEVELOPMENT_TEAM = ""; 554 | DYLIB_COMPATIBILITY_VERSION = 1; 555 | DYLIB_CURRENT_VERSION = 1; 556 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 557 | INFOPLIST_FILE = GagatObjectiveC/Info.plist; 558 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 559 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 560 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 561 | PRODUCT_BUNDLE_IDENTIFIER = se.cocoabeans.GagatObjectiveC; 562 | PRODUCT_NAME = "$(TARGET_NAME)"; 563 | SKIP_INSTALL = YES; 564 | SWIFT_VERSION = 4.2; 565 | VERSIONING_SYSTEM = "apple-generic"; 566 | VERSION_INFO_PREFIX = ""; 567 | }; 568 | name = Release; 569 | }; 570 | 9386EDEA1E577236009079B6 /* Debug */ = { 571 | isa = XCBuildConfiguration; 572 | buildSettings = { 573 | ALWAYS_SEARCH_USER_PATHS = NO; 574 | CLANG_ANALYZER_NONNULL = YES; 575 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 576 | CLANG_CXX_LIBRARY = "libc++"; 577 | CLANG_ENABLE_MODULES = YES; 578 | CLANG_ENABLE_OBJC_ARC = YES; 579 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 580 | CLANG_WARN_BOOL_CONVERSION = YES; 581 | CLANG_WARN_COMMA = YES; 582 | CLANG_WARN_CONSTANT_CONVERSION = YES; 583 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 584 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 585 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 586 | CLANG_WARN_EMPTY_BODY = YES; 587 | CLANG_WARN_ENUM_CONVERSION = YES; 588 | CLANG_WARN_INFINITE_RECURSION = YES; 589 | CLANG_WARN_INT_CONVERSION = YES; 590 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 591 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 592 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 593 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 594 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 595 | CLANG_WARN_STRICT_PROTOTYPES = YES; 596 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 597 | CLANG_WARN_UNREACHABLE_CODE = YES; 598 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 599 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 600 | COPY_PHASE_STRIP = NO; 601 | DEBUG_INFORMATION_FORMAT = dwarf; 602 | ENABLE_STRICT_OBJC_MSGSEND = YES; 603 | ENABLE_TESTABILITY = YES; 604 | GCC_C_LANGUAGE_STANDARD = gnu99; 605 | GCC_DYNAMIC_NO_PIC = NO; 606 | GCC_NO_COMMON_BLOCKS = YES; 607 | GCC_OPTIMIZATION_LEVEL = 0; 608 | GCC_PREPROCESSOR_DEFINITIONS = ( 609 | "DEBUG=1", 610 | "$(inherited)", 611 | ); 612 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 613 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 614 | GCC_WARN_UNDECLARED_SELECTOR = YES; 615 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 616 | GCC_WARN_UNUSED_FUNCTION = YES; 617 | GCC_WARN_UNUSED_VARIABLE = YES; 618 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 619 | MTL_ENABLE_DEBUG_INFO = YES; 620 | ONLY_ACTIVE_ARCH = YES; 621 | SDKROOT = iphoneos; 622 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 623 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 624 | TARGETED_DEVICE_FAMILY = "1,2"; 625 | }; 626 | name = Debug; 627 | }; 628 | 9386EDEB1E577236009079B6 /* Release */ = { 629 | isa = XCBuildConfiguration; 630 | buildSettings = { 631 | ALWAYS_SEARCH_USER_PATHS = NO; 632 | CLANG_ANALYZER_NONNULL = YES; 633 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 634 | CLANG_CXX_LIBRARY = "libc++"; 635 | CLANG_ENABLE_MODULES = YES; 636 | CLANG_ENABLE_OBJC_ARC = YES; 637 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 638 | CLANG_WARN_BOOL_CONVERSION = YES; 639 | CLANG_WARN_COMMA = YES; 640 | CLANG_WARN_CONSTANT_CONVERSION = YES; 641 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 642 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 643 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 644 | CLANG_WARN_EMPTY_BODY = YES; 645 | CLANG_WARN_ENUM_CONVERSION = YES; 646 | CLANG_WARN_INFINITE_RECURSION = YES; 647 | CLANG_WARN_INT_CONVERSION = YES; 648 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 649 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 650 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 651 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 652 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 653 | CLANG_WARN_STRICT_PROTOTYPES = YES; 654 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 655 | CLANG_WARN_UNREACHABLE_CODE = YES; 656 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 657 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 658 | COPY_PHASE_STRIP = NO; 659 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 660 | ENABLE_NS_ASSERTIONS = NO; 661 | ENABLE_STRICT_OBJC_MSGSEND = YES; 662 | GCC_C_LANGUAGE_STANDARD = gnu99; 663 | GCC_NO_COMMON_BLOCKS = YES; 664 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 665 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 666 | GCC_WARN_UNDECLARED_SELECTOR = YES; 667 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 668 | GCC_WARN_UNUSED_FUNCTION = YES; 669 | GCC_WARN_UNUSED_VARIABLE = YES; 670 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 671 | MTL_ENABLE_DEBUG_INFO = NO; 672 | SDKROOT = iphoneos; 673 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 674 | TARGETED_DEVICE_FAMILY = "1,2"; 675 | VALIDATE_PRODUCT = YES; 676 | }; 677 | name = Release; 678 | }; 679 | 9386EDED1E577236009079B6 /* Debug */ = { 680 | isa = XCBuildConfiguration; 681 | buildSettings = { 682 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 683 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 684 | DEVELOPMENT_TEAM = ""; 685 | INFOPLIST_FILE = "Gagat Example/Info.plist"; 686 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 687 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 688 | PRODUCT_BUNDLE_IDENTIFIER = se.cocoabeans.GagatExample; 689 | PRODUCT_NAME = "$(TARGET_NAME)"; 690 | SWIFT_VERSION = 5.0; 691 | }; 692 | name = Debug; 693 | }; 694 | 9386EDEE1E577236009079B6 /* Release */ = { 695 | isa = XCBuildConfiguration; 696 | buildSettings = { 697 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 698 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 699 | DEVELOPMENT_TEAM = ""; 700 | INFOPLIST_FILE = "Gagat Example/Info.plist"; 701 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 702 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 703 | PRODUCT_BUNDLE_IDENTIFIER = se.cocoabeans.GagatExample; 704 | PRODUCT_NAME = "$(TARGET_NAME)"; 705 | SWIFT_VERSION = 5.0; 706 | }; 707 | name = Release; 708 | }; 709 | 939006491EE2038900BF6787 /* Debug */ = { 710 | isa = XCBuildConfiguration; 711 | buildSettings = { 712 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 713 | CLANG_ENABLE_MODULES = YES; 714 | CODE_SIGN_IDENTITY = ""; 715 | CURRENT_PROJECT_VERSION = 1; 716 | DEFINES_MODULE = YES; 717 | DEVELOPMENT_TEAM = ""; 718 | DYLIB_COMPATIBILITY_VERSION = 1; 719 | DYLIB_CURRENT_VERSION = 1; 720 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 721 | INFOPLIST_FILE = Gagat/Info.plist; 722 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 723 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 724 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 725 | PRODUCT_BUNDLE_IDENTIFIER = se.cocoabeans.Gagat; 726 | PRODUCT_NAME = "$(TARGET_NAME)"; 727 | SKIP_INSTALL = YES; 728 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 729 | SWIFT_VERSION = 5.0; 730 | VERSIONING_SYSTEM = "apple-generic"; 731 | VERSION_INFO_PREFIX = ""; 732 | }; 733 | name = Debug; 734 | }; 735 | 9390064A1EE2038900BF6787 /* Release */ = { 736 | isa = XCBuildConfiguration; 737 | buildSettings = { 738 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 739 | CLANG_ENABLE_MODULES = YES; 740 | CODE_SIGN_IDENTITY = ""; 741 | CURRENT_PROJECT_VERSION = 1; 742 | DEFINES_MODULE = YES; 743 | DEVELOPMENT_TEAM = ""; 744 | DYLIB_COMPATIBILITY_VERSION = 1; 745 | DYLIB_CURRENT_VERSION = 1; 746 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 747 | INFOPLIST_FILE = Gagat/Info.plist; 748 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 749 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 750 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 751 | PRODUCT_BUNDLE_IDENTIFIER = se.cocoabeans.Gagat; 752 | PRODUCT_NAME = "$(TARGET_NAME)"; 753 | SKIP_INSTALL = YES; 754 | SWIFT_VERSION = 5.0; 755 | VERSIONING_SYSTEM = "apple-generic"; 756 | VERSION_INFO_PREFIX = ""; 757 | }; 758 | name = Release; 759 | }; 760 | A5C93683217C826A00776DFC /* Debug */ = { 761 | isa = XCBuildConfiguration; 762 | buildSettings = { 763 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 764 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 765 | DEVELOPMENT_TEAM = ""; 766 | INFOPLIST_FILE = "GagatObjectiveC Example/Info.plist"; 767 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 768 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 769 | PRODUCT_BUNDLE_IDENTIFIER = se.cocoabeans.GagatObjCExample; 770 | PRODUCT_NAME = "$(TARGET_NAME)"; 771 | SWIFT_VERSION = 4.2; 772 | }; 773 | name = Debug; 774 | }; 775 | A5C93684217C826A00776DFC /* Release */ = { 776 | isa = XCBuildConfiguration; 777 | buildSettings = { 778 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 779 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 780 | DEVELOPMENT_TEAM = ""; 781 | INFOPLIST_FILE = "GagatObjectiveC Example/Info.plist"; 782 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 783 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 784 | PRODUCT_BUNDLE_IDENTIFIER = se.cocoabeans.GagatObjCExample; 785 | PRODUCT_NAME = "$(TARGET_NAME)"; 786 | SWIFT_VERSION = 4.2; 787 | }; 788 | name = Release; 789 | }; 790 | /* End XCBuildConfiguration section */ 791 | 792 | /* Begin XCConfigurationList section */ 793 | 935503121EF2F4CA00FD428F /* Build configuration list for PBXNativeTarget "GagatObjectiveC" */ = { 794 | isa = XCConfigurationList; 795 | buildConfigurations = ( 796 | 935503101EF2F4CA00FD428F /* Debug */, 797 | 935503111EF2F4CA00FD428F /* Release */, 798 | ); 799 | defaultConfigurationIsVisible = 0; 800 | defaultConfigurationName = Release; 801 | }; 802 | 9386EDD51E577236009079B6 /* Build configuration list for PBXProject "Gagat" */ = { 803 | isa = XCConfigurationList; 804 | buildConfigurations = ( 805 | 9386EDEA1E577236009079B6 /* Debug */, 806 | 9386EDEB1E577236009079B6 /* Release */, 807 | ); 808 | defaultConfigurationIsVisible = 0; 809 | defaultConfigurationName = Release; 810 | }; 811 | 9386EDEC1E577236009079B6 /* Build configuration list for PBXNativeTarget "Gagat Example" */ = { 812 | isa = XCConfigurationList; 813 | buildConfigurations = ( 814 | 9386EDED1E577236009079B6 /* Debug */, 815 | 9386EDEE1E577236009079B6 /* Release */, 816 | ); 817 | defaultConfigurationIsVisible = 0; 818 | defaultConfigurationName = Release; 819 | }; 820 | 939006481EE2038900BF6787 /* Build configuration list for PBXNativeTarget "Gagat" */ = { 821 | isa = XCConfigurationList; 822 | buildConfigurations = ( 823 | 939006491EE2038900BF6787 /* Debug */, 824 | 9390064A1EE2038900BF6787 /* Release */, 825 | ); 826 | defaultConfigurationIsVisible = 0; 827 | defaultConfigurationName = Release; 828 | }; 829 | A5C93682217C826A00776DFC /* Build configuration list for PBXNativeTarget "GagatObjectiveC Example" */ = { 830 | isa = XCConfigurationList; 831 | buildConfigurations = ( 832 | A5C93683217C826A00776DFC /* Debug */, 833 | A5C93684217C826A00776DFC /* Release */, 834 | ); 835 | defaultConfigurationIsVisible = 0; 836 | defaultConfigurationName = Release; 837 | }; 838 | /* End XCConfigurationList section */ 839 | }; 840 | rootObject = 9386EDD21E577236009079B6 /* Project object */; 841 | } 842 | -------------------------------------------------------------------------------- /Gagat.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Gagat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Gagat.xcodeproj/xcshareddata/xcschemes/Gagat.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 68 | 69 | 75 | 76 | 77 | 78 | 79 | 80 | 86 | 87 | 93 | 94 | 95 | 96 | 98 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /Gagat/AnimationUtilities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationUtilities.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-02-17. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import QuartzCore 11 | 12 | private func distance(from source: CGPoint, to destination: CGPoint) -> CGFloat { 13 | return sqrt(pow(destination.x - source.x, 2) + pow(destination.y - source.y, 2)) 14 | } 15 | 16 | private func timeRequiredToMove(from source: CGPoint, to destination: CGPoint, withVelocity velocity: CGPoint) -> TimeInterval { 17 | let distanceToMove = distance(from: source, to: destination) 18 | let velocityMagnitude = sqrt(pow(velocity.x, 2) + pow(velocity.y, 2)) 19 | let requiredTime = TimeInterval(abs(distanceToMove / velocityMagnitude)) 20 | return requiredTime 21 | } 22 | 23 | private func maximumReasonableTimeToMove(from source: CGPoint, to destination: CGPoint) -> TimeInterval { 24 | let minimumReasonableVelocity: CGFloat = 300.0 // points per second, chosen empirically 25 | let distanceToMove = distance(from: source, to: destination) 26 | return TimeInterval(distanceToMove / minimumReasonableVelocity) 27 | } 28 | 29 | func animate(_ layer: CALayer, to targetPoint: CGPoint, withVelocity velocity: CGPoint, completion: @escaping () -> Void) { 30 | let startPoint = layer.position 31 | layer.position = targetPoint 32 | 33 | let positionAnimation = CABasicAnimation(keyPath: "position") 34 | positionAnimation.duration = min( 35 | maximumReasonableTimeToMove(from: startPoint, to: targetPoint), 36 | timeRequiredToMove(from: startPoint, to: targetPoint, withVelocity: velocity) 37 | ) 38 | positionAnimation.fromValue = NSValue(cgPoint: startPoint) 39 | positionAnimation.toValue = NSValue(cgPoint: targetPoint) 40 | 41 | CATransaction.begin() 42 | CATransaction.setCompletionBlock(completion) 43 | 44 | layer.add(positionAnimation, forKey: "position") 45 | 46 | CATransaction.commit() 47 | } 48 | -------------------------------------------------------------------------------- /Gagat/Gagat.h: -------------------------------------------------------------------------------- 1 | // 2 | // Gagat.h 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-06-02. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Gagat. 12 | FOUNDATION_EXPORT double GagatVersionNumber; 13 | 14 | //! Project version string for Gagat. 15 | FOUNDATION_EXPORT const unsigned char GagatVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Gagat/Gagat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Gagat.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-02-17. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | /// A type that knows how to toggle between two alternative visual styles. 13 | public protocol GagatStyleable { 14 | /// Activates the alternative style that is currently not active. 15 | /// 16 | /// This method is called by Gagat at the start of a transition 17 | /// and at the end of a _cancelled_ transition (to revert to the 18 | /// previous style). 19 | /// 20 | /// This method is required. 21 | func toggleActiveStyle() 22 | 23 | /// Called when the style transition is about to begin. `toggleActiveStyle()` will be called just after this. 24 | /// 25 | /// This method is optional. 26 | func styleTransitionWillBegin() 27 | 28 | /// Called when the style transition ended. 29 | /// 30 | /// This method is optional. 31 | func styleTransitionDidEnd() 32 | } 33 | 34 | /// Since styleTransitionWillBegin() and styleTransitionDidEnd() aren't considered as required to implement 35 | /// the core functionality of the GagatStyleable protocol, this extension provides default (empty) implementations 36 | /// of those functions. This makes implementing them in your GagatStyleable object optional. 37 | public extension GagatStyleable { 38 | func styleTransitionWillBegin() {} 39 | func styleTransitionDidEnd() {} 40 | } 41 | 42 | public struct Gagat { 43 | 44 | /// The `Configuration` struct allows clients to configure certain 45 | /// aspects of the transition. 46 | /// 47 | /// Initialize an empty instance of the struct to use the defaults. 48 | public struct Configuration { 49 | /// Controls how much the border between the new and 50 | /// previous style is deformed when panning. The larger the 51 | /// factor is, the more the border is deformed. 52 | /// Specify a factor of 0 to entirely disable the deformation. 53 | /// 54 | /// Defaults to 1.0. 55 | public let jellyFactor: Double 56 | 57 | public init(jellyFactor: Double = 1.0) { 58 | self.jellyFactor = jellyFactor 59 | } 60 | } 61 | 62 | /// Represents a configured transition and allows clients to 63 | /// access properties that can be modified after configuration. 64 | public struct TransitionHandle { 65 | private let coordinator: TransitionCoordinator 66 | 67 | fileprivate init(coordinator: TransitionCoordinator) { 68 | self.coordinator = coordinator 69 | } 70 | 71 | /// The pan gesture recognizer that Gagat uses to trigger and drive the 72 | /// transition. 73 | /// 74 | /// You may use this property to configure the minimum or maximum number 75 | /// of required touches if you don't want to use the default two-finger 76 | /// pan. You may also use it to setup dependencies between this gesture 77 | /// recognizer and any other gesture recognizers in your application. 78 | /// 79 | /// - important: You *must not* change the gesture recognizer's delegate 80 | /// or remove targets not added by the client. 81 | public var panGestureRecognizer: UIPanGestureRecognizer { 82 | return coordinator.panGestureRecognizer 83 | } 84 | } 85 | 86 | /// Configures everything that's needed by Gagat to begin handling the transition 87 | /// in the specified window. 88 | /// 89 | /// - important: You *must* keep a reference to the `TransitionHandle` 90 | /// returned by this method even if you don't intend to access 91 | /// any of its properties. 92 | /// All Gagat-related objects will be torn down when the 93 | /// handle is deallocated, and the client will need to call 94 | /// `Gagat.configure(for:with:using:)` again to re-enable 95 | /// the Gagat transition. 96 | /// 97 | /// - note: This method shouldn't be called multiple times for the same window 98 | /// unless the returned handle has been deallocated. 99 | /// 100 | /// - parameter window: The window that the user will pan in to trigger the 101 | /// transition. 102 | /// - parameter styleableObject: An object that conforms to `GagatStyleable` and 103 | /// which is responsible for toggling to the alternative style when 104 | /// the transition is triggered or cancelled. 105 | /// - parameter configuration: The configuration to use for the transition. 106 | /// 107 | /// - returns: A new instance of `TransitionHandle`. 108 | public static func configure(for window: UIWindow, with styleableObject: GagatStyleable, using configuration: Configuration = Configuration()) -> TransitionHandle { 109 | let coordinator = TransitionCoordinator(targetView: window, styleableObject: styleableObject, configuration: configuration) 110 | return TransitionHandle(coordinator: coordinator) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Gagat/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Gagat/PessimisticPanGestureRecognizer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PessimisticPanGestureRecognizer.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-05-25. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UIKit.UIGestureRecognizerSubclass 11 | 12 | /// `PessimisticPanGestureRecognizer` is a `UIPanGestureRecognizer` subclass that 13 | /// fails earlier than the base `UIPanGestureRecognizer` class. 14 | /// More specifically it fails if the user drags fewer than the minimum number 15 | /// of required fingers past a certain threshold (10pt). 16 | /// 17 | /// In Gagat, this is used as the pan gesture recognizer that drives the 18 | /// style transition. It is required in order to let the transition work 19 | /// in harmony with any `UIScrollViews` (e.g. `UITableView`) that exist in the 20 | /// application. 21 | class PessimisticPanGestureRecognizer: UIPanGestureRecognizer { 22 | 23 | override func touchesMoved(_ touches: Set, with event: UIEvent) { 24 | let hasLessThanNumberOfRequiredTouches = (event.allTouches?.count ?? 0) < minimumNumberOfTouches 25 | let hasDraggedPastFailureThreshold = absoluteDistanceFromStartingPoint < 10.0 26 | if hasLessThanNumberOfRequiredTouches && hasDraggedPastFailureThreshold { 27 | state = .failed 28 | } else { 29 | super.touchesMoved(touches, with: event) 30 | } 31 | } 32 | 33 | private var absoluteDistanceFromStartingPoint: CGFloat { 34 | let translation = self.translation(in: view) 35 | return sqrt(pow(translation.x, 2) + pow(translation.y, 2)) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Gagat/TransitionCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransitionCoordinator.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-02-17. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class TransitionCoordinator: NSObject { 13 | 14 | fileprivate enum State { 15 | case idle 16 | case tracking 17 | case transitioning 18 | } 19 | 20 | /// The view (or window) that the transition should occur in, and 21 | /// which the pan gesture recognizer is installed in. 22 | fileprivate let targetView: UIView 23 | 24 | fileprivate let configuration: Gagat.Configuration 25 | private let styleableObject: GagatStyleable 26 | 27 | private(set) var panGestureRecognizer: PessimisticPanGestureRecognizer! 28 | 29 | fileprivate var state = State.idle 30 | 31 | init(targetView: UIView, styleableObject: GagatStyleable, configuration: Gagat.Configuration) { 32 | self.targetView = targetView 33 | self.configuration = configuration 34 | self.styleableObject = styleableObject 35 | 36 | super.init() 37 | 38 | setupPanGestureRecognizer(in: targetView) 39 | } 40 | 41 | deinit { 42 | panGestureRecognizer.view?.removeGestureRecognizer(panGestureRecognizer) 43 | } 44 | 45 | // MARK: - Pan gesture recognizer 46 | 47 | private func setupPanGestureRecognizer(in targetView: UIView) { 48 | let panGestureRecognizer = PessimisticPanGestureRecognizer(target: self, action: #selector(panRecognizerDidChange(_:))) 49 | panGestureRecognizer.maximumNumberOfTouches = 2 50 | panGestureRecognizer.minimumNumberOfTouches = 2 51 | panGestureRecognizer.delegate = self 52 | targetView.addGestureRecognizer(panGestureRecognizer) 53 | 54 | self.panGestureRecognizer = panGestureRecognizer 55 | } 56 | 57 | @objc func panRecognizerDidChange(_ panRecognizer: PessimisticPanGestureRecognizer) { 58 | switch panRecognizer.state { 59 | case .began: 60 | beginInteractiveStyleTransition(withPanRecognizer: panRecognizer) 61 | case .changed: 62 | adjustMaskLayer(basedOn: panRecognizer) 63 | case .ended, .failed: 64 | endInteractiveStyleTransition(withPanRecognizer: panRecognizer) 65 | case .cancelled: 66 | cancelInteractiveStyleTransitionWithoutAnimation() 67 | default: break 68 | } 69 | } 70 | 71 | // MARK: - Interactive style transition 72 | 73 | /// During the interactive transition, this property contains a 74 | /// snapshot of the view when it was styled with the previous style 75 | /// (i.e. the style we're transitioning _from_). 76 | /// As the transition progresses, less and less of the snapshot view 77 | /// will be visible, revealing more of the real view which is styled 78 | /// with the new style. 79 | private var previousStyleTargetViewSnapshot: UIView? 80 | 81 | /// During the interactive transition, this property contains the layer 82 | /// used to mask the contents of `previousStyleTargetViewSnapshot`. 83 | /// When the user pans, the position and path of `snapshotMaskLayer` is 84 | /// adjusted to reflect the current translation of the pan recognizer. 85 | private var snapshotMaskLayer: CAShapeLayer? 86 | 87 | private func beginInteractiveStyleTransition(withPanRecognizer panRecognizer: PessimisticPanGestureRecognizer) { 88 | 89 | // Inform our object that we're about to start a transition. 90 | styleableObject.styleTransitionWillBegin() 91 | 92 | // We snapshot the targetView before applying the new style, and make sure 93 | // it's positioned on top of all the other content. 94 | previousStyleTargetViewSnapshot = targetView.snapshotView(afterScreenUpdates: false) 95 | targetView.addSubview(previousStyleTargetViewSnapshot!) 96 | targetView.bringSubviewToFront(previousStyleTargetViewSnapshot!) 97 | 98 | // When we have the snapshot we create a new mask layer that's used to 99 | // control how much of the previous view we display as the transition 100 | // progresses. 101 | snapshotMaskLayer = CAShapeLayer() 102 | snapshotMaskLayer?.path = UIBezierPath(rect: targetView.bounds).cgPath 103 | snapshotMaskLayer?.fillColor = UIColor.black.cgColor 104 | previousStyleTargetViewSnapshot?.layer.mask = snapshotMaskLayer 105 | 106 | // Now we're free to apply the new style. This won't be visible until 107 | // the user pans more since the snapshot is displayed on top of the 108 | // actual content. 109 | styleableObject.toggleActiveStyle() 110 | 111 | // Finally we make our first adjustment to the mask layer based on the 112 | // values of the pan recognizer. 113 | adjustMaskLayer(basedOn: panRecognizer) 114 | 115 | state = .tracking 116 | } 117 | 118 | private func adjustMaskLayer(basedOn panRecognizer: PessimisticPanGestureRecognizer) { 119 | adjustMaskLayerPosition(basedOn: panRecognizer) 120 | adjustMaskLayerPath(basedOn: panRecognizer) 121 | } 122 | 123 | private func adjustMaskLayerPosition(basedOn panRecognizer: PessimisticPanGestureRecognizer) { 124 | // We need to disable implicit animations since we don't want to 125 | // animate the position change of the mask layer. 126 | CATransaction.begin() 127 | CATransaction.setDisableActions(true) 128 | 129 | let verticalTranslation = panRecognizer.translation(in: targetView).y 130 | if verticalTranslation < 0.0 { 131 | // We wan't to prevent the user from moving the mask layer out the 132 | // top of the targetView, since doing so would show the new style at 133 | // the bottom of the targetView instead. 134 | // 135 | // By resetting the translation we make sure there's no visual 136 | // delay between when the user tries to pan upwards and when they 137 | // start panning downwards again. 138 | panRecognizer.setTranslation(.zero, in: targetView) 139 | snapshotMaskLayer?.frame.origin.y = 0.0 140 | } else { 141 | // Simply move the mask layer as much as the user has panned. 142 | // Note that if we had used the _location_ of the pan recognizer 143 | // instead of the _translation_, the top of the mask layer would 144 | // follow the fingers exactly. Using the translation results in a 145 | // better user experience since the location of the mask layer is 146 | // instead relative to the distance moved, just like when moving a 147 | // piece of paper with our fingertips. 148 | snapshotMaskLayer?.frame.origin.y = verticalTranslation 149 | } 150 | 151 | CATransaction.commit() 152 | } 153 | 154 | private func adjustMaskLayerPath(basedOn panRecognizer: PessimisticPanGestureRecognizer) { 155 | let maskingPath = UIBezierPath() 156 | 157 | // Top-left corner... 158 | maskingPath.move(to: .zero) 159 | 160 | // ...arc to top-right corner... 161 | // This is all the code that is required to get the bouncy effect. 162 | // Since the control point of the quad curve depends on the velocity 163 | // of the pan recognizer, the path will "deform" more for a larger 164 | // velocity. 165 | // We don't need to do anything to animate the path back to its 166 | // non-deformed state since the pan gesture recognizer's target method 167 | // (panRecognizerDidChange(_:) in our case) is called periodically 168 | // even when the user stops moving their finger (until the velocity 169 | // reaches 0). 170 | // Note: To increase the bouncy effect, decrease the `damping` value. 171 | let damping = configuration.jellyFactor > 0.0 ? CGFloat(45.0 / configuration.jellyFactor) : 0.0 172 | let verticalOffset = damping > 0.0 ? panRecognizer.velocity(in: targetView).y / damping : 0.0 173 | let horizontalTouchLocation = panRecognizer.location(in: targetView).x 174 | maskingPath.addQuadCurve(to: CGPoint(x: targetView.bounds.maxX, y: 0.0), controlPoint: CGPoint(x: horizontalTouchLocation, y: verticalOffset)) 175 | 176 | // ...to bottom-right corner... 177 | maskingPath.addLine(to: CGPoint(x: targetView.bounds.maxX, y: targetView.bounds.maxY)) 178 | 179 | // ...to bottom-left corner... 180 | maskingPath.addLine(to: CGPoint(x: 0.0, y: targetView.bounds.maxY)) 181 | 182 | // ...and close the path. 183 | maskingPath.close() 184 | 185 | snapshotMaskLayer?.path = maskingPath.cgPath 186 | } 187 | 188 | private func endInteractiveStyleTransition(withPanRecognizer panRecognizer: PessimisticPanGestureRecognizer) { 189 | let velocity = panRecognizer.velocity(in: targetView) 190 | let translation = panRecognizer.translation(in: targetView) 191 | 192 | let isMovingDownwards = velocity.y > 0.0 193 | let hasPassedThreshold = translation.y > targetView.bounds.midY 194 | 195 | // We support both completing the transition and cancelling the transition. 196 | // The transition to the new style should be completed if the user is panning 197 | // downwards or if they've panned enough that more than half of the new view 198 | // is already shown. 199 | let shouldCompleteTransition = isMovingDownwards || hasPassedThreshold 200 | 201 | if shouldCompleteTransition { 202 | completeInteractiveStyleTransition(withVelocity: velocity) 203 | } else { 204 | cancelInteractiveStyleTransition(withVelocity: velocity) 205 | } 206 | } 207 | 208 | private func cancelInteractiveStyleTransitionWithoutAnimation() { 209 | styleableObject.toggleActiveStyle() 210 | cleanupAfterInteractiveStyleTransition() 211 | state = .idle 212 | styleableObject.styleTransitionDidEnd() 213 | } 214 | 215 | private func cancelInteractiveStyleTransition(withVelocity velocity: CGPoint) { 216 | guard let snapshotMaskLayer = snapshotMaskLayer else { 217 | return 218 | } 219 | 220 | state = .transitioning 221 | 222 | // When cancelling the transition we simply animate the mask layer to its original 223 | // location (which means that the entire previous style snapshot is shown), then 224 | // reset the style to the previous style and remove the snapshot. 225 | animate(snapshotMaskLayer, to: .zero, withVelocity: velocity) { 226 | self.styleableObject.toggleActiveStyle() 227 | self.cleanupAfterInteractiveStyleTransition() 228 | self.state = .idle 229 | self.styleableObject.styleTransitionDidEnd() 230 | } 231 | } 232 | 233 | private func completeInteractiveStyleTransition(withVelocity velocity: CGPoint) { 234 | guard let snapshotMaskLayer = snapshotMaskLayer else { 235 | return 236 | } 237 | 238 | state = .transitioning 239 | 240 | // When completing the transition we slide the mask layer down to the bottom of 241 | // the targetView and then remove the snapshot. The further down the mask layer is, 242 | // the more of the underlying view is visible. When the mask layer reaches the 243 | // bottom of the targetView, the entire underlying view will be visible so removing 244 | // the snapshot will have no visual effect. 245 | let targetLocation = CGPoint(x: 0.0, y: targetView.bounds.maxY) 246 | animate(snapshotMaskLayer, to: targetLocation, withVelocity: velocity) { 247 | self.cleanupAfterInteractiveStyleTransition() 248 | self.state = .idle 249 | self.styleableObject.styleTransitionDidEnd() 250 | } 251 | } 252 | 253 | private func cleanupAfterInteractiveStyleTransition() { 254 | self.previousStyleTargetViewSnapshot?.removeFromSuperview() 255 | self.previousStyleTargetViewSnapshot = nil 256 | self.snapshotMaskLayer = nil 257 | } 258 | } 259 | 260 | extension TransitionCoordinator: UIGestureRecognizerDelegate { 261 | private typealias Degrees = Double 262 | 263 | private enum Direction { 264 | case up, down, left, right 265 | } 266 | 267 | func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { 268 | guard let panRecognizer = gestureRecognizer as? PessimisticPanGestureRecognizer else { 269 | return true 270 | } 271 | 272 | guard state == .idle else { 273 | return false 274 | } 275 | 276 | let translation = panRecognizer.translation(in: targetView) 277 | let panningAngle: Degrees = atan2(Double(translation.y), Double(translation.x)) * 360 / (Double.pi * 2) 278 | let panningDirection = direction(for: panningAngle) 279 | return panningDirection == .down 280 | } 281 | 282 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool { 283 | // This prevents other pan gesture recognizerns (such as the one in scroll views) from interfering with the Gagat gesture. 284 | return otherGestureRecognizer is UIPanGestureRecognizer 285 | } 286 | 287 | private func direction(for angle: Degrees) -> Direction { 288 | switch angle { 289 | case 45.0...135.0: return .down 290 | case 135.0...225.0: return .left 291 | case 225.0...315.0: return .up 292 | default: return .right 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | @import UIKit; 2 | 3 | @interface AppDelegate : UIResponder 4 | 5 | @end 6 | 7 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "StyleableNavigationController.h" 3 | #import "StyleableViewController.h" 4 | @import GagatObjectiveC.Swift; 5 | 6 | @interface AppDelegate () 7 | 8 | @property (nonatomic) GGTTransitionHandle *transitionHandle; 9 | 10 | @end 11 | 12 | @implementation AppDelegate 13 | 14 | @synthesize window = _window; 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 17 | self.window = [[UIWindow alloc] init]; 18 | 19 | StyleableNavigationController *navigationController = [[StyleableNavigationController alloc] initWithRootViewController:[[StyleableViewController alloc] init]]; 20 | self.window.rootViewController = navigationController; 21 | 22 | // Configure Gagat for the applications only window using a jelly factor that is 23 | // slightly larger than the default factor of 1.0. We'll use the root view controller 24 | // as the styleable object, but you can use any object that conforms to `GGTStyleable`. 25 | // 26 | // Note: Make sure you keep a reference to the value returned from `configureForWindow:withStyleableObject:usingConfiguration:`. 27 | // If this object is deallocated then the Gagat transition will no longer work. 28 | GGTConfiguration *configuration = [[GGTConfiguration alloc] initWithJellyFactor:1.5]; 29 | self.transitionHandle = [GGTManager configureForWindow:self.window withStyleableObject:navigationController usingConfiguration:configuration]; 30 | 31 | [self.window makeKeyAndVisible]; 32 | 33 | return YES; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/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 | } -------------------------------------------------------------------------------- /GagatObjectiveC Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /GagatObjectiveC Example/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 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/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 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/StyleableNavigationController.h: -------------------------------------------------------------------------------- 1 | @import UIKit; 2 | @import GagatObjectiveC.Swift; 3 | 4 | @interface StyleableNavigationController: UINavigationController 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/StyleableNavigationController.m: -------------------------------------------------------------------------------- 1 | #import "StyleableNavigationController.h" 2 | 3 | @interface StyleableNavigationController () 4 | 5 | @property (nonatomic) BOOL useDarkMode; 6 | 7 | @end 8 | 9 | @implementation StyleableNavigationController 10 | 11 | - (void)styleTransitionWillBegin { 12 | // Do any work you might need to do once the transition has completed. 13 | UIViewController *topViewController = self.topViewController; 14 | if ([topViewController conformsToProtocol:@protocol(GGTStyleable)]) { 15 | [(id)topViewController styleTransitionWillBegin]; 16 | } 17 | } 18 | 19 | - (void)styleTransitionDidEnd { 20 | // Do any work you might need to do once the transition has completed. 21 | UIViewController *topViewController = self.topViewController; 22 | if ([topViewController conformsToProtocol:@protocol(GGTStyleable)]) { 23 | [(id)topViewController styleTransitionDidEnd]; 24 | } 25 | } 26 | 27 | - (void)toggleActiveStyle { 28 | self.useDarkMode = !self.useDarkMode; 29 | 30 | // It's up to us to get any child view controllers to 31 | // toggle their active style. In this example application we've made 32 | // the child view controller also conform to `GGTStyleable`, but 33 | // this is not required by Gagat. 34 | UIViewController *topViewController = self.topViewController; 35 | if ([topViewController conformsToProtocol:@protocol(GGTStyleable)]) { 36 | [(id)topViewController toggleActiveStyle]; 37 | } 38 | } 39 | 40 | - (void)setUseDarkMode:(BOOL)useDarkMode { 41 | _useDarkMode = useDarkMode; 42 | 43 | self.navigationBar.barStyle = useDarkMode ? UIBarStyleBlack : UIBarStyleDefault; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/StyleableViewController.h: -------------------------------------------------------------------------------- 1 | @import UIKit; 2 | @import GagatObjectiveC.Swift; 3 | 4 | @interface StyleableViewController: UIViewController 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/StyleableViewController.m: -------------------------------------------------------------------------------- 1 | #import "StyleableViewController.h" 2 | 3 | @interface StyleableViewController () 4 | 5 | @property (nonatomic) BOOL useDarkMode; 6 | @property (nonatomic) UILabel *label; 7 | 8 | @end 9 | 10 | @implementation StyleableViewController 11 | 12 | - (void)loadView { 13 | self.label = [[UILabel alloc] init]; 14 | self.label.text = @"Gagat"; 15 | self.label.textAlignment = NSTextAlignmentCenter; 16 | self.view = self.label; 17 | 18 | self.title = @"Gagat"; 19 | } 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | [self applyStyle]; 25 | } 26 | 27 | - (void)styleTransitionWillBegin { 28 | // Do any work you might need to do once the transition has completed. 29 | } 30 | 31 | - (void)styleTransitionDidEnd { 32 | // Do any work you might need to do once the transition has completed. 33 | } 34 | 35 | - (void)toggleActiveStyle { 36 | self.useDarkMode = !self.useDarkMode; 37 | } 38 | 39 | - (void)setUseDarkMode:(BOOL)useDarkMode { 40 | _useDarkMode = useDarkMode; 41 | [self applyStyle]; 42 | } 43 | 44 | - (void)applyStyle { 45 | self.view.backgroundColor = self.useDarkMode ? [UIColor colorWithWhite:0.15f alpha:1] : UIColor.whiteColor; 46 | self.label.textColor = self.useDarkMode ? UIColor.lightTextColor : UIColor.darkTextColor; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /GagatObjectiveC Example/main.m: -------------------------------------------------------------------------------- 1 | @import UIKit; 2 | #import "AppDelegate.h" 3 | 4 | int main(int argc, char * argv[]) { 5 | @autoreleasepool { 6 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /GagatObjectiveC/Gagat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GagatObjectiveC.swift 3 | // Gagat 4 | // 5 | // Created by Tim Andersson on 2017-06-12. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | // 10 | // +---------------------------------------------------------------------------------+ 11 | // | This file contains an Objective-C bridge for Gagat. It is the only component of | 12 | // | the GagatObjectiveC target and has a dependency on the original Gagat target. | 13 | // +---------------------------------------------------------------------------------+ 14 | // 15 | 16 | import Foundation 17 | import Gagat 18 | 19 | /// A type that knows how to toggle between two alternative visual styles. 20 | @objc public protocol GGTStyleable { 21 | /// Activates the alternative style that is currently not active. 22 | /// 23 | /// This method is called by Gagat at the start of a transition 24 | /// and at the end of a _cancelled_ transition (to revert to the 25 | /// previous style). 26 | @objc func toggleActiveStyle() 27 | 28 | /// Called when the style transition is about to begin. `toggleActiveStyle()` will be called just after this. 29 | @objc optional func styleTransitionWillBegin() 30 | 31 | /// Called when the style transition ended. 32 | @objc optional func styleTransitionDidEnd() 33 | } 34 | 35 | /// The `GGTConfiguration` class allows clients to configure certain 36 | /// aspects of the transition. 37 | /// 38 | /// Initialize an empty instance of the class to use the defaults. 39 | public class GGTConfiguration: NSObject { 40 | /// Controls how much the border between the new and 41 | /// previous style is deformed when panning. The larger the 42 | /// factor is, the more the border is deformed. 43 | /// Specify a factor of 0 to entirely disable the deformation. 44 | /// 45 | /// Defaults to 1.0. 46 | @objc public let jellyFactor: Double 47 | 48 | @objc public init(jellyFactor: Double = 1.0) { 49 | self.jellyFactor = jellyFactor 50 | } 51 | 52 | fileprivate var toSwiftRepresentation: Gagat.Configuration { 53 | return Gagat.Configuration(jellyFactor: jellyFactor) 54 | } 55 | } 56 | 57 | /// Represents a configured transition and allows clients to 58 | /// access properties that can be modified after configuration. 59 | public class GGTTransitionHandle: NSObject { 60 | private let wrappedHandle: Gagat.TransitionHandle 61 | 62 | fileprivate init(byWrapping handle: Gagat.TransitionHandle) { 63 | wrappedHandle = handle 64 | } 65 | 66 | /// The pan gesture recognizer that Gagat uses to trigger and drive the 67 | /// transition. 68 | /// 69 | /// You may use this property to configure the minimum or maximum number 70 | /// of required touches if you don't want to use the default two-finger 71 | /// pan. You may also use it to setup dependencies between this gesture 72 | /// recognizer and any other gesture recognizers in your application. 73 | /// 74 | /// - important: You *must not* change the gesture recognizer's delegate 75 | /// or remove targets not added by the client. 76 | @objc public var panGestureRecognizer: UIPanGestureRecognizer { 77 | return wrappedHandle.panGestureRecognizer 78 | } 79 | } 80 | 81 | public class GGTManager: NSObject { 82 | 83 | /// Configures everything that's needed by Gagat to begin handling the transition 84 | /// in the specified window. 85 | /// 86 | /// - important: You *must* keep a reference to the `GGTTransitionHandle` 87 | /// returned by this method even if you don't intend to access 88 | /// any of its properties. 89 | /// All Gagat-related objects will be torn down when the 90 | /// handle is deallocated, and the client will need to call 91 | /// `-[GGTManager configureForWindow:withStyleableObject:usingConfiguration:]` 92 | /// again to re-enable the Gagat transition. 93 | /// 94 | /// - note: This method shouldn't be called multiple times for the same window 95 | /// unless the returned handle has been deallocated. 96 | /// 97 | /// - parameter window: The window that the user will pan in to trigger the 98 | /// transition. 99 | /// - parameter styleableObject: An object that conforms to `GGTStyleable` and 100 | /// which is responsible for toggling to the alternative style when 101 | /// the transition is triggered or cancelled. 102 | /// - parameter configuration: The configuration to use for the transition. 103 | /// 104 | /// - returns: A new instance of `GGTTransitionHandle`. 105 | @objc public class func configure(forWindow window: UIWindow, withStyleableObject styleableObject: GGTStyleable, usingConfiguration configuration: GGTConfiguration = GGTConfiguration()) -> GGTTransitionHandle { 106 | let styleableObjectProxy = GagatStyleableSwiftToObjCProxy(target: styleableObject) 107 | let handle = Gagat.configure(for: window, with: styleableObjectProxy, using: configuration.toSwiftRepresentation) 108 | return GGTTransitionHandle(byWrapping: handle) 109 | } 110 | 111 | } 112 | 113 | /// `GagatStyleableSwiftToObjCProxy` is used internally to bridge the Swift-only 114 | /// `GagatStyleable` type to the `GGTStyleable` type. 115 | private struct GagatStyleableSwiftToObjCProxy: GagatStyleable { 116 | private let target: GGTStyleable 117 | 118 | init(target: GGTStyleable) { 119 | self.target = target 120 | } 121 | 122 | func toggleActiveStyle() { 123 | target.toggleActiveStyle() 124 | } 125 | 126 | func styleTransitionWillBegin() { 127 | if let targetImplementation = target.styleTransitionWillBegin { 128 | targetImplementation() 129 | } 130 | } 131 | 132 | func styleTransitionDidEnd() { 133 | if let targetImplementation = target.styleTransitionDidEnd { 134 | targetImplementation() 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /GagatObjectiveC/GagatObjectiveC.h: -------------------------------------------------------------------------------- 1 | // 2 | // GagatObjectiveC.h 3 | // GagatObjectiveC 4 | // 5 | // Created by Tim Andersson on 2017-06-15. 6 | // Copyright © 2017 Cocoabeans Software. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for GagatObjectiveC. 12 | FOUNDATION_EXPORT double GagatObjectiveCVersionNumber; 13 | 14 | //! Project version string for GagatObjectiveC. 15 | FOUNDATION_EXPORT const unsigned char GagatObjectiveCVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /GagatObjectiveC/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2017 Cocoabeans Software 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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 | ![Gagat Logo](gagat_logo.png) 2 | 3 | # What is Gagat? 4 | [![Platform](https://img.shields.io/badge/Platform-iOS-lightgrey.svg)]() 5 | [![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org) 6 | [![Objective-C compatible](https://img.shields.io/badge/Objective--C-compatible-48B3E9.svg)]() 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | [![GitHub license](https://img.shields.io/badge/License-MIT-lightgrey.svg)](https://raw.githubusercontent.com/Boerworz/Gagat/master/LICENSE) 9 | 10 | Gagat is a small Swift library that makes it easy to add a delightful, interactive way to switch between two different themes in your iOS application using a two-finger pan. The library was designed primarily for applications that support a "dark mode", and is heavily inspired by the Night Mode transition in [Castro 2](http://supertop.co/castro). 11 | 12 | 13 | 14 | ### What Gagat is not 15 | Gagat is **not** a library for styling your iOS applications. You must write all the styling logic yourself without any help from Gagat, and then call your styling code from your implementation of `GagatStyleable.toggleActiveStyle()`. If you are unsure of how to implement the styling, the Example app included with Gagat showcases one way to support different themes in an application by using [configuration models](http://www.jessesquires.com/enums-as-configs/). 16 | 17 | ## Requirements 18 | Gagat is written in Swift 5.0 and requires Xcode 10.2. It runs on iOS 10.0+ and can be used from both Swift and Objective-C clients. 19 | 20 | ## Installation 21 | 22 | You can either install Gagat using [Carthage] \(recommended) or by adding Gagat as a subproject. 23 | 24 | #### Carthage 25 | 26 | The recommended way to install Gagat is using [Carthage]. Simply add `github "Boerworz/Gagat" ~> 3.0` to your `Cartfile`, run a suitable Carthage command (such as `bootstrap`) and then add the framework(s) to your project. If you're using Gagat from Swift you only need `Gagat.framework`, but for Objective-C clients you need both `Gagat.framework` and `GagatObjectiveC.framework`. 27 | 28 | #### Adding Gagat as a subproject 29 | 30 | If you don't use [Carthage] to manage your dependencies then adding Gagat as a subproject is the easiest way to integrate Gagat. To do this simply download an [archive of the master branch](https://github.com/Boerworz/Gagat/archive/master.zip) and copy or move the entire directory to your project directory. When that's done, drag `Gagat.xcodeproj` from the Finder into your project organizer in Xcode. You should now be able to add the Gagat framework(s) as Embedded Binaries to your target. If you're using Gagat from Swift you only need `Gagat.framework`, but for Objective-C clients you need both `Gagat.framework` and `GagatObjectiveC.framework`. If you're unsure of how to do add frameworks to your project, please refer to [Apple's Technical Note on the topic](https://developer.apple.com/library/content/technotes/tn2435/_index.html#//apple_ref/doc/uid/DTS40017543-CH1-EMBED_IN_APP_SECTION). 31 | 32 | ## Usage 33 | To support Gagat in your iOS application there's two things you need to do (apart from `import`ing Gagat): 34 | 35 | #### 1. Conform to `GagatStyleable` 36 | In order for Gagat to be able to trigger the style change in your application you must provide it with an object that conforms to `GagatStyleable`. This object could be your root view controller, a specialized `StyleManager` kind of object, your app delegate, or any other object that fits your architecture. 37 | 38 | The only requirement in the `GagatStyleable` protocol is that you implement a `toggleActiveStyle()` method. When this method is invoked you should switch to the theme that is currently inactive, e.g. switch from a light theme to a dark theme or vice versa. It's not strictly a requirement, but for the best results you should attempt to have completed the style change before you return from `toggleActiveStyle()` (i.e. avoid asynchronous calls if possible). 39 | 40 | #### 2. Call `Gagat.configure(for:with:using:)` 41 | This is typically done in your implementation of `application(_:didFinishLaunchingWithOptions:)` and tells Gagat to get ready to handle the interactive transition in the specified window (probably your application's only window). The second argument is your object that conforms to `GagatStyleable`. 42 | 43 | If you want to customize properties of the transition, such as the jelly factor, pass an instance of `Gagat.Configuration` as the last argument to `Gagat.configure(for:with:using:)`, or omit it if you want to use the defaults. 44 | 45 | **Important:** In order for Gagat to work as expected you _must_ keep a reference to the value returned by `Gagat.configure(for:with:using:)` for the entire lifetime of your application (or until you want to disable Gagat). 46 | 47 | ```swift 48 | // AppDelegate.swift 49 | 50 | var window: UIWindow? 51 | var gagatTransitionHandle: Gagat.TransitionHandle! 52 | 53 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 54 | let configuration = Gagat.Configuration(jellyFactor: 1.5) 55 | let styleableViewController = window!.rootViewController as! GagatStyleable 56 | gagatTransitionHandle = Gagat.configure(for: window!, with: styleableViewController, using: configuration) 57 | 58 | return true 59 | } 60 | ``` 61 | 62 | #### You're done! 63 | 64 | You should now be able to switch between your different styles by using a two-finger downwards pan from anywhere in your application. 65 | 66 | ### About Objective-C interoperability 67 | 68 | Using Gagat from Objective-C is fundamentally the same as using it from Swift, but there are some minor differences. 69 | 70 | First, you need to make sure you've added both the `GagatObjectiveC.framework` _and_ the `Gagat.framework` to your project, and that you're importing `GagatObjectiveC` (i.e. `@import GagatObjectiveC` or `#import `). 71 | 72 | Second, the type names are slightly different: 73 | 74 | | Swift Type | Objective-C Type | 75 | | ------------------------ | --------------------- | 76 | | `Gagat` | `GGTManager` | 77 | | `GagatStyleable` | `GGTStyleable` | 78 | | `Gagat.Configuration` | `GGTConfiguration` | 79 | | `Gagat.TransitionHandle` | `GGTTransitionHandle` | 80 | 81 | ## Running the Example app 82 | Gagat includes an app that showcases what this library has to offer. To run the Example app, open [Gagat.xcodeproj](Gagat.xcodeproj) and run the _Gagat Example_ scheme. If you want to run the Example app on a device (you do!) then you'll need to select a team in the target settings for the _Gagat Example_ target. 83 | 84 | #### Podcasts in the Example app 85 | * [If I Were You](http://ifiwereyoushow.com) 86 | * [Radiolab](http://radiolab.org) 87 | * [Appodden](https://overcast.fm/itunes1005587579/appodden) 88 | * [Love + Radio](http://loveandradio.org) 89 | * [Nancy](https://www.wnyc.org/shows/nancy) 90 | 91 | ## Apps that use Gagat 92 | If you're on your iPhone or iPad and want to try Gagat, download one of the following apps. They all use Gagat! 93 | 94 | * [Cascable](https://cascable.se) 95 | * [Push Me](https://pushme.jagcesar.se) 96 | * [Bitbook](https://bitbookapp.com) 97 | 98 | _Want your app to be listed here? Please create a pull request that adds it to the end of the list._ 99 | 100 | ## Credits 101 | The Gagat logo was created by [Johan Rothoff Andersson](http://www.johanrothoff.com). 102 | 103 | ## License 104 | 105 | The MIT License (MIT) 106 | 107 | Copyright 2017 Cocoabeans Software 108 | 109 | 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: 110 | 111 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 112 | 113 | 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. 114 | 115 | [Carthage]: https://github.com/Carthage/Carthage -------------------------------------------------------------------------------- /gagat_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Boerworz/Gagat/2f61038d7eddb156636045c749877e56b1f51b12/gagat_example.gif -------------------------------------------------------------------------------- /gagat_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Boerworz/Gagat/2f61038d7eddb156636045c749877e56b1f51b12/gagat_logo.png --------------------------------------------------------------------------------