├── imgs ├── animation_example.gif └── animation_code_example.gif ├── iOS Example ├── Assets.xcassets │ ├── Contents.json │ ├── img_logo.imageset │ │ ├── img_logo.png │ │ ├── img_logo@2x.png │ │ ├── img_logo@3x.png │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── AppDelegate.swift ├── SecondViewController.swift ├── UIButton+Extensions.swift ├── FirstViewController.swift ├── Info.plist ├── AlphaByStepTransition.swift ├── Base.lproj │ └── LaunchScreen.storyboard ├── FashionTransition.swift └── Main.storyboard ├── AZTransitions.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ └── AZTransitions.xcscheme └── project.pbxproj ├── .gitignore ├── Source ├── AZTransitions.h ├── Info.plist └── CustomModalTransition.swift ├── AZTransitions.podspec ├── LICENSE ├── Package.swift └── README.md /imgs/animation_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimin/AZTransitions/HEAD/imgs/animation_example.gif -------------------------------------------------------------------------------- /imgs/animation_code_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimin/AZTransitions/HEAD/imgs/animation_code_example.gif -------------------------------------------------------------------------------- /iOS Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /iOS Example/Assets.xcassets/img_logo.imageset/img_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimin/AZTransitions/HEAD/iOS Example/Assets.xcassets/img_logo.imageset/img_logo.png -------------------------------------------------------------------------------- /iOS Example/Assets.xcassets/img_logo.imageset/img_logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimin/AZTransitions/HEAD/iOS Example/Assets.xcassets/img_logo.imageset/img_logo@2x.png -------------------------------------------------------------------------------- /iOS Example/Assets.xcassets/img_logo.imageset/img_logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azimin/AZTransitions/HEAD/iOS Example/Assets.xcassets/img_logo.imageset/img_logo@3x.png -------------------------------------------------------------------------------- /AZTransitions.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AZTransitions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## OS X Finder 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | 19 | ## Other 20 | *.xccheckout 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | # Swift Package Manager 30 | .build/ 31 | 32 | # Carthage 33 | Carthage/Build -------------------------------------------------------------------------------- /iOS Example/Assets.xcassets/img_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "img_logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "img_logo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "img_logo@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iOS Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // iOS Example 4 | // 5 | // Created by Alex Zimin on 02/11/2016. 6 | // Copyright © 2016 Alexander Zimin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | return true 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Source/AZTransitions.h: -------------------------------------------------------------------------------- 1 | // 2 | // AZTransitions.h 3 | // AZTransitions 4 | // 5 | // Created by Alex Zimin on 02/11/2016. 6 | // Copyright © 2016 Alexander Zimin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for AZTransitions. 12 | FOUNDATION_EXPORT double AZTransitionsVersionNumber; 13 | 14 | //! Project version string for AZTransitions. 15 | FOUNDATION_EXPORT const unsigned char AZTransitionsVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /iOS Example/SecondViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.swift 3 | // iOS Example 4 | // 5 | // Created by Alex Zimin on 02/11/2016. 6 | // Copyright © 2016 Alexander Zimin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SecondViewController: UIViewController { 12 | 13 | @IBOutlet weak var closeButton: UIButton! { 14 | didSet { 15 | closeButton.underlineCurrentTitle() 16 | } 17 | } 18 | 19 | @IBAction func closeButtonAction(_ sender: UIButton) { 20 | self.dismiss(animated: true, completion: nil) 21 | } 22 | 23 | override var preferredStatusBarStyle: UIStatusBarStyle { 24 | return .lightContent 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /iOS Example/UIButton+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+Extensions.swift 3 | // AZTransitions 4 | // 5 | // Created by Alex Zimin on 06/11/2016. 6 | // Copyright © 2016 Alexander Zimin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIButton { 12 | func underlineCurrentTitle() { 13 | guard let text = title(for: .normal) else { return } 14 | let textRange = NSMakeRange(0, text.count) 15 | let attributedText = NSMutableAttributedString(string: text) 16 | attributedText.addAttribute(.underlineStyle , value: NSUnderlineStyle.single.rawValue, range: textRange) 17 | attributedText.addAttribute(.foregroundColor, value: titleColor(for: .normal) ?? UIColor.blue, range: textRange) 18 | setAttributedTitle(attributedText, for: .normal) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/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 | 0.26.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AZTransitions.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "AZTransitions" 3 | s.version = "0.26.0" 4 | s.summary = "Framework that helps you to work with custom modal transtions." 5 | s.description = <<-DESC 6 | With this framework can easily work with custom modal transitions. CustomModalTransition class provides you with all nesessary API, so you should only think about aniamtions. 7 | DESC 8 | s.homepage = "https://github.com/azimin/AZTransition" 9 | s.license = "MIT" 10 | s.author = { "Alexander Zimin" => "azimin@me.com" } 11 | 12 | s.ios.deployment_target = '8.0' 13 | s.source = { 14 | :git => 'https://github.com/azimin/AZTransition.git', 15 | :tag => s.version 16 | } 17 | s.source_files = 'Source/*.swift' 18 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '5' } 19 | end 20 | -------------------------------------------------------------------------------- /iOS Example/FirstViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.swift 3 | // iOS Example 4 | // 5 | // Created by Alex Zimin on 02/11/2016. 6 | // Copyright © 2016 Alexander Zimin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FirstViewController: UIViewController { 12 | 13 | var isOverCurrentContextTransition = false 14 | 15 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 16 | if isOverCurrentContextTransition { 17 | segue.destination.setCustomModalTransition(customModalTransition: AlphaByStepTransition(), inPresentationStyle: .overCurrentContext) 18 | } else { 19 | segue.destination.customModalTransition = FashionTransition() 20 | } 21 | } 22 | 23 | @IBOutlet weak var showButton: UIButton! { 24 | didSet { 25 | showButton.underlineCurrentTitle() 26 | } 27 | } 28 | 29 | @IBAction func isOverTransitionSwiftAction(_ sender: UISwitch) { 30 | isOverCurrentContextTransition = sender.isOn 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /iOS 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 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Alex Zimin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 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: "AZTransitions", 8 | platforms: [ 9 | .iOS(.v10) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 13 | .library( 14 | name: "AZTransitions", 15 | targets: ["AZTransitions"]), 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | // .package(url: /* package url */, from: "1.0.0"), 20 | ], 21 | targets: [ 22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 23 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 24 | .target( 25 | name: "AZTransitions", 26 | dependencies: [], 27 | path: "Source" 28 | ), 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /iOS 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 | UIInterfaceOrientationPortraitUpsideDown 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /iOS Example/AlphaByStepTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlphaByStepTransition.swift 3 | // AZTransitions 4 | // 5 | // Created by Alex Zimin on 06/11/2016. 6 | // Copyright © 2016 Alexander Zimin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AZTransitions 11 | 12 | private let animationDuration: TimeInterval = 1 13 | 14 | final class AlphaByStepTransition: CustomModalTransition { 15 | override init() { 16 | super.init(duration: animationDuration) 17 | } 18 | 19 | // Ony present 20 | func performTransition(interactive: Bool) { 21 | perfromAnimation() 22 | } 23 | 24 | private func perfromAnimation() { 25 | let halfDuration = duration / 2 26 | 27 | transitionContainerView.bringSubviewToFront(toViewController.view) 28 | 29 | toViewController.view.alpha = 0.0 30 | 31 | UIView.animateKeyframes(withDuration: duration, delay: 0, options: .allowUserInteraction, animations: { 32 | UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: halfDuration, animations: { 33 | self.fromViewController.view.alpha = 0.5 34 | }) 35 | UIView.addKeyframe(withRelativeStartTime: halfDuration, relativeDuration: halfDuration, animations: { 36 | self.fromViewController.view.alpha = 1.0 37 | self.toViewController.view.alpha = 0.8 38 | }) 39 | }, completion: { 40 | success in 41 | self.finishAnimation(completion: nil) 42 | }) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /iOS 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 | -------------------------------------------------------------------------------- /AZTransitions.xcodeproj/xcshareddata/xcschemes/AZTransitions.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /iOS Example/FashionTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FashionTransition.swift 3 | // iOS Example 4 | // 5 | // Created by Alex Zimin on 02/11/2016. 6 | // Copyright © 2016 Alexander Zimin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AZTransitions 11 | 12 | private let animationDuration: TimeInterval = 1 13 | private let sizeScaleValue: CGFloat = 0.35 14 | 15 | private enum ViewControllerStyle { 16 | case compact 17 | case original 18 | } 19 | 20 | final class FashionTransition: CustomModalTransition { 21 | override init() { 22 | super.init(duration: animationDuration) 23 | } 24 | 25 | func performTransition(interactive: Bool) { 26 | perfromAnimation(isPresenting: true) 27 | } 28 | 29 | func performDismissingTransition(interactive: Bool) { 30 | perfromAnimation(isPresenting: false) 31 | } 32 | 33 | private func perfromAnimation(isPresenting: Bool) { 34 | let halfDuration = duration / 2 35 | 36 | let onScreenViewControllerType: TransitionViewControllerType = isPresenting ? .presenting : .presented 37 | let shouldBeOnScreenViewControllerType: TransitionViewControllerType = isPresenting ? .presented : .presenting 38 | 39 | scale(viewControllerType: onScreenViewControllerType, toStyle: .original) 40 | scale(viewControllerType: shouldBeOnScreenViewControllerType, toStyle: .compact) 41 | 42 | let backgroundView = UIView(frame: transitionContainerView.frame) 43 | backgroundView.backgroundColor = UIColor.black 44 | transitionContainerView.insertSubview(backgroundView, at: 0) 45 | 46 | UIView.animate(withDuration: halfDuration, animations: { 47 | self.scale(viewControllerType: onScreenViewControllerType, toStyle: .compact) 48 | }, completion: { 49 | (completion) in 50 | UIView.animate(withDuration: halfDuration, animations: { 51 | self.scale(viewControllerType: shouldBeOnScreenViewControllerType, toStyle: .original) 52 | }, completion: { (completion) in 53 | backgroundView.removeFromSuperview() 54 | self.finishTransition() 55 | }) 56 | }) 57 | } 58 | 59 | private var itemCompactWidth: CGFloat { 60 | return self.transitionContainerView.frame.width * sizeScaleValue 61 | } 62 | 63 | private var space: CGFloat { 64 | let coefficent = (1 - sizeScaleValue * 2) / 3 65 | return self.transitionContainerView.frame.width * coefficent 66 | } 67 | 68 | private func scale(viewControllerType: TransitionViewControllerType, toStyle style: ViewControllerStyle) { 69 | let viewController = viewControllerFor(type: viewControllerType) 70 | let anotherViewController = viewControllerFor(type: viewControllerType.revesed) 71 | 72 | let offset: CGFloat = viewControllerType == .presented ? ((space * 2) + itemCompactWidth) : space 73 | 74 | switch style { 75 | case .compact: 76 | scale(viewController: viewController, toValue: sizeScaleValue, withOffset: offset) 77 | default: 78 | transitionContainerView.insertSubview(anotherViewController.view, belowSubview: viewController.view) 79 | scale(viewController: viewController, toValue: 1, withOffset: 0) 80 | } 81 | } 82 | 83 | private func scale(viewController: UIViewController, toValue value: CGFloat, withOffset offset: CGFloat) { 84 | viewController.view.transform = CGAffineTransform(scaleX: value, y: value) 85 | viewController.view.frame.origin.x = offset 86 | } 87 | 88 | private func finishTransition() { 89 | presentingViewController.view.transform = CGAffineTransform.identity 90 | presentedViewController.view.transform = CGAffineTransform.identity 91 | finishAnimation(completion: nil) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AZTransitions 2 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/AZTransitions.svg)](https://img.shields.io/cocoapods/v/AZTransitions.svg) 3 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | [![SPM Compatible](https://img.shields.io/badge/spm-compatible-4BC51D.svg?style=flat)](https://github.com/apple/swift-package-manager) 5 | [![Platform](https://img.shields.io/cocoapods/p/AZTransitions.svg?style=flat)](http://cocoadocs.org/docsets/AZTransitions) 6 | [![Twitter](https://img.shields.io/badge/twitter-@ziminalex-blue.svg?style=flat)](http://twitter.com/ziminalex) 7 | ![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat) 8 | 9 | Make your modal transition with custom animation. 10 | AZTransitions helps you think about creativity, giving specific API methods. 11 | 12 | ## Visual Example 13 | 14 | Inside this repository you can try `iOS Example` target with example `FashionTransition.swift` class: 15 | 16 | ![Animation example](imgs/animation_example.gif) 17 | 18 | ## Installation 19 | 20 | - Add the following to your [`Podfile`](http://cocoapods.org/) and run `pod install` 21 | ```ruby 22 | pod 'AZTransitions' 23 | ``` 24 | - or add the following to your [`Cartfile`](https://github.com/Carthage/Carthage) and run `carthage update` 25 | ``` 26 | github "azimin/AZTransitions" 27 | ``` 28 | - if you are using [`Swift Package Manager`](https://github.com/apple/swift-package-manager) just add it to the `dependencies` value of your `Package.swift` 29 | ```swift 30 | dependencies: [ 31 | .package(url: "https://github.com/azimin/AZTransitions.git", .upToNextMajor(from: "0.26.0")) 32 | ] 33 | ``` 34 | - or clone as a git submodule, 35 | 36 | - or just copy `AZTransitions/Source/CustomModalTransition.swift` into your project. 37 | 38 | ## Code Example 39 | 40 | To create any custom transition just subclass `CustomModalTransition`: 41 | 42 | ```swift 43 | class FashionTransition: CustomModalTransition { 44 | override init() { 45 | super.init(duration: 0.5) 46 | } 47 | } 48 | ``` 49 | 50 | -- 51 | 52 | Then set as `az_modalTransition` to nessesary view just before presenting it 53 | 54 | ```swift 55 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 56 | segue.destination.customModalTransition = FashionTransition() 57 | } 58 | ``` 59 | 60 | or 61 | 62 | ```swift 63 | func show() { 64 | let viewController = UIViewController() 65 | viewController.customModalTransition = FashionTransition() 66 | self.present(viewController, animated: true, completion: nil) 67 | } 68 | ``` 69 | 70 | -- 71 | 72 | To have custom present animation, just implement `performTransition(interactive: Bool)` inside your `FashionTransition` class: 73 | 74 | ```swift 75 | func performTransition(interactive: Bool) { 76 | self.presentedViewController.view.alpha = 0.0 77 | 78 | UIView.animate(withDuration: duration, animations: { 79 | self.presentedViewController.view.alpha = 1.0 80 | self.presentingViewController.view.alpha = 0.0 81 | }, completion: { (completed) in 82 | self.presentingViewController.view.alpha = 1.0 83 | self.finishAnimation(completion: nil) 84 | }) 85 | } 86 | ``` 87 | 88 | As you may have guessed, you have different properties. The main ones: 89 | 90 | - `duration` — transition duration 91 | - `presentingViewController` — the presenting view controller (bottom one) 92 | - `presentedViewController` — view controller that is going to be presented (top one) 93 | 94 | You can animate them as you want. 95 | 96 | **🔥IMPORTANT🔥** don't forget to call `finishAnimation(completion: nil)` in the end. 97 | 98 | In this case animation will be: 99 | 100 | ![Animation code example](imgs/animation_code_example.gif) 101 | 102 | ## UIModalPresentationStyle 103 | 104 | Of course sometimes you want to use diffenret modal presentation styles (for example `overCurrentContext`), in this case you can call `setCustomModalTransition(customModalTransition: CustomModalTransition, inPresentationStyle: UIModalPresentationStyle)` of UIViewController instead of setting `customModalTransition` directly. 105 | 106 | ## More 107 | 108 | You have different properties and methods to help you: 109 | 110 | - `performDismissingTransition(interactive: Bool)` to implement custom transition animation when dismissing 111 | - `fromViewController`/`toViewController` in term of Apple transition. They are reversed in presenting and dismissing transitions. 112 | - `transitionContainerView` view where the transition takes place (`resentingViewController.view` and `presentedViewController.view` located on inside `transitionContainerView`), so you can add your custom views here to make animation more interesting (see `iOS Example`) 113 | - Some methods for interactive animations (example will be added be soon) 114 | - Some method to work with orientation changing things (example will be added be soon) 115 | -------------------------------------------------------------------------------- /iOS Example/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 | 40 | 41 | 42 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Source/CustomModalTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomModalTransition.swift 3 | // TransitionsHelper 4 | // 5 | // Created by Alex Zimin on 02/11/2016. 6 | // Copyright © 2016 Alexander Zimin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ObjectiveC.runtime 11 | 12 | @objc 13 | public protocol CustomModalTransitionType: NSObjectProtocol { 14 | @objc optional func performTransition(interactive: Bool) 15 | @objc optional func performDismissingTransition(interactive: Bool) 16 | } 17 | 18 | extension CustomModalTransition: CustomModalTransitionType { } 19 | 20 | public enum TransitionViewControllerType { 21 | case presented 22 | case presenting 23 | case none 24 | 25 | public var revesed: TransitionViewControllerType { 26 | switch self { 27 | case .presented: 28 | return .presenting 29 | case .presenting: 30 | return .presented 31 | case .none: 32 | fatalError("Wrong type") 33 | } 34 | } 35 | } 36 | 37 | open class CustomModalTransition: NSObject { 38 | 39 | // MARK: - Init 40 | 41 | public init(duration: TimeInterval) { 42 | self.duration = duration 43 | } 44 | 45 | public override init() { 46 | self.duration = 0.25 47 | super.init() 48 | } 49 | 50 | // MARK: - Default parametrs 51 | 52 | public private(set) var duration: TimeInterval 53 | 54 | // MARK: - Transition Parameters 55 | 56 | public fileprivate(set) weak var transitionContext: UIViewControllerContextTransitioning! 57 | public fileprivate(set) weak var transitionContainerView: UIView! 58 | public fileprivate(set) var isPresenting: Bool = false 59 | public fileprivate(set) var isInteractive: Bool = false 60 | 61 | // MARK: - View Controllers 62 | // You can choose any pair 63 | 64 | // Presented/preseting style view controllees 65 | public fileprivate(set) weak var presentedViewController: UIViewController! 66 | public fileprivate(set) weak var presentingViewController: UIViewController! 67 | 68 | // Apple style view controllers 69 | public fileprivate(set) weak var fromViewController: UIViewController! 70 | public fileprivate(set) weak var toViewController: UIViewController! 71 | 72 | public func viewControllerFor(type: TransitionViewControllerType) -> UIViewController { 73 | switch type { 74 | case .presented: 75 | return presentedViewController 76 | case .presenting: 77 | return presentingViewController 78 | case .none: 79 | fatalError("Wrong type") 80 | } 81 | } 82 | 83 | public func viewControllerTypeFrom(viewController: UIViewController) -> TransitionViewControllerType { 84 | if viewController == presentedViewController { 85 | return .presented 86 | } 87 | if viewController == presentingViewController { 88 | return .presenting 89 | } 90 | return .none 91 | } 92 | 93 | // MARK: - Animating the Transition 94 | 95 | public func prepareForTransition(isInteractive: Bool) { 96 | 97 | } 98 | 99 | public func finishAnimation(completion: ((_ finished: Bool) -> Void)?) { 100 | let success = !transitionContext.transitionWasCancelled 101 | 102 | completion?(success) 103 | 104 | transitionContext.completeTransition(success) 105 | 106 | self.transitionContext = nil 107 | self.presentingViewController = nil 108 | self.presentedViewController = nil 109 | } 110 | 111 | // MARK: - Interactive Transition 112 | 113 | public func beginInteractiveDismissalTransition(completion: (() -> Void)?) { 114 | self.interactionController = UIPercentDrivenInteractiveTransition() 115 | owningController.dismiss(animated: true, completion: completion) 116 | } 117 | 118 | public func updateInteractiveTransitionToProgress(progress: CGFloat) { 119 | interactionController.update(progress) 120 | } 121 | 122 | public func cancelInteractiveTransition() { 123 | // http://openradar.appspot.com/14675246 124 | interactionController.completionSpeed = 0.999 // http://stackoverflow.com/a/22968139/188461 125 | interactionController.cancel() 126 | 127 | self.interactionController = nil 128 | } 129 | 130 | public func finishInteractiveTransition() { 131 | interactionController.finish() 132 | interactionController = nil 133 | } 134 | 135 | // MARK: - Rotation Helpers 136 | 137 | public private(set) var initialTransform: CGAffineTransform! 138 | public private(set) var finalTransform: CGAffineTransform! 139 | 140 | public func initialCenterFor(viewControllerType: TransitionViewControllerType) -> CGPoint { 141 | let frame = transitionContext.initialFrame(for: viewControllerFor(type: viewControllerType)) 142 | return frame.az_center 143 | } 144 | 145 | public func finalCenterFor(viewControllerType: TransitionViewControllerType) -> CGPoint { 146 | let frame = transitionContext.finalFrame(for: viewControllerFor(type: viewControllerType)) 147 | return frame.az_center 148 | } 149 | 150 | public func initialBoundsFor(viewControllerType: TransitionViewControllerType) -> CGRect { 151 | let frame = transitionContext.initialFrame(for: viewControllerFor(type: viewControllerType)) 152 | let transform = viewControllerType == .presented ? initialTransform : finalTransform 153 | let frameAfterRotation = frame.applying(transform!) 154 | return CGRect(x: 0, y: 0, width: frameAfterRotation.width, height: frameAfterRotation.height) 155 | } 156 | 157 | public func finalBoundsFor(viewControllerType: TransitionViewControllerType) -> CGRect { 158 | let frame = transitionContext.finalFrame(for: viewControllerFor(type: viewControllerType)) 159 | let transform = (viewControllerType == .presented) ? initialTransform : finalTransform 160 | let frameAfterRotation = frame.applying(transform!) 161 | return CGRect(x: 0, y: 0, width: frameAfterRotation.width, height: frameAfterRotation.height) 162 | } 163 | 164 | fileprivate func prepareTransitionParameters() { 165 | if isPresenting { 166 | transitionContainerView.addSubview(toViewController.view) 167 | transitionContainerView.addSubview(fromViewController.view) 168 | 169 | self.initialTransform = presentingViewController.view.transform 170 | self.finalTransform = presentedViewController.view.transform 171 | 172 | presentingViewController.view.frame = self.transitionContext.initialFrame(for: presentingViewController) 173 | } else { 174 | transitionContainerView.addSubview(fromViewController.view) 175 | transitionContainerView.addSubview(toViewController.view) 176 | 177 | self.initialTransform = presentedViewController.view.transform; 178 | self.finalTransform = presentingViewController.view.transform; 179 | 180 | presentingViewController.view.frame = self.transitionContext.finalFrame(for: presentingViewController) 181 | } 182 | } 183 | 184 | // MARK: - Private 185 | 186 | fileprivate var interactionController: UIPercentDrivenInteractiveTransition! 187 | fileprivate weak var owningController: UIViewController! 188 | } 189 | 190 | // MARK: - UIViewControllerTransitioningDelegate 191 | 192 | extension CustomModalTransition: UIViewControllerTransitioningDelegate { 193 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 194 | 195 | presented.modalPresentationCapturesStatusBarAppearance = true 196 | 197 | // If subclass don't implementing dismissing protocol 198 | if !self.responds(to: #selector(CustomModalTransitionType.performTransition(interactive:))) { 199 | return nil 200 | } 201 | 202 | guard presented == owningController else { return nil } 203 | 204 | self.presentedViewController = presented 205 | self.presentingViewController = presenting 206 | self.isPresenting = true 207 | 208 | return self 209 | } 210 | 211 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 212 | 213 | self.addFinalSetupToDismissedViewController(dismissed: dismissed) 214 | 215 | // If subclass don't implementing dismissing protocol 216 | if !self.responds(to: #selector(CustomModalTransitionType.performDismissingTransition(interactive:))) { 217 | return nil 218 | } 219 | 220 | guard dismissed == owningController else { return nil } 221 | 222 | self.presentedViewController = dismissed 223 | self.presentingViewController = dismissed.presentingViewController 224 | self.isPresenting = false 225 | 226 | return self 227 | } 228 | 229 | // http://openradar.appspot.com/radar?id=5320103646199808 230 | private func addFinalSetupToDismissedViewController(dismissed: UIViewController) { 231 | if dismissed.modalPresentationStyle == .fullScreen || dismissed.modalPresentationStyle == .currentContext { 232 | return 233 | } 234 | 235 | // Adding to main thread queue, becase when this method get called `transitionCoordinator` is nil 236 | OperationQueue.main.addOperation { 237 | dismissed.transitionCoordinator?.animate(alongsideTransition: nil, completion: { (context) in 238 | if !context.isCancelled { 239 | let toViewController = context.viewController(forKey: UITransitionContextViewControllerKey.to) 240 | UIApplication.shared.keyWindow?.addSubview(toViewController!.view) 241 | } 242 | }) 243 | } 244 | } 245 | 246 | public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 247 | return interactionController 248 | } 249 | } 250 | 251 | // MARK: - UIViewControllerAnimatedTransitioning 252 | 253 | extension CustomModalTransition: UIViewControllerAnimatedTransitioning { 254 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 255 | return duration 256 | } 257 | 258 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 259 | self.transitionContext = transitionContext 260 | self.transitionContainerView = transitionContext.containerView 261 | self.isInteractive = transitionContext.isInteractive 262 | 263 | self.fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) 264 | self.toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) 265 | 266 | prepareTransitionParameters() 267 | 268 | prepareForTransition(isInteractive: isInteractive) 269 | 270 | if isPresenting { 271 | (self as CustomModalTransitionType).performTransition?(interactive: isInteractive) 272 | } else { 273 | (self as CustomModalTransitionType).performDismissingTransition?(interactive: isInteractive) 274 | } 275 | } 276 | } 277 | 278 | // MARK: - UIViewController Extensions 279 | 280 | private var associatedObjectHandle: UInt8 = 0 281 | 282 | public extension UIViewController { 283 | var customModalTransition: CustomModalTransition? { 284 | get { 285 | return objc_getAssociatedObject(self, &associatedObjectHandle) as? CustomModalTransition 286 | } 287 | 288 | set { 289 | self.customModalTransition?.owningController = nil 290 | 291 | self.transitioningDelegate = newValue 292 | 293 | if let transition = newValue { 294 | self.modalPresentationStyle = .fullScreen 295 | transition.owningController = self 296 | } 297 | 298 | objc_setAssociatedObject(self, &associatedObjectHandle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 299 | } 300 | } 301 | 302 | func setCustomModalTransition(customModalTransition: CustomModalTransition, inPresentationStyle: UIModalPresentationStyle) { 303 | self.customModalTransition = customModalTransition 304 | self.modalPresentationStyle = inPresentationStyle 305 | } 306 | } 307 | 308 | // MARK: - CGRect Extensions 309 | 310 | private extension CGRect { 311 | var az_center: CGPoint { 312 | return CGPoint(x: self.midX, y: self.midY) 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /AZTransitions.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 921ABFA91DCE8E050085C7F9 /* AlphaByStepTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 921ABFA81DCE8E050085C7F9 /* AlphaByStepTransition.swift */; }; 11 | 921ABFAE1DCE946D0085C7F9 /* UIButton+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 921ABFAD1DCE946D0085C7F9 /* UIButton+Extensions.swift */; }; 12 | 921ABFB01DCE94F80085C7F9 /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 921ABFAF1DCE94F80085C7F9 /* FirstViewController.swift */; }; 13 | 92DE87BF1DCA54B50003D2AB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DE87BE1DCA54B50003D2AB /* AppDelegate.swift */; }; 14 | 92DE87C61DCA54B50003D2AB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92DE87C51DCA54B50003D2AB /* Assets.xcassets */; }; 15 | 92DE87C91DCA54B50003D2AB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 92DE87C71DCA54B50003D2AB /* LaunchScreen.storyboard */; }; 16 | 92DE87D11DCA54C80003D2AB /* FashionTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DE87D01DCA54C80003D2AB /* FashionTransition.swift */; }; 17 | 92DE87D41DCA56DC0003D2AB /* AZTransitions.h in Headers */ = {isa = PBXBuildFile; fileRef = 92DE87D31DCA56DC0003D2AB /* AZTransitions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 92DE87D81DCA56E40003D2AB /* CustomModalTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DE87D71DCA56E40003D2AB /* CustomModalTransition.swift */; }; 19 | 92DE87DA1DCA57D60003D2AB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 92DE87D91DCA57D60003D2AB /* Main.storyboard */; }; 20 | 92DE87DE1DCA58370003D2AB /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DE87DC1DCA58370003D2AB /* SecondViewController.swift */; }; 21 | 9F3F74EC1E1687F700382922 /* AZTransitions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DE87841DCA51300003D2AB /* AZTransitions.framework */; }; 22 | 9F3F74ED1E1687F700382922 /* AZTransitions.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 92DE87841DCA51300003D2AB /* AZTransitions.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 92DE87CE1DCA54BC0003D2AB /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 92DE877B1DCA51300003D2AB /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 92DE87831DCA51300003D2AB; 31 | remoteInfo = AZTransitions; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXCopyFilesBuildPhase section */ 36 | 9F3F74EE1E1687F700382922 /* Embed Frameworks */ = { 37 | isa = PBXCopyFilesBuildPhase; 38 | buildActionMask = 2147483647; 39 | dstPath = ""; 40 | dstSubfolderSpec = 10; 41 | files = ( 42 | 9F3F74ED1E1687F700382922 /* AZTransitions.framework in Embed Frameworks */, 43 | ); 44 | name = "Embed Frameworks"; 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXCopyFilesBuildPhase section */ 48 | 49 | /* Begin PBXFileReference section */ 50 | 921ABFA81DCE8E050085C7F9 /* AlphaByStepTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlphaByStepTransition.swift; sourceTree = ""; }; 51 | 921ABFAD1DCE946D0085C7F9 /* UIButton+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Extensions.swift"; sourceTree = ""; }; 52 | 921ABFAF1DCE94F80085C7F9 /* FirstViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; }; 53 | 92DE87841DCA51300003D2AB /* AZTransitions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AZTransitions.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 92DE87BC1DCA54B50003D2AB /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 92DE87BE1DCA54B50003D2AB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 56 | 92DE87C51DCA54B50003D2AB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 57 | 92DE87C81DCA54B50003D2AB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 58 | 92DE87CA1DCA54B50003D2AB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 92DE87D01DCA54C80003D2AB /* FashionTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FashionTransition.swift; sourceTree = ""; }; 60 | 92DE87D31DCA56DC0003D2AB /* AZTransitions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AZTransitions.h; path = Source/AZTransitions.h; sourceTree = ""; }; 61 | 92DE87D51DCA56E00003D2AB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Source/Info.plist; sourceTree = ""; }; 62 | 92DE87D71DCA56E40003D2AB /* CustomModalTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomModalTransition.swift; path = Source/CustomModalTransition.swift; sourceTree = ""; }; 63 | 92DE87D91DCA57D60003D2AB /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 64 | 92DE87DC1DCA58370003D2AB /* SecondViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 92DE87801DCA51300003D2AB /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | 92DE87B91DCA54B50003D2AB /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | 9F3F74EC1E1687F700382922 /* AZTransitions.framework in Frameworks */, 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | 921ABFAA1DCE944F0085C7F9 /* Transitions */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 92DE87D01DCA54C80003D2AB /* FashionTransition.swift */, 90 | 921ABFA81DCE8E050085C7F9 /* AlphaByStepTransition.swift */, 91 | ); 92 | name = Transitions; 93 | sourceTree = ""; 94 | }; 95 | 921ABFAB1DCE94540085C7F9 /* ViewControllers */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 921ABFAF1DCE94F80085C7F9 /* FirstViewController.swift */, 99 | 92DE87DC1DCA58370003D2AB /* SecondViewController.swift */, 100 | ); 101 | name = ViewControllers; 102 | sourceTree = ""; 103 | }; 104 | 921ABFAC1DCE945B0085C7F9 /* Extensison */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 921ABFAD1DCE946D0085C7F9 /* UIButton+Extensions.swift */, 108 | ); 109 | name = Extensison; 110 | sourceTree = ""; 111 | }; 112 | 92DE877A1DCA51300003D2AB = { 113 | isa = PBXGroup; 114 | children = ( 115 | 92DE87D21DCA56CB0003D2AB /* Source */, 116 | 92DE87BD1DCA54B50003D2AB /* iOS Example */, 117 | 92DE87851DCA51300003D2AB /* Products */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | 92DE87851DCA51300003D2AB /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 92DE87841DCA51300003D2AB /* AZTransitions.framework */, 125 | 92DE87BC1DCA54B50003D2AB /* iOS Example.app */, 126 | ); 127 | name = Products; 128 | sourceTree = ""; 129 | }; 130 | 92DE87BD1DCA54B50003D2AB /* iOS Example */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 92DE87BE1DCA54B50003D2AB /* AppDelegate.swift */, 134 | 921ABFAB1DCE94540085C7F9 /* ViewControllers */, 135 | 921ABFAA1DCE944F0085C7F9 /* Transitions */, 136 | 921ABFAC1DCE945B0085C7F9 /* Extensison */, 137 | 92DE87D91DCA57D60003D2AB /* Main.storyboard */, 138 | 92DE87C51DCA54B50003D2AB /* Assets.xcassets */, 139 | 92DE87C71DCA54B50003D2AB /* LaunchScreen.storyboard */, 140 | 92DE87CA1DCA54B50003D2AB /* Info.plist */, 141 | ); 142 | path = "iOS Example"; 143 | sourceTree = ""; 144 | }; 145 | 92DE87D21DCA56CB0003D2AB /* Source */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 92DE87D31DCA56DC0003D2AB /* AZTransitions.h */, 149 | 92DE87D71DCA56E40003D2AB /* CustomModalTransition.swift */, 150 | 92DE87D51DCA56E00003D2AB /* Info.plist */, 151 | ); 152 | name = Source; 153 | sourceTree = ""; 154 | }; 155 | /* End PBXGroup section */ 156 | 157 | /* Begin PBXHeadersBuildPhase section */ 158 | 92DE87811DCA51300003D2AB /* Headers */ = { 159 | isa = PBXHeadersBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 92DE87D41DCA56DC0003D2AB /* AZTransitions.h in Headers */, 163 | ); 164 | runOnlyForDeploymentPostprocessing = 0; 165 | }; 166 | /* End PBXHeadersBuildPhase section */ 167 | 168 | /* Begin PBXNativeTarget section */ 169 | 92DE87831DCA51300003D2AB /* AZTransitions */ = { 170 | isa = PBXNativeTarget; 171 | buildConfigurationList = 92DE878C1DCA51300003D2AB /* Build configuration list for PBXNativeTarget "AZTransitions" */; 172 | buildPhases = ( 173 | 92DE877F1DCA51300003D2AB /* Sources */, 174 | 92DE87801DCA51300003D2AB /* Frameworks */, 175 | 92DE87811DCA51300003D2AB /* Headers */, 176 | 92DE87821DCA51300003D2AB /* Resources */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | ); 182 | name = AZTransitions; 183 | productName = AZTransitions; 184 | productReference = 92DE87841DCA51300003D2AB /* AZTransitions.framework */; 185 | productType = "com.apple.product-type.framework"; 186 | }; 187 | 92DE87BB1DCA54B50003D2AB /* iOS Example */ = { 188 | isa = PBXNativeTarget; 189 | buildConfigurationList = 92DE87CB1DCA54B50003D2AB /* Build configuration list for PBXNativeTarget "iOS Example" */; 190 | buildPhases = ( 191 | 92DE87B81DCA54B50003D2AB /* Sources */, 192 | 92DE87B91DCA54B50003D2AB /* Frameworks */, 193 | 92DE87BA1DCA54B50003D2AB /* Resources */, 194 | 9F3F74EE1E1687F700382922 /* Embed Frameworks */, 195 | ); 196 | buildRules = ( 197 | ); 198 | dependencies = ( 199 | 92DE87CF1DCA54BC0003D2AB /* PBXTargetDependency */, 200 | ); 201 | name = "iOS Example"; 202 | productName = "iOS Example"; 203 | productReference = 92DE87BC1DCA54B50003D2AB /* iOS Example.app */; 204 | productType = "com.apple.product-type.application"; 205 | }; 206 | /* End PBXNativeTarget section */ 207 | 208 | /* Begin PBXProject section */ 209 | 92DE877B1DCA51300003D2AB /* Project object */ = { 210 | isa = PBXProject; 211 | attributes = { 212 | LastSwiftUpdateCheck = 0810; 213 | LastUpgradeCheck = 1010; 214 | ORGANIZATIONNAME = "Alexander Zimin"; 215 | TargetAttributes = { 216 | 92DE87831DCA51300003D2AB = { 217 | CreatedOnToolsVersion = 8.1; 218 | DevelopmentTeam = 5MGEMLZRS4; 219 | LastSwiftMigration = 1020; 220 | ProvisioningStyle = Automatic; 221 | }; 222 | 92DE87BB1DCA54B50003D2AB = { 223 | CreatedOnToolsVersion = 8.1; 224 | DevelopmentTeam = 5MGEMLZRS4; 225 | LastSwiftMigration = 1020; 226 | ProvisioningStyle = Automatic; 227 | }; 228 | }; 229 | }; 230 | buildConfigurationList = 92DE877E1DCA51300003D2AB /* Build configuration list for PBXProject "AZTransitions" */; 231 | compatibilityVersion = "Xcode 3.2"; 232 | developmentRegion = en; 233 | hasScannedForEncodings = 0; 234 | knownRegions = ( 235 | en, 236 | Base, 237 | ); 238 | mainGroup = 92DE877A1DCA51300003D2AB; 239 | productRefGroup = 92DE87851DCA51300003D2AB /* Products */; 240 | projectDirPath = ""; 241 | projectRoot = ""; 242 | targets = ( 243 | 92DE87831DCA51300003D2AB /* AZTransitions */, 244 | 92DE87BB1DCA54B50003D2AB /* iOS Example */, 245 | ); 246 | }; 247 | /* End PBXProject section */ 248 | 249 | /* Begin PBXResourcesBuildPhase section */ 250 | 92DE87821DCA51300003D2AB /* Resources */ = { 251 | isa = PBXResourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | 92DE87BA1DCA54B50003D2AB /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | 92DE87DA1DCA57D60003D2AB /* Main.storyboard in Resources */, 262 | 92DE87C91DCA54B50003D2AB /* LaunchScreen.storyboard in Resources */, 263 | 92DE87C61DCA54B50003D2AB /* Assets.xcassets in Resources */, 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | /* End PBXResourcesBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | 92DE877F1DCA51300003D2AB /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 92DE87D81DCA56E40003D2AB /* CustomModalTransition.swift in Sources */, 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | }; 278 | 92DE87B81DCA54B50003D2AB /* Sources */ = { 279 | isa = PBXSourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | 921ABFA91DCE8E050085C7F9 /* AlphaByStepTransition.swift in Sources */, 283 | 921ABFAE1DCE946D0085C7F9 /* UIButton+Extensions.swift in Sources */, 284 | 92DE87D11DCA54C80003D2AB /* FashionTransition.swift in Sources */, 285 | 921ABFB01DCE94F80085C7F9 /* FirstViewController.swift in Sources */, 286 | 92DE87DE1DCA58370003D2AB /* SecondViewController.swift in Sources */, 287 | 92DE87BF1DCA54B50003D2AB /* AppDelegate.swift in Sources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | /* End PBXSourcesBuildPhase section */ 292 | 293 | /* Begin PBXTargetDependency section */ 294 | 92DE87CF1DCA54BC0003D2AB /* PBXTargetDependency */ = { 295 | isa = PBXTargetDependency; 296 | target = 92DE87831DCA51300003D2AB /* AZTransitions */; 297 | targetProxy = 92DE87CE1DCA54BC0003D2AB /* PBXContainerItemProxy */; 298 | }; 299 | /* End PBXTargetDependency section */ 300 | 301 | /* Begin PBXVariantGroup section */ 302 | 92DE87C71DCA54B50003D2AB /* LaunchScreen.storyboard */ = { 303 | isa = PBXVariantGroup; 304 | children = ( 305 | 92DE87C81DCA54B50003D2AB /* Base */, 306 | ); 307 | name = LaunchScreen.storyboard; 308 | sourceTree = ""; 309 | }; 310 | /* End PBXVariantGroup section */ 311 | 312 | /* Begin XCBuildConfiguration section */ 313 | 92DE878A1DCA51300003D2AB /* Debug */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | ALWAYS_SEARCH_USER_PATHS = NO; 317 | CLANG_ANALYZER_NONNULL = YES; 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_DOCUMENTATION_COMMENTS = YES; 329 | CLANG_WARN_EMPTY_BODY = YES; 330 | CLANG_WARN_ENUM_CONVERSION = YES; 331 | CLANG_WARN_INFINITE_RECURSION = YES; 332 | CLANG_WARN_INT_CONVERSION = YES; 333 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 334 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 335 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 337 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 338 | CLANG_WARN_STRICT_PROTOTYPES = YES; 339 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 340 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 341 | CLANG_WARN_UNREACHABLE_CODE = YES; 342 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 343 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 344 | COPY_PHASE_STRIP = NO; 345 | CURRENT_PROJECT_VERSION = 1; 346 | DEBUG_INFORMATION_FORMAT = dwarf; 347 | ENABLE_STRICT_OBJC_MSGSEND = YES; 348 | ENABLE_TESTABILITY = YES; 349 | GCC_C_LANGUAGE_STANDARD = gnu99; 350 | GCC_DYNAMIC_NO_PIC = NO; 351 | GCC_NO_COMMON_BLOCKS = YES; 352 | GCC_OPTIMIZATION_LEVEL = 0; 353 | GCC_PREPROCESSOR_DEFINITIONS = ( 354 | "DEBUG=1", 355 | "$(inherited)", 356 | ); 357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 359 | GCC_WARN_UNDECLARED_SELECTOR = YES; 360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 361 | GCC_WARN_UNUSED_FUNCTION = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 364 | MTL_ENABLE_DEBUG_INFO = YES; 365 | ONLY_ACTIVE_ARCH = YES; 366 | SDKROOT = iphoneos; 367 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 368 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 369 | TARGETED_DEVICE_FAMILY = "1,2"; 370 | VERSIONING_SYSTEM = "apple-generic"; 371 | VERSION_INFO_PREFIX = ""; 372 | }; 373 | name = Debug; 374 | }; 375 | 92DE878B1DCA51300003D2AB /* Release */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | ALWAYS_SEARCH_USER_PATHS = NO; 379 | CLANG_ANALYZER_NONNULL = YES; 380 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 381 | CLANG_CXX_LIBRARY = "libc++"; 382 | CLANG_ENABLE_MODULES = YES; 383 | CLANG_ENABLE_OBJC_ARC = YES; 384 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_COMMA = YES; 387 | CLANG_WARN_CONSTANT_CONVERSION = YES; 388 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 389 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 390 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 391 | CLANG_WARN_EMPTY_BODY = YES; 392 | CLANG_WARN_ENUM_CONVERSION = YES; 393 | CLANG_WARN_INFINITE_RECURSION = YES; 394 | CLANG_WARN_INT_CONVERSION = YES; 395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 397 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 399 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 400 | CLANG_WARN_STRICT_PROTOTYPES = YES; 401 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 402 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 403 | CLANG_WARN_UNREACHABLE_CODE = YES; 404 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 405 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 406 | COPY_PHASE_STRIP = NO; 407 | CURRENT_PROJECT_VERSION = 1; 408 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 409 | ENABLE_NS_ASSERTIONS = NO; 410 | ENABLE_STRICT_OBJC_MSGSEND = YES; 411 | GCC_C_LANGUAGE_STANDARD = gnu99; 412 | GCC_NO_COMMON_BLOCKS = YES; 413 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 414 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 415 | GCC_WARN_UNDECLARED_SELECTOR = YES; 416 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 417 | GCC_WARN_UNUSED_FUNCTION = YES; 418 | GCC_WARN_UNUSED_VARIABLE = YES; 419 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 420 | MTL_ENABLE_DEBUG_INFO = NO; 421 | SDKROOT = iphoneos; 422 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 423 | TARGETED_DEVICE_FAMILY = "1,2"; 424 | VALIDATE_PRODUCT = YES; 425 | VERSIONING_SYSTEM = "apple-generic"; 426 | VERSION_INFO_PREFIX = ""; 427 | }; 428 | name = Release; 429 | }; 430 | 92DE878D1DCA51300003D2AB /* Debug */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | CLANG_ENABLE_MODULES = YES; 434 | CODE_SIGN_IDENTITY = ""; 435 | DEFINES_MODULE = YES; 436 | DEVELOPMENT_TEAM = 5MGEMLZRS4; 437 | DYLIB_COMPATIBILITY_VERSION = 1; 438 | DYLIB_CURRENT_VERSION = 1; 439 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 440 | INFOPLIST_FILE = Source/Info.plist; 441 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 442 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 443 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 444 | PRODUCT_BUNDLE_IDENTIFIER = com.alex.AZTransitions; 445 | PRODUCT_NAME = "$(TARGET_NAME)"; 446 | SKIP_INSTALL = YES; 447 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 448 | SWIFT_VERSION = 5.0; 449 | }; 450 | name = Debug; 451 | }; 452 | 92DE878E1DCA51300003D2AB /* Release */ = { 453 | isa = XCBuildConfiguration; 454 | buildSettings = { 455 | CLANG_ENABLE_MODULES = YES; 456 | CODE_SIGN_IDENTITY = ""; 457 | DEFINES_MODULE = YES; 458 | DEVELOPMENT_TEAM = 5MGEMLZRS4; 459 | DYLIB_COMPATIBILITY_VERSION = 1; 460 | DYLIB_CURRENT_VERSION = 1; 461 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 462 | INFOPLIST_FILE = Source/Info.plist; 463 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 464 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 466 | PRODUCT_BUNDLE_IDENTIFIER = com.alex.AZTransitions; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | SKIP_INSTALL = YES; 469 | SWIFT_VERSION = 5.0; 470 | }; 471 | name = Release; 472 | }; 473 | 92DE87CC1DCA54B50003D2AB /* Debug */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 477 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 478 | DEVELOPMENT_TEAM = 5MGEMLZRS4; 479 | INFOPLIST_FILE = "iOS Example/Info.plist"; 480 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 481 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 482 | PRODUCT_BUNDLE_IDENTIFIER = "com.alex.iOS-Example"; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | SWIFT_VERSION = 5.0; 485 | TARGETED_DEVICE_FAMILY = "1,2"; 486 | }; 487 | name = Debug; 488 | }; 489 | 92DE87CD1DCA54B50003D2AB /* Release */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 493 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 494 | DEVELOPMENT_TEAM = 5MGEMLZRS4; 495 | INFOPLIST_FILE = "iOS Example/Info.plist"; 496 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 497 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 498 | PRODUCT_BUNDLE_IDENTIFIER = "com.alex.iOS-Example"; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | SWIFT_VERSION = 5.0; 501 | TARGETED_DEVICE_FAMILY = "1,2"; 502 | }; 503 | name = Release; 504 | }; 505 | /* End XCBuildConfiguration section */ 506 | 507 | /* Begin XCConfigurationList section */ 508 | 92DE877E1DCA51300003D2AB /* Build configuration list for PBXProject "AZTransitions" */ = { 509 | isa = XCConfigurationList; 510 | buildConfigurations = ( 511 | 92DE878A1DCA51300003D2AB /* Debug */, 512 | 92DE878B1DCA51300003D2AB /* Release */, 513 | ); 514 | defaultConfigurationIsVisible = 0; 515 | defaultConfigurationName = Release; 516 | }; 517 | 92DE878C1DCA51300003D2AB /* Build configuration list for PBXNativeTarget "AZTransitions" */ = { 518 | isa = XCConfigurationList; 519 | buildConfigurations = ( 520 | 92DE878D1DCA51300003D2AB /* Debug */, 521 | 92DE878E1DCA51300003D2AB /* Release */, 522 | ); 523 | defaultConfigurationIsVisible = 0; 524 | defaultConfigurationName = Release; 525 | }; 526 | 92DE87CB1DCA54B50003D2AB /* Build configuration list for PBXNativeTarget "iOS Example" */ = { 527 | isa = XCConfigurationList; 528 | buildConfigurations = ( 529 | 92DE87CC1DCA54B50003D2AB /* Debug */, 530 | 92DE87CD1DCA54B50003D2AB /* Release */, 531 | ); 532 | defaultConfigurationIsVisible = 0; 533 | defaultConfigurationName = Release; 534 | }; 535 | /* End XCConfigurationList section */ 536 | }; 537 | rootObject = 92DE877B1DCA51300003D2AB /* Project object */; 538 | } 539 | --------------------------------------------------------------------------------