├── .gitignore ├── Configs ├── UIWindowTransitions.plist └── UIWindowTransitionsTests.plist ├── ExampleApp ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.swift └── ViewController2.swift ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── UIWindowTransitions │ └── UIWindowTransitions.swift ├── Tests ├── LinuxMain.swift └── UIWindowTransitionsTests │ └── UIWindowTransitionsTests.swift ├── UIWindowTransitions.podspec ├── UIWindowTransitions.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── daniele.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── UIWindowTransitions-iOS.xcscheme └── xcuserdata │ └── daniele.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── UIWindowTransitions └── .gitignore ├── UIWindowTransitions_Demo_Animation.gif └── banner.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | xcshareddata 3 | *.xcuserstate 4 | -------------------------------------------------------------------------------- /Configs/UIWindowTransitions.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 | 0.1.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 0 23 | NSHumanReadableCopyright 24 | Copyright © 2017 Daniele Margutti. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Configs/UIWindowTransitionsTests.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ExampleApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ExampleApp 4 | // 5 | // Created by danielemargutti on 31/10/2017. 6 | // Copyright © 2017 Daniele Margutti. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ExampleApp/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 | } -------------------------------------------------------------------------------- /ExampleApp/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 | -------------------------------------------------------------------------------- /ExampleApp/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 36 | 47 | 58 | 69 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 149 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /ExampleApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ExampleApp/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ExampleApp 4 | // 5 | // Created by danielemargutti on 31/10/2017. 6 | // Copyright © 2017 Daniele Margutti. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UIWindowTransitions 11 | 12 | class ViewController: UIViewController { 13 | 14 | @IBOutlet public var button_top: UIButton? 15 | @IBOutlet public var button_bottom: UIButton? 16 | @IBOutlet public var button_left: UIButton? 17 | @IBOutlet public var button_right: UIButton? 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | // Do any additional setup after loading the view, typically from a nib. 22 | 23 | [button_top,button_bottom,button_left,button_right].forEach { 24 | $0?.tint() 25 | } 26 | } 27 | 28 | override func didReceiveMemoryWarning() { 29 | super.didReceiveMemoryWarning() 30 | // Dispose of any resources that can be recreated. 31 | } 32 | 33 | 34 | @IBAction func tapButton(_ sender: UIButton) { 35 | let wnd = UIApplication.shared.keyWindow 36 | 37 | let u = UIStoryboard(name: "Main", bundle: Bundle.main) 38 | let vc = u.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController 39 | 40 | let options = UIWindow.TransitionOptions() 41 | switch sender.tag { 42 | case 0: 43 | options.direction = .toRight 44 | case 1: 45 | options.direction = .toLeft 46 | case 2: 47 | options.direction = .toTop 48 | case 3: 49 | options.direction = .toBottom 50 | default: 51 | break 52 | } 53 | wnd?.set(rootViewController: vc, options: options, nil) 54 | } 55 | 56 | } 57 | 58 | public extension UIButton { 59 | 60 | func tint() { 61 | self.layer.borderColor = UIColor.white.cgColor 62 | self.layer.borderWidth = 1.0 63 | self.layer.cornerRadius = 4 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /ExampleApp/ViewController2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController2.swift 3 | // ExampleApp 4 | // 5 | // Created by danielemargutti on 31/10/2017. 6 | // Copyright © 2017 Daniele Margutti. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public class ViewController2: UIViewController { 13 | 14 | @IBOutlet public var button_reset: UIButton? 15 | 16 | @IBAction func tapButton(_ sender: UIButton) { 17 | let u = UIStoryboard(name: "Main", bundle: Bundle.main) 18 | let vc = u.instantiateViewController(withIdentifier: "ViewController") 19 | 20 | let opt = UIWindow.TransitionOptions(direction: .toLeft, style: .easeInOut) 21 | //opt.background = UIWindow.TransitionOptions.Background.solidColor(UIColor.red) 22 | opt.duration = 0.25 23 | UIApplication.shared.keyWindow?.set(rootViewController: vc, options: opt, nil) 24 | } 25 | 26 | public override func viewDidLoad() { 27 | super.viewDidLoad() 28 | button_reset!.tint() 29 | self.navigationItem.title = "Navigation Bar" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Daniele Margutti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "UIWindowTransitions", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "UIWindowTransitions", 12 | targets: ["UIWindowTransitions"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 21 | .target( 22 | name: "UIWindowTransitions", 23 | dependencies: []), 24 | .testTarget( 25 | name: "UIWindowTransitionsTests", 26 | dependencies: ["UIWindowTransitions"]), 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | UIWindowTransitions 3 |

4 | 5 |

Animate UIWindow's rootViewController transitions

6 | 7 | This is a small project used to demostrate how to implement UIWindow's `rootViewController` transitions with a little piece of code. 8 | You can found the original article for [this article here](http://danielemargutti.com/animate-uiwindows-rootviewcontroller-transitions). 9 | 10 | ## Motivation 11 | 12 | Sometimes during the lifecycle of an application you may need to change the `rootViewController` of your main `UIWindow` ; a typical case maybe the transition between the initial on-boarding and the home of the app (ie. a `UITabBarController` ). 13 | 14 | In order to handle this edge case you may want to create a top controller (typically an `UINavigationController` with invisible navigation bar) which enable you to push your new container using a standard push animation. 15 | 16 | While basically it works fine, the addition of a container used only to handle this single operation is a bit awful to see. 17 | 18 | A way better solution is to apply an animated transition (push/pop or slide) to the `rootViewController` set so you will get an animate switch from the current view controller to the new one. 19 | 20 | In fact, even if not exposed, you can and its really simple to accomplish via `CoreAnimation` and `CATransition` . 21 | 22 | The following code implements a new function as extension of `UIWindow` class; it takes two arguments: the destination controller and options. 23 | 24 | As you may imagine the destination controller is the controller you want to set as new `rootViewController`, while options is struct used to group some typical CoreAnimation settings: the animation direction (pop/push/slide from top or bottom), the animation curve (linear, ease in/out), the duration of the animation (by default is 0.25s) and an optional UIView instance used to fade in/out between the old and new view. 25 | 26 | ## Known Issue & Fix 27 | 28 | The 1.0.0 fixes and issue with iOS currently opened as [radar here](http://www.openradar.me/21404408). This fix was created by voltbank in his repository [ReplaceRootViewController](https://github.com/voltbank/ReplaceRootViewController) forked from this project. 29 | 30 | ## ❤️ Your Support 31 | 32 | *Hi fellow developer!* 33 | You know, maintaing and developing tools consumes resources and time. While I enjoy making them **your support is foundamental to allow me continue its development**. 34 | 35 | If you are using SwiftLocation or any other of my creations please consider the following options: 36 | 37 | - [**Make a donation with PayPal**](https://www.paypal.com/paypalme/danielemargutti/20) 38 | - [**Become a Sponsor**](https://github.com/sponsors/malcommac) 39 | 40 | - [Follow Me](https://github.com/malcommac) 41 | - 42 | ## How to use it 43 | 44 | Once implemented you can use the only function exposed to all UIWindow instances called 45 | ```swift 46 | func set(rootViewController newRootViewController: UIViewController, options: TransitionOptions = TransitionOptions(), _ completion:((Bool) -> Void)? = nil) 47 | ``` 48 | 49 | where: 50 | 51 | * `controller`: the destination view controller to set as new `rootViewController` 52 | * `options`: a `TransitionOptions` with the following properties: 53 | * `duration`: duration of the animation (expressed in `TimeInterval`, seconds, default is `0.25`) 54 | * `direction`: direction of the transition (`toRight`,`toLeft`,`toTop`,`toBottom`, default is `.toRight`) 55 | * `style`: animation curve (`linear`,`easeIn`,`easeOut`,`easeInOut`, default is `.linear`) 56 | * `background`: background view set for fade in-out into the old/new controller (by default is `nil`). 57 | * `completion`: completion callback to receive info when transition did completes. 58 | 59 | The following code change the ``rootViewController`` using a ease out - slide up animation of 0.4 seconds. 60 | 61 | ```swift 62 | let wnd = UIApplication.shared.keyWindow 63 | var options = UIWindow.TransitionOptions() 64 | options.direction = .toTop 65 | options.duration = 0.4 66 | options.style = .easeOut 67 | wnd?.set(rootViewController(newVC, options: options) 68 | ``` 69 | 70 | If you just need of a simple push (like `UINavigationController`'s push) you can call it without the `options` arg: 71 | 72 | ```swift 73 | wnd?.set(rootViewController(newVC) 74 | ``` 75 | 76 | ## Demo App 77 | 78 | The following demo is available inside the project's demo application. 79 | 80 | ![](https://github.com/malcommac/UIWindowTransitions/blob/master/UIWindowTransitions_Demo_Animation.gif) 81 | 82 | ## Installation 83 | You can install UIWindowTransitions using CocoaPods, Carthage and Swift package manager 84 | 85 | `pod 'UIWindowTransitions'` 86 | 87 | ### CocoaPods 88 | 89 | ```ruby 90 | use_frameworks! 91 | pod 'UIWindowTransitions' 92 | ``` 93 | 94 | ### Swift Package Manager 95 | Add UIWindowTransitions as dependency in your `Package.swift` 96 | 97 | ```swift 98 | import PackageDescription 99 | 100 | let package = Package(name: "YourPackage", 101 | dependencies: [ 102 | .Package(url: "https://github.com/malcommac/UIWindowTransitions.git", majorVersion: 0), 103 | ] 104 | ) 105 | ``` 106 | 107 | ## Contributing 108 | 109 | - If you **need help** or you'd like to **ask a general question**, open an issue. 110 | - If you **found a bug**, open an issue. 111 | - If you **have a feature request**, open an issue. 112 | - If you **want to contribute**, submit a pull request. 113 | 114 | ## Copyright & Acknowledgements 115 | 116 | UIWindowTransitions is currently owned and maintained by Daniele Margutti. 117 | You can follow me on Twitter [@danielemargutti](http://twitter.com/danielemargutti). 118 | My web site is [https://www.danielemargutti.com](https://www.danielemargutti.com) 119 | 120 | This software is licensed under [MIT License](LICENSE.md). 121 | 122 | ***Follow me on:*** 123 | - 💼 [Linkedin](https://www.linkedin.com/in/danielemargutti/) 124 | - 🐦 [Twitter](https://twitter.com/danielemargutti) -------------------------------------------------------------------------------- /Sources/UIWindowTransitions/UIWindowTransitions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIWindowTransitions.swift 3 | // 4 | // Copyright (c) 2020 Daniele Margutti (hello@danielemargutti.com). 5 | // Additional work by sushant to fix https://stackoverflow.com/a/27153956/8923019 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | 26 | import Foundation 27 | import UIKit 28 | 29 | public extension UIWindow { 30 | 31 | /// Transition Options 32 | class TransitionOptions: NSObject, CAAnimationDelegate { 33 | 34 | /// Curve of animation 35 | /// 36 | /// - linear: linear 37 | /// - easeIn: ease in 38 | /// - easeOut: ease out 39 | /// - easeInOut: ease in - ease out 40 | public enum Curve { 41 | case linear 42 | case easeIn 43 | case easeOut 44 | case easeInOut 45 | 46 | /// Return the media timing function associated with curve 47 | internal var function: CAMediaTimingFunction { 48 | let key: String! 49 | switch self { 50 | case .linear: key = convertFromCAMediaTimingFunctionName(CAMediaTimingFunctionName.linear) 51 | case .easeIn: key = convertFromCAMediaTimingFunctionName(CAMediaTimingFunctionName.easeIn) 52 | case .easeOut: key = convertFromCAMediaTimingFunctionName(CAMediaTimingFunctionName.easeOut) 53 | case .easeInOut: key = convertFromCAMediaTimingFunctionName(CAMediaTimingFunctionName.easeInEaseOut) 54 | } 55 | return CAMediaTimingFunction(name: convertToCAMediaTimingFunctionName(key)) 56 | } 57 | } 58 | 59 | /// Direction of the animation 60 | /// 61 | /// - fade: fade to new controller 62 | /// - toTop: slide from bottom to top 63 | /// - toBottom: slide from top to bottom 64 | /// - toLeft: pop to left 65 | /// - toRight: push to right 66 | public enum Direction { 67 | case fade 68 | case toTop 69 | case toBottom 70 | case toLeft 71 | case toRight 72 | 73 | /// Return the associated transition 74 | /// 75 | /// - Returns: transition 76 | internal func transition() -> CATransition { 77 | let transition = CATransition() 78 | transition.type = CATransitionType.push 79 | switch self { 80 | case .fade: 81 | transition.type = CATransitionType.fade 82 | transition.subtype = nil 83 | case .toLeft: 84 | transition.subtype = CATransitionSubtype.fromLeft 85 | case .toRight: 86 | transition.subtype = CATransitionSubtype.fromRight 87 | case .toTop: 88 | transition.subtype = CATransitionSubtype.fromTop 89 | case .toBottom: 90 | transition.subtype = CATransitionSubtype.fromBottom 91 | } 92 | return transition 93 | } 94 | } 95 | 96 | /// Background of the transition 97 | /// - snapshot: snapshot of current root view controller 98 | /// - solidColor: solid color 99 | /// - customView: custom view 100 | public enum Background { 101 | case snapshot 102 | case solidColor(_: UIColor) 103 | case customView(_: UIView) 104 | } 105 | 106 | /// Duration of the animation (default is 0.20s) 107 | public var duration: TimeInterval = 0.20 108 | 109 | /// Direction of the transition (default is `toRight`) 110 | public var direction: TransitionOptions.Direction = .toRight 111 | 112 | /// Style of the transition (default is `linear`) 113 | public var style: TransitionOptions.Curve = .linear 114 | 115 | /// Background of the transition (default is `nil`) 116 | public var background: TransitionOptions.Background? = nil 117 | 118 | public var completionBlock: ((Bool) -> Void)? = nil 119 | 120 | weak var previousViewController: UIViewController? 121 | 122 | /// Initialize a new options object with given direction and curve 123 | /// 124 | /// - Parameters: 125 | /// - direction: direction 126 | /// - style: style 127 | public init(direction: TransitionOptions.Direction = .toRight, style: TransitionOptions.Curve = .linear) { 128 | self.direction = direction 129 | self.style = style 130 | } 131 | 132 | /// Return the animation to perform for given options object 133 | internal var animation: CATransition { 134 | let transition = self.direction.transition() 135 | transition.duration = self.duration 136 | transition.timingFunction = self.style.function 137 | transition.delegate = self 138 | 139 | return transition 140 | } 141 | 142 | public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { 143 | let oldNavigationController = previousViewController as? UINavigationController 144 | oldNavigationController?.viewControllers = []//This is a tempoarary hack - not sure why nav controllers arent freed up. 145 | 146 | completionBlock?(flag) 147 | } 148 | } 149 | 150 | 151 | /// Fix for http://stackoverflow.com/a/27153956/849645 152 | func set(rootViewController newRootViewController: UIViewController, options: TransitionOptions = TransitionOptions(), _ completion:((Bool) -> Void)? = nil) { 153 | 154 | let previousViewController = rootViewController 155 | 156 | layer.add(options.animation, forKey: kCATransition) 157 | options.completionBlock = completion 158 | options.previousViewController = rootViewController 159 | 160 | rootViewController = newRootViewController 161 | 162 | // Update status bar appearance using the new view controllers appearance - animate if needed 163 | if UIView.areAnimationsEnabled { 164 | UIView.animate(withDuration: CATransaction.animationDuration()) { 165 | newRootViewController.setNeedsStatusBarAppearanceUpdate() 166 | } 167 | } else { 168 | newRootViewController.setNeedsStatusBarAppearanceUpdate() 169 | } 170 | 171 | if #available(iOS 13.0, *) { 172 | // In iOS 13 we don't want to remove the transition view as it'll create a blank screen 173 | } else { 174 | // The presenting view controllers view doesn't get removed from the window as its currently transistioning and presenting a view controller 175 | if let transitionViewClass = NSClassFromString("UITransitionView") { 176 | for subview in subviews where subview.isKind(of: transitionViewClass) { 177 | subview.removeFromSuperview() 178 | } 179 | } 180 | } 181 | if let previousViewController = previousViewController { 182 | // Allow the view controller to be deallocated 183 | previousViewController.dismiss(animated: false) { 184 | // Remove the root view in case its still showing 185 | previousViewController.view.removeFromSuperview() 186 | } 187 | } 188 | } 189 | } 190 | 191 | // Helper function inserted by Swift 4.2 migrator. 192 | fileprivate func convertFromCAMediaTimingFunctionName(_ input: CAMediaTimingFunctionName) -> String { 193 | return input.rawValue 194 | } 195 | 196 | // Helper function inserted by Swift 4.2 migrator. 197 | fileprivate func convertToCAMediaTimingFunctionName(_ input: String) -> CAMediaTimingFunctionName { 198 | return CAMediaTimingFunctionName(rawValue: input) 199 | } 200 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import UIWindowTransitionsTests 3 | 4 | XCTMain([ 5 | testCase(UIWindowTransitionsTests.allTests), 6 | ]) 7 | -------------------------------------------------------------------------------- /Tests/UIWindowTransitionsTests/UIWindowTransitionsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIWindowTransitionsTests.swift 3 | // Daniele Margutti 4 | // 5 | // Created by Daniele Margutti on 31/10/2017. 6 | // Copyright © 2017 Daniele Margutti. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import UIWindowTransitions 12 | 13 | class UIWindowTransitionsTests: XCTestCase { 14 | func testExample() { 15 | // This is an example of a functional test case. 16 | // Use XCTAssert and related functions to verify your tests produce the correct results. 17 | //// XCTAssertEqual(UIWindowTransitions().text, "Hello, World!") 18 | } 19 | 20 | static var allTests = [ 21 | ("testExample", testExample), 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /UIWindowTransitions.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "UIWindowTransitions" 3 | s.version = "1.0.0" 4 | s.summary = "Animated transitions for UIWindow's rootViewController property" 5 | s.description = <<-DESC 6 | This library allows to execute animated transition when changing the UIWindow's rootViewController property. Animations are done using CoreAnimation. 7 | DESC 8 | s.homepage = "https://github.com/malcommac/UIWindowTransitions" 9 | s.license = { :type => "MIT", :file => "LICENSE" } 10 | s.author = { "Daniele Margutti" => "me@danielemargutti.com" } 11 | s.social_media_url = "https://twitter.com/danielemargutti" 12 | s.ios.deployment_target = "11.0" 13 | s.source = { :git => "https://github.com/malcommac/UIWindowTransitions.git", :tag => s.version.to_s } 14 | s.source_files = "Sources/**/*.swift" 15 | s.frameworks = "Foundation" 16 | s.swift_versions = ['5.0', '5.1', '5.3'] 17 | end 18 | -------------------------------------------------------------------------------- /UIWindowTransitions.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 47; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 640CE0C51FA857C800CF5222 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640CE0C41FA857C800CF5222 /* AppDelegate.swift */; }; 11 | 640CE0C71FA857C800CF5222 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640CE0C61FA857C800CF5222 /* ViewController.swift */; }; 12 | 640CE0CA1FA857C800CF5222 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 640CE0C81FA857C800CF5222 /* Main.storyboard */; }; 13 | 640CE0CC1FA857C800CF5222 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 640CE0CB1FA857C800CF5222 /* Assets.xcassets */; }; 14 | 640CE0CF1FA857C800CF5222 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 640CE0CD1FA857C800CF5222 /* LaunchScreen.storyboard */; }; 15 | 640CE0D91FA86D7E00CF5222 /* UIWindowTransitions.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* UIWindowTransitions.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | 640CE0DB1FA87BCF00CF5222 /* ViewController2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640CE0DA1FA87BCF00CF5222 /* ViewController2.swift */; }; 17 | 64FB68E81FCAF77C0092C430 /* UIWindowTransitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FB68E71FCAF77C0092C430 /* UIWindowTransitions.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 640CE0D61FA86D5A00CF5222 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 52D6D97B1BEFF229002C0205; 26 | remoteInfo = "UIWindowTransitions-iOS"; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXCopyFilesBuildPhase section */ 31 | 640CE0D81FA86D7200CF5222 /* Copy Frameworks */ = { 32 | isa = PBXCopyFilesBuildPhase; 33 | buildActionMask = 2147483647; 34 | dstPath = ""; 35 | dstSubfolderSpec = 10; 36 | files = ( 37 | 640CE0D91FA86D7E00CF5222 /* UIWindowTransitions.framework in Copy Frameworks */, 38 | ); 39 | name = "Copy Frameworks"; 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXCopyFilesBuildPhase section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 52D6D97C1BEFF229002C0205 /* UIWindowTransitions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = UIWindowTransitions.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 640CE0C21FA857C800CF5222 /* ExampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 640CE0C41FA857C800CF5222 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 48 | 640CE0C61FA857C800CF5222 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 49 | 640CE0C91FA857C800CF5222 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 50 | 640CE0CB1FA857C800CF5222 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 51 | 640CE0CE1FA857C800CF5222 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | 640CE0D01FA857C800CF5222 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 640CE0DA1FA87BCF00CF5222 /* ViewController2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController2.swift; sourceTree = ""; }; 54 | 64FB68E71FCAF77C0092C430 /* UIWindowTransitions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UIWindowTransitions.swift; path = UIWindowTransitions/UIWindowTransitions.swift; sourceTree = ""; }; 55 | 8933C7891EB5B82A000D00A4 /* UIWindowTransitionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIWindowTransitionsTests.swift; sourceTree = ""; }; 56 | AD2FAA261CD0B6D800659CF4 /* UIWindowTransitions.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = UIWindowTransitions.plist; sourceTree = ""; }; 57 | AD2FAA281CD0B6E100659CF4 /* UIWindowTransitionsTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = UIWindowTransitionsTests.plist; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 52D6D9781BEFF229002C0205 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | 640CE0BF1FA857C800CF5222 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 52D6D9721BEFF229002C0205 = { 79 | isa = PBXGroup; 80 | children = ( 81 | 8933C7811EB5B7E0000D00A4 /* Sources */, 82 | 8933C7831EB5B7EB000D00A4 /* Tests */, 83 | 52D6D99C1BEFF38C002C0205 /* Configs */, 84 | 640CE0C31FA857C800CF5222 /* ExampleApp */, 85 | 52D6D97D1BEFF229002C0205 /* Products */, 86 | ); 87 | sourceTree = ""; 88 | }; 89 | 52D6D97D1BEFF229002C0205 /* Products */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 52D6D97C1BEFF229002C0205 /* UIWindowTransitions.framework */, 93 | 640CE0C21FA857C800CF5222 /* ExampleApp.app */, 94 | ); 95 | name = Products; 96 | sourceTree = ""; 97 | }; 98 | 52D6D99C1BEFF38C002C0205 /* Configs */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | DD7502721C68FC1B006590AF /* Frameworks */, 102 | DD7502731C68FC20006590AF /* Tests */, 103 | ); 104 | path = Configs; 105 | sourceTree = ""; 106 | }; 107 | 640CE0C31FA857C800CF5222 /* ExampleApp */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 640CE0C41FA857C800CF5222 /* AppDelegate.swift */, 111 | 640CE0C61FA857C800CF5222 /* ViewController.swift */, 112 | 640CE0DA1FA87BCF00CF5222 /* ViewController2.swift */, 113 | 640CE0C81FA857C800CF5222 /* Main.storyboard */, 114 | 640CE0CB1FA857C800CF5222 /* Assets.xcassets */, 115 | 640CE0CD1FA857C800CF5222 /* LaunchScreen.storyboard */, 116 | 640CE0D01FA857C800CF5222 /* Info.plist */, 117 | ); 118 | path = ExampleApp; 119 | sourceTree = ""; 120 | }; 121 | 8933C7811EB5B7E0000D00A4 /* Sources */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 64FB68E71FCAF77C0092C430 /* UIWindowTransitions.swift */, 125 | ); 126 | path = Sources; 127 | sourceTree = ""; 128 | }; 129 | 8933C7831EB5B7EB000D00A4 /* Tests */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 8933C7891EB5B82A000D00A4 /* UIWindowTransitionsTests.swift */, 133 | ); 134 | name = Tests; 135 | path = Tests/UIWindowTransitionsTests; 136 | sourceTree = ""; 137 | }; 138 | DD7502721C68FC1B006590AF /* Frameworks */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | AD2FAA261CD0B6D800659CF4 /* UIWindowTransitions.plist */, 142 | ); 143 | name = Frameworks; 144 | sourceTree = ""; 145 | }; 146 | DD7502731C68FC20006590AF /* Tests */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | AD2FAA281CD0B6E100659CF4 /* UIWindowTransitionsTests.plist */, 150 | ); 151 | name = Tests; 152 | sourceTree = ""; 153 | }; 154 | /* End PBXGroup section */ 155 | 156 | /* Begin PBXHeadersBuildPhase section */ 157 | 52D6D9791BEFF229002C0205 /* Headers */ = { 158 | isa = PBXHeadersBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | ); 162 | runOnlyForDeploymentPostprocessing = 0; 163 | }; 164 | /* End PBXHeadersBuildPhase section */ 165 | 166 | /* Begin PBXNativeTarget section */ 167 | 52D6D97B1BEFF229002C0205 /* UIWindowTransitions-iOS */ = { 168 | isa = PBXNativeTarget; 169 | buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "UIWindowTransitions-iOS" */; 170 | buildPhases = ( 171 | 52D6D9771BEFF229002C0205 /* Sources */, 172 | 52D6D9781BEFF229002C0205 /* Frameworks */, 173 | 52D6D9791BEFF229002C0205 /* Headers */, 174 | 52D6D97A1BEFF229002C0205 /* Resources */, 175 | ); 176 | buildRules = ( 177 | ); 178 | dependencies = ( 179 | ); 180 | name = "UIWindowTransitions-iOS"; 181 | productName = UIWindowTransitions; 182 | productReference = 52D6D97C1BEFF229002C0205 /* UIWindowTransitions.framework */; 183 | productType = "com.apple.product-type.framework"; 184 | }; 185 | 640CE0C11FA857C800CF5222 /* ExampleApp */ = { 186 | isa = PBXNativeTarget; 187 | buildConfigurationList = 640CE0D11FA857C800CF5222 /* Build configuration list for PBXNativeTarget "ExampleApp" */; 188 | buildPhases = ( 189 | 640CE0BE1FA857C800CF5222 /* Sources */, 190 | 640CE0BF1FA857C800CF5222 /* Frameworks */, 191 | 640CE0C01FA857C800CF5222 /* Resources */, 192 | 640CE0D81FA86D7200CF5222 /* Copy Frameworks */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | 640CE0D71FA86D5A00CF5222 /* PBXTargetDependency */, 198 | ); 199 | name = ExampleApp; 200 | productName = ExampleApp; 201 | productReference = 640CE0C21FA857C800CF5222 /* ExampleApp.app */; 202 | productType = "com.apple.product-type.application"; 203 | }; 204 | /* End PBXNativeTarget section */ 205 | 206 | /* Begin PBXProject section */ 207 | 52D6D9731BEFF229002C0205 /* Project object */ = { 208 | isa = PBXProject; 209 | attributes = { 210 | LastSwiftUpdateCheck = 0900; 211 | LastUpgradeCheck = 1220; 212 | ORGANIZATIONNAME = "Daniele Margutti"; 213 | TargetAttributes = { 214 | 52D6D97B1BEFF229002C0205 = { 215 | CreatedOnToolsVersion = 7.1; 216 | LastSwiftMigration = 1220; 217 | }; 218 | 640CE0C11FA857C800CF5222 = { 219 | CreatedOnToolsVersion = 9.0.1; 220 | DevelopmentTeam = E5DU3FA699; 221 | LastSwiftMigration = 1220; 222 | ProvisioningStyle = Automatic; 223 | }; 224 | }; 225 | }; 226 | buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "UIWindowTransitions" */; 227 | compatibilityVersion = "Xcode 6.3"; 228 | developmentRegion = en; 229 | hasScannedForEncodings = 0; 230 | knownRegions = ( 231 | en, 232 | Base, 233 | ); 234 | mainGroup = 52D6D9721BEFF229002C0205; 235 | productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */; 236 | projectDirPath = ""; 237 | projectRoot = ""; 238 | targets = ( 239 | 52D6D97B1BEFF229002C0205 /* UIWindowTransitions-iOS */, 240 | 640CE0C11FA857C800CF5222 /* ExampleApp */, 241 | ); 242 | }; 243 | /* End PBXProject section */ 244 | 245 | /* Begin PBXResourcesBuildPhase section */ 246 | 52D6D97A1BEFF229002C0205 /* Resources */ = { 247 | isa = PBXResourcesBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | 640CE0C01FA857C800CF5222 /* Resources */ = { 254 | isa = PBXResourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 640CE0CF1FA857C800CF5222 /* LaunchScreen.storyboard in Resources */, 258 | 640CE0CC1FA857C800CF5222 /* Assets.xcassets in Resources */, 259 | 640CE0CA1FA857C800CF5222 /* Main.storyboard in Resources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | /* End PBXResourcesBuildPhase section */ 264 | 265 | /* Begin PBXSourcesBuildPhase section */ 266 | 52D6D9771BEFF229002C0205 /* Sources */ = { 267 | isa = PBXSourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | 64FB68E81FCAF77C0092C430 /* UIWindowTransitions.swift in Sources */, 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | 640CE0BE1FA857C800CF5222 /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | 640CE0C71FA857C800CF5222 /* ViewController.swift in Sources */, 279 | 640CE0C51FA857C800CF5222 /* AppDelegate.swift in Sources */, 280 | 640CE0DB1FA87BCF00CF5222 /* ViewController2.swift in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | /* End PBXSourcesBuildPhase section */ 285 | 286 | /* Begin PBXTargetDependency section */ 287 | 640CE0D71FA86D5A00CF5222 /* PBXTargetDependency */ = { 288 | isa = PBXTargetDependency; 289 | target = 52D6D97B1BEFF229002C0205 /* UIWindowTransitions-iOS */; 290 | targetProxy = 640CE0D61FA86D5A00CF5222 /* PBXContainerItemProxy */; 291 | }; 292 | /* End PBXTargetDependency section */ 293 | 294 | /* Begin PBXVariantGroup section */ 295 | 640CE0C81FA857C800CF5222 /* Main.storyboard */ = { 296 | isa = PBXVariantGroup; 297 | children = ( 298 | 640CE0C91FA857C800CF5222 /* Base */, 299 | ); 300 | name = Main.storyboard; 301 | sourceTree = ""; 302 | }; 303 | 640CE0CD1FA857C800CF5222 /* LaunchScreen.storyboard */ = { 304 | isa = PBXVariantGroup; 305 | children = ( 306 | 640CE0CE1FA857C800CF5222 /* Base */, 307 | ); 308 | name = LaunchScreen.storyboard; 309 | sourceTree = ""; 310 | }; 311 | /* End PBXVariantGroup section */ 312 | 313 | /* Begin XCBuildConfiguration section */ 314 | 52D6D98E1BEFF229002C0205 /* Debug */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 319 | CLANG_CXX_LIBRARY = "libc++"; 320 | CLANG_ENABLE_MODULES = YES; 321 | CLANG_ENABLE_OBJC_ARC = YES; 322 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 323 | CLANG_WARN_BOOL_CONVERSION = YES; 324 | CLANG_WARN_COMMA = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 327 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 328 | CLANG_WARN_EMPTY_BODY = YES; 329 | CLANG_WARN_ENUM_CONVERSION = YES; 330 | CLANG_WARN_INFINITE_RECURSION = YES; 331 | CLANG_WARN_INT_CONVERSION = YES; 332 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 334 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 335 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 336 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 337 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 338 | CLANG_WARN_STRICT_PROTOTYPES = YES; 339 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 340 | CLANG_WARN_UNREACHABLE_CODE = YES; 341 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 342 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 343 | COPY_PHASE_STRIP = NO; 344 | CURRENT_PROJECT_VERSION = 1; 345 | DEBUG_INFORMATION_FORMAT = dwarf; 346 | ENABLE_STRICT_OBJC_MSGSEND = YES; 347 | ENABLE_TESTABILITY = YES; 348 | GCC_C_LANGUAGE_STANDARD = gnu99; 349 | GCC_DYNAMIC_NO_PIC = NO; 350 | GCC_NO_COMMON_BLOCKS = YES; 351 | GCC_OPTIMIZATION_LEVEL = 0; 352 | GCC_PREPROCESSOR_DEFINITIONS = ( 353 | "DEBUG=1", 354 | "$(inherited)", 355 | ); 356 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 357 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 358 | GCC_WARN_UNDECLARED_SELECTOR = YES; 359 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 360 | GCC_WARN_UNUSED_FUNCTION = YES; 361 | GCC_WARN_UNUSED_VARIABLE = YES; 362 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 363 | MTL_ENABLE_DEBUG_INFO = YES; 364 | ONLY_ACTIVE_ARCH = YES; 365 | SDKROOT = iphoneos; 366 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 367 | SWIFT_VERSION = 5.0; 368 | TARGETED_DEVICE_FAMILY = "1,2"; 369 | VERSIONING_SYSTEM = "apple-generic"; 370 | VERSION_INFO_PREFIX = ""; 371 | }; 372 | name = Debug; 373 | }; 374 | 52D6D98F1BEFF229002C0205 /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ALWAYS_SEARCH_USER_PATHS = NO; 378 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 379 | CLANG_CXX_LIBRARY = "libc++"; 380 | CLANG_ENABLE_MODULES = YES; 381 | CLANG_ENABLE_OBJC_ARC = YES; 382 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 383 | CLANG_WARN_BOOL_CONVERSION = YES; 384 | CLANG_WARN_COMMA = YES; 385 | CLANG_WARN_CONSTANT_CONVERSION = YES; 386 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_EMPTY_BODY = YES; 389 | CLANG_WARN_ENUM_CONVERSION = YES; 390 | CLANG_WARN_INFINITE_RECURSION = YES; 391 | CLANG_WARN_INT_CONVERSION = YES; 392 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 394 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 395 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 396 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 397 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 398 | CLANG_WARN_STRICT_PROTOTYPES = YES; 399 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 400 | CLANG_WARN_UNREACHABLE_CODE = YES; 401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 402 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 403 | COPY_PHASE_STRIP = NO; 404 | CURRENT_PROJECT_VERSION = 1; 405 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 406 | ENABLE_NS_ASSERTIONS = NO; 407 | ENABLE_STRICT_OBJC_MSGSEND = YES; 408 | GCC_C_LANGUAGE_STANDARD = gnu99; 409 | GCC_NO_COMMON_BLOCKS = YES; 410 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 411 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 412 | GCC_WARN_UNDECLARED_SELECTOR = YES; 413 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 414 | GCC_WARN_UNUSED_FUNCTION = YES; 415 | GCC_WARN_UNUSED_VARIABLE = YES; 416 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 417 | MTL_ENABLE_DEBUG_INFO = NO; 418 | SDKROOT = iphoneos; 419 | SWIFT_VERSION = 5.0; 420 | TARGETED_DEVICE_FAMILY = "1,2"; 421 | VALIDATE_PRODUCT = YES; 422 | VERSIONING_SYSTEM = "apple-generic"; 423 | VERSION_INFO_PREFIX = ""; 424 | }; 425 | name = Release; 426 | }; 427 | 52D6D9911BEFF229002C0205 /* Debug */ = { 428 | isa = XCBuildConfiguration; 429 | buildSettings = { 430 | APPLICATION_EXTENSION_API_ONLY = YES; 431 | CLANG_ENABLE_MODULES = YES; 432 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 433 | DEFINES_MODULE = YES; 434 | DYLIB_COMPATIBILITY_VERSION = 1; 435 | DYLIB_CURRENT_VERSION = 1; 436 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 437 | INFOPLIST_FILE = Configs/UIWindowTransitions.plist; 438 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 439 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 440 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 441 | ONLY_ACTIVE_ARCH = NO; 442 | PRODUCT_BUNDLE_IDENTIFIER = "com.UIWindowTransitions.UIWindowTransitions-iOS"; 443 | PRODUCT_NAME = UIWindowTransitions; 444 | SKIP_INSTALL = YES; 445 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 446 | SWIFT_VERSION = 5.0; 447 | }; 448 | name = Debug; 449 | }; 450 | 52D6D9921BEFF229002C0205 /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | APPLICATION_EXTENSION_API_ONLY = YES; 454 | CLANG_ENABLE_MODULES = YES; 455 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 456 | DEFINES_MODULE = YES; 457 | DYLIB_COMPATIBILITY_VERSION = 1; 458 | DYLIB_CURRENT_VERSION = 1; 459 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 460 | INFOPLIST_FILE = Configs/UIWindowTransitions.plist; 461 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 462 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 463 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 464 | PRODUCT_BUNDLE_IDENTIFIER = "com.UIWindowTransitions.UIWindowTransitions-iOS"; 465 | PRODUCT_NAME = UIWindowTransitions; 466 | SKIP_INSTALL = YES; 467 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 468 | SWIFT_VERSION = 5.0; 469 | }; 470 | name = Release; 471 | }; 472 | 640CE0D21FA857C800CF5222 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 476 | CLANG_ANALYZER_NONNULL = YES; 477 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 479 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 480 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 481 | CODE_SIGN_IDENTITY = "iPhone Developer"; 482 | CODE_SIGN_STYLE = Automatic; 483 | DEVELOPMENT_TEAM = E5DU3FA699; 484 | GCC_C_LANGUAGE_STANDARD = gnu11; 485 | INFOPLIST_FILE = ExampleApp/Info.plist; 486 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 487 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 488 | PRODUCT_BUNDLE_IDENTIFIER = com.danielemargutti.ExampleApp; 489 | PRODUCT_NAME = "$(TARGET_NAME)"; 490 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 491 | SWIFT_VERSION = 5.0; 492 | TARGETED_DEVICE_FAMILY = "1,2"; 493 | }; 494 | name = Debug; 495 | }; 496 | 640CE0D31FA857C800CF5222 /* Release */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 500 | CLANG_ANALYZER_NONNULL = YES; 501 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 502 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 503 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 504 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 505 | CODE_SIGN_IDENTITY = "iPhone Developer"; 506 | CODE_SIGN_STYLE = Automatic; 507 | DEVELOPMENT_TEAM = E5DU3FA699; 508 | GCC_C_LANGUAGE_STANDARD = gnu11; 509 | INFOPLIST_FILE = ExampleApp/Info.plist; 510 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 511 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 512 | PRODUCT_BUNDLE_IDENTIFIER = com.danielemargutti.ExampleApp; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 515 | SWIFT_VERSION = 5.0; 516 | TARGETED_DEVICE_FAMILY = "1,2"; 517 | }; 518 | name = Release; 519 | }; 520 | /* End XCBuildConfiguration section */ 521 | 522 | /* Begin XCConfigurationList section */ 523 | 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "UIWindowTransitions" */ = { 524 | isa = XCConfigurationList; 525 | buildConfigurations = ( 526 | 52D6D98E1BEFF229002C0205 /* Debug */, 527 | 52D6D98F1BEFF229002C0205 /* Release */, 528 | ); 529 | defaultConfigurationIsVisible = 0; 530 | defaultConfigurationName = Release; 531 | }; 532 | 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "UIWindowTransitions-iOS" */ = { 533 | isa = XCConfigurationList; 534 | buildConfigurations = ( 535 | 52D6D9911BEFF229002C0205 /* Debug */, 536 | 52D6D9921BEFF229002C0205 /* Release */, 537 | ); 538 | defaultConfigurationIsVisible = 0; 539 | defaultConfigurationName = Release; 540 | }; 541 | 640CE0D11FA857C800CF5222 /* Build configuration list for PBXNativeTarget "ExampleApp" */ = { 542 | isa = XCConfigurationList; 543 | buildConfigurations = ( 544 | 640CE0D21FA857C800CF5222 /* Debug */, 545 | 640CE0D31FA857C800CF5222 /* Release */, 546 | ); 547 | defaultConfigurationIsVisible = 0; 548 | defaultConfigurationName = Release; 549 | }; 550 | /* End XCConfigurationList section */ 551 | }; 552 | rootObject = 52D6D9731BEFF229002C0205 /* Project object */; 553 | } 554 | -------------------------------------------------------------------------------- /UIWindowTransitions.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UIWindowTransitions.xcodeproj/project.xcworkspace/xcuserdata/daniele.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malcommac/UIWindowTransitions/9fae35fc44e2742cb12f640afbad6111355bda85/UIWindowTransitions.xcodeproj/project.xcworkspace/xcuserdata/daniele.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UIWindowTransitions.xcodeproj/xcshareddata/xcschemes/UIWindowTransitions-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /UIWindowTransitions.xcodeproj/xcuserdata/daniele.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /UIWindowTransitions.xcodeproj/xcuserdata/daniele.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ExampleApp.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | ExampleApp.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | UIWindowTransitions-iOS.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 0 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /UIWindowTransitions/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /UIWindowTransitions_Demo_Animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malcommac/UIWindowTransitions/9fae35fc44e2742cb12f640afbad6111355bda85/UIWindowTransitions_Demo_Animation.gif -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malcommac/UIWindowTransitions/9fae35fc44e2742cb12f640afbad6111355bda85/banner.png --------------------------------------------------------------------------------