├── .swift-version
├── images
└── demo.gif
├── TransEasy
├── Assets.xcassets
│ ├── Contents.json
│ ├── qr-64.imageset
│ │ ├── qr-64.png
│ │ ├── qr-64@2x.png
│ │ ├── qr-64@3x.png
│ │ └── Contents.json
│ ├── qr-200.imageset
│ │ ├── qr-200.png
│ │ ├── qr-200@2x.png
│ │ ├── qr-200@3x.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ ├── Icon-1024@1x-1.png
│ │ ├── Icon-29.0@1x.png
│ │ ├── Icon-29.0@2x.png
│ │ ├── Icon-29.0@3x.png
│ │ ├── Icon-40.0@1x.png
│ │ ├── Icon-40.0@2x.png
│ │ ├── Icon-40.0@3x.png
│ │ ├── Icon-60.0@2x.png
│ │ ├── Icon-60.0@3x.png
│ │ ├── Icon-76.0@1x.png
│ │ ├── Icon-76.0@2x.png
│ │ ├── Icon-83.5@2x.png
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── SecondViewController.swift
├── FirstViewController.swift
└── AppDelegate.swift
├── TransEasy.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── CONTRIBUTING.md
├── .swiftlint.yml
├── Package.swift
├── LICENSE.txt
├── .gitignore
├── TransEasy.podspec
├── README.md
└── Sources
└── TransEasy
├── TransEasySegue.swift
├── UIViewController+TransEasy.swift
└── TransEasyAnimationController.swift
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.2
2 |
--------------------------------------------------------------------------------
/images/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/images/demo.gif
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/qr-64.imageset/qr-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/qr-64.imageset/qr-64.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/qr-200.imageset/qr-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/qr-200.imageset/qr-200.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/qr-64.imageset/qr-64@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/qr-64.imageset/qr-64@2x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/qr-64.imageset/qr-64@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/qr-64.imageset/qr-64@3x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/qr-200.imageset/qr-200@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/qr-200.imageset/qr-200@2x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/qr-200.imageset/qr-200@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/qr-200.imageset/qr-200@3x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-1024@1x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-1024@1x-1.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-29.0@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-29.0@1x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-29.0@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-29.0@2x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-29.0@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-29.0@3x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-40.0@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-40.0@1x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-40.0@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-40.0@2x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-40.0@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-40.0@3x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-60.0@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-60.0@2x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-60.0@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-60.0@3x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-76.0@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-76.0@1x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-76.0@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-76.0@2x.png
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohpor/TransEasy/HEAD/TransEasy/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
--------------------------------------------------------------------------------
/TransEasy.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TransEasy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Fork, then clone the repo:
4 |
5 | git clone git@github.com:your-username/TransEasy.git
6 |
7 | Push to your fork and [submit a pull request][pr].
8 |
9 | [pr]: https://github.com/mohpor/TransEasy/compare/
10 |
11 | I will check your pull request and upon success, will merge your changes to the repo.
12 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules: # rule identifiers to exclude from running
2 | - trailing_whitespace
3 | - trailing_newline
4 | - line_length
5 | - function_body_length
6 | - multiple_closures_with_trailing_closure
7 |
8 | identifier_name:
9 | min_length: 2
10 | file_length:
11 | warning: 1000
12 | error: 2000
13 | type_body_length:
14 | warning: 1000
15 | excluded: # paths to ignore during linting. Takes precedence over `included`.
16 | - Build
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/qr-64.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "qr-64.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "qr-64@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "qr-64@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/qr-200.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "qr-200.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "qr-200@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "qr-200@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
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: "TransEasy",
8 | products: [
9 | // Products define the executables and libraries a package produces, and make them visible to other packages.
10 | .library(
11 | name: "TransEasy",
12 | targets: ["TransEasy"])
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 this package depends on.
21 | .target(
22 | name: "TransEasy",
23 | dependencies: [])
24 | ]
25 | )
26 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Mohammad Poroushani
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 |
--------------------------------------------------------------------------------
/TransEasy/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 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/.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 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/Preview.html
64 | fastlane/screenshots
65 | fastlane/test_output
66 |
67 | .DS_Store
68 |
--------------------------------------------------------------------------------
/TransEasy/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 |
--------------------------------------------------------------------------------
/TransEasy.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint TransEasy.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'TransEasy'
11 | s.version = '0.8.0'
12 | s.summary = 'A simple way to have gorgeous transitions.'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | Ever wanted to have that gorgeous zoom transition with your segues but hesitated because it looked way too complex to implement? Well, I thought it is too complex too! Custom Transitions shouldn't be this complex.
22 | DESC
23 |
24 | s.homepage = 'https://github.com/mohpor/TransEasy'
25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
26 | s.license = { :type => 'MIT', :file => 'LICENSE.txt' }
27 | s.author = { 'M. Porooshani' => 'porooshani@gmail.com' }
28 | s.source = { :git => 'https://github.com/mohpor/TransEasy.git', :tag => s.version.to_s }
29 | # s.social_media_url = 'https://twitter.com/mohpor'
30 |
31 | s.ios.deployment_target = '10.0'
32 |
33 | s.source_files = 'Sources/TransEasy**/*'
34 |
35 | # s.resource_bundles = {
36 | # 'TransEasy' => ['TransEasy/Assets/*.png']
37 | # }
38 |
39 | # s.public_header_files = 'Pod/Classes/**/*.h'
40 | # s.frameworks = 'UIKit', 'MapKit'
41 | # s.dependency 'AFNetworking', '~> 2.3'
42 | end
43 |
--------------------------------------------------------------------------------
/TransEasy/SecondViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+TransEasy.swift
3 | // TransEasy
4 | //
5 | // Created by Mohammad Porooshani on 7/21/16.
6 | // Copyright © 2016 Porooshani. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Copyright (c) 2016 Mohammad Poroushani
11 | //
12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
13 | // of this software and associated documentation files (the "Software"), to deal
14 | // in the Software without restriction, including without limitation the rights
15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | // copies of the Software, and to permit persons to whom the Software is
17 | // furnished to do so, subject to the following conditions:
18 | //
19 | // The above copyright notice and this permission notice shall be included in all
20 | // copies or substantial portions of the Software.
21 | //
22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | // SOFTWARE.
29 |
30 | import UIKit
31 |
32 | class SecondViewController: UIViewController {
33 |
34 | @IBOutlet weak var qrImage: UIImageView!
35 | @IBOutlet weak var qrTextView: UITextView!
36 | override func viewDidLoad() {
37 | super.viewDidLoad()
38 | }
39 |
40 | @IBAction func closeButtonClicked(_ sender: AnyObject) {
41 |
42 | if let navC = navigationController {
43 | navC.popViewController(animated: true)
44 | } else {
45 | dismiss(animated: true, completion: nil)
46 | }
47 | }
48 |
49 | override var preferredStatusBarStyle: UIStatusBarStyle {
50 | return .lightContent
51 | }
52 |
53 | }
54 |
55 | extension SecondViewController: TransEasyDestinationViewControllerProtocol {
56 |
57 | func transEasyDestinationView() -> UIView {
58 | return qrImage
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/TransEasy/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "filename" : "Icon-29.0@2x.png",
15 | "idiom" : "iphone",
16 | "scale" : "2x",
17 | "size" : "29x29"
18 | },
19 | {
20 | "filename" : "Icon-29.0@3x.png",
21 | "idiom" : "iphone",
22 | "scale" : "3x",
23 | "size" : "29x29"
24 | },
25 | {
26 | "filename" : "Icon-40.0@2x.png",
27 | "idiom" : "iphone",
28 | "scale" : "2x",
29 | "size" : "40x40"
30 | },
31 | {
32 | "filename" : "Icon-40.0@3x.png",
33 | "idiom" : "iphone",
34 | "scale" : "3x",
35 | "size" : "40x40"
36 | },
37 | {
38 | "filename" : "Icon-60.0@2x.png",
39 | "idiom" : "iphone",
40 | "scale" : "2x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "filename" : "Icon-60.0@3x.png",
45 | "idiom" : "iphone",
46 | "scale" : "3x",
47 | "size" : "60x60"
48 | },
49 | {
50 | "idiom" : "ipad",
51 | "scale" : "1x",
52 | "size" : "20x20"
53 | },
54 | {
55 | "idiom" : "ipad",
56 | "scale" : "2x",
57 | "size" : "20x20"
58 | },
59 | {
60 | "filename" : "Icon-29.0@1x.png",
61 | "idiom" : "ipad",
62 | "scale" : "1x",
63 | "size" : "29x29"
64 | },
65 | {
66 | "filename" : "Icon-29.0@2x.png",
67 | "idiom" : "ipad",
68 | "scale" : "2x",
69 | "size" : "29x29"
70 | },
71 | {
72 | "filename" : "Icon-40.0@1x.png",
73 | "idiom" : "ipad",
74 | "scale" : "1x",
75 | "size" : "40x40"
76 | },
77 | {
78 | "filename" : "Icon-40.0@2x.png",
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "40x40"
82 | },
83 | {
84 | "filename" : "Icon-76.0@1x.png",
85 | "idiom" : "ipad",
86 | "scale" : "1x",
87 | "size" : "76x76"
88 | },
89 | {
90 | "filename" : "Icon-76.0@2x.png",
91 | "idiom" : "ipad",
92 | "scale" : "2x",
93 | "size" : "76x76"
94 | },
95 | {
96 | "filename" : "Icon-83.5@2x.png",
97 | "idiom" : "ipad",
98 | "scale" : "2x",
99 | "size" : "83.5x83.5"
100 | },
101 | {
102 | "filename" : "Icon-1024@1x-1.png",
103 | "idiom" : "ios-marketing",
104 | "scale" : "1x",
105 | "size" : "1024x1024"
106 | }
107 | ],
108 | "info" : {
109 | "author" : "xcode",
110 | "version" : 1
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/TransEasy/FirstViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+TransEasy.swift
3 | // TransEasy
4 | //
5 | // Created by Mohammad Porooshani on 7/21/16.
6 | // Copyright © 2016 Porooshani. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Copyright (c) 2016 Mohammad Poroushani
11 | //
12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
13 | // of this software and associated documentation files (the "Software"), to deal
14 | // in the Software without restriction, including without limitation the rights
15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | // copies of the Software, and to permit persons to whom the Software is
17 | // furnished to do so, subject to the following conditions:
18 | //
19 | // The above copyright notice and this permission notice shall be included in all
20 | // copies or substantial portions of the Software.
21 | //
22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | // SOFTWARE.
29 |
30 | import UIKit
31 |
32 | private let toSecondViewSegueID = "toSecondViewSegue"
33 |
34 | class FirstViewController: UIViewController, TransEasyDestinationViewControllerProtocol {
35 |
36 | @IBOutlet weak var qrButton: UIButton!
37 | @IBOutlet weak var qrLabel: UILabel!
38 | @IBOutlet weak var presentationStyleButton: UIBarButtonItem!
39 |
40 | var isSegueModal = false
41 |
42 | override func viewDidLoad() {
43 | super.viewDidLoad()
44 | }
45 |
46 | override func didReceiveMemoryWarning() {
47 | super.didReceiveMemoryWarning()
48 | }
49 | @IBAction func qrButtonClicked(_ sender: AnyObject) {
50 | guard let destinationViewController = storyboard?.instantiateViewController(withIdentifier: "secondVC") else {
51 | return
52 | }
53 | // This method adds easy trans to the SecondViewController using the provided options for present and dismiss.
54 | setupEasyTransition(on: destinationViewController, presentOptions: TransEasyPresentOptions(duration: 0.4, sourceView: qrButton, blurStyle: UIBlurEffect.Style.dark), dismissOptions: TransEasyDismissOptions(duration: 0.4, destinationView: qrButton, interactive: true))
55 | if isSegueModal {
56 | present(destinationViewController, animated: true, completion: nil)
57 | } else {
58 | performSegue(withIdentifier: toSecondViewSegueID, sender: sender)
59 | }
60 |
61 | }
62 |
63 | func transEasyDestinationView() -> UIView {
64 | return qrButton
65 | }
66 |
67 | @IBAction func modalButtonClicked(_ sender: AnyObject) {
68 | isSegueModal = !isSegueModal
69 | presentationStyleButton.title = "Style: " + (isSegueModal ? "Modal" : "Push")
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/TransEasy/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+TransEasy.swift
3 | // TransEasy
4 | //
5 | // Created by Mohammad Porooshani on 7/21/16.
6 | // Copyright © 2016 Porooshani. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Copyright (c) 2016 Mohammad Poroushani
11 | //
12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
13 | // of this software and associated documentation files (the "Software"), to deal
14 | // in the Software without restriction, including without limitation the rights
15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | // copies of the Software, and to permit persons to whom the Software is
17 | // furnished to do so, subject to the following conditions:
18 | //
19 | // The above copyright notice and this permission notice shall be included in all
20 | // copies or substantial portions of the Software.
21 | //
22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | // SOFTWARE.
29 |
30 | import UIKit
31 |
32 | @UIApplicationMain
33 | class AppDelegate: UIResponder, UIApplicationDelegate {
34 |
35 | var window: UIWindow?
36 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
37 | // Override point for customization after application launch.
38 | return true
39 | }
40 |
41 | func applicationWillResignActive(_ application: UIApplication) {
42 | // 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.
43 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
44 | }
45 |
46 | func applicationDidEnterBackground(_ application: UIApplication) {
47 | // 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.
48 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
49 | }
50 |
51 | func applicationWillEnterForeground(_ application: UIApplication) {
52 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
53 | }
54 |
55 | func applicationDidBecomeActive(_ application: UIApplication) {
56 | // 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.
57 | }
58 |
59 | func applicationWillTerminate(_ application: UIApplication) {
60 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
61 | }
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | # TransEasy
11 | An easy to implement custom transitions.
12 |
13 |

14 |
15 | ## Overview:
16 |
17 | This library will help easily customize your transitions (Modal and Push) so that you can be able to move your views from one to another.
18 |
19 | ---
20 | ## How to setup:
21 |
22 | ### Cocoapods (Recommended)
23 |
24 | 1- In your pod file add:
25 | ```
26 | pod 'TransEasy'
27 | ```
28 | 2- In terminal:
29 | ```
30 | $ pod update
31 | ```
32 |
33 | ### Manual
34 |
35 | Clone or download this repo, add files inside `Source` folder to your project.
36 |
37 | ---
38 |
39 | ## How to use:
40 |
41 | ### Real easy approach:
42 |
43 | In this method you will setup the EasyTrans very easily using a simple method and us it for both push transitions and modal presentations.
44 |
45 | ```swift
46 | func next() {
47 |
48 | guard let destinationViewController = storyboard?.instantiateViewControllerWithIdentifier("secondVC") else {
49 | return
50 | }
51 | // This method adds easy trans to the SecondViewController using the provided options for present and dismiss.
52 |
53 | setupEasyTransition(on: destinationViewController, presentOptions: TransEasyPresentOptions(duration: 0.4, sourceView: qrButton, blurStyle: UIBlurEffectStyle.Dark), dismissOptions: TransEasyDismissOptions(duration: 0.4, destinationView: qrButton, interactive: true))
54 |
55 | if modal {
56 | presentViewController(destinationViewController, animated: true, completion: nil)
57 | } else {
58 | performSegueWithIdentifier(toSecondViewSegueID, sender: sender)
59 | }
60 |
61 | }
62 |
63 | ```
64 |
65 | In the destination view controller:
66 |
67 | ```swift
68 | extension SecondViewController: TransEasyDestinationViewControllerProtocol {
69 |
70 | func transEasyDestinationView() -> UIView {
71 | return qrImage
72 | }
73 |
74 | }
75 |
76 | ```
77 |
78 | And to be able to use TransEasy for pop transitions in source view controller:
79 |
80 | ```swift
81 |
82 | func transEasyDestinationView() -> UIView {
83 | return qrButton
84 | }
85 |
86 | ```
87 |
88 |
89 |
90 | ### Not so easy approach (Modal Presentation Only):
91 | Alternatively, you can implement the `transitioningDelegate` yourself and just use the animator controller.
92 | * In your view controller add required properties to hold animators:
93 |
94 | ```swift
95 |
96 | let presentAnimator: EasyPresentAnimationController = EasyPresentAnimationController()
97 | let dismissAnimator: EasyDismissAnimationController = EasyDismissAnimationController()
98 | ```
99 |
100 | * In `prepareForSegue`, set the `transitioningDelegate`:
101 |
102 | ```swift
103 |
104 | segue.destinationViewController.transitioningDelegate = self
105 |
106 |
107 | ```
108 |
109 | * Extend your view controller to use the **TransEasy** transitions:
110 |
111 |
112 | ```swift
113 |
114 |
115 | extension ViewController: UIViewControllerTransitioningDelegate {
116 |
117 | func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
118 |
119 | guard let secondVC = presented as? SecondViewController else {
120 | return nil
121 | }
122 |
123 | presentAnimator.duration = 0.4
124 | presentAnimator.originalView = qrButton
125 | presentAnimator.destinationView = secondVC.qrImage
126 | presentAnimator.blurEffectStyle = .Dark
127 |
128 |
129 | return presentAnimator
130 | }
131 |
132 | func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
133 |
134 | guard let secondVC = dismissed as? SecondViewController else {
135 | return nil
136 | }
137 | dismissAnimator.duration = 0.4
138 | dismissAnimator.originalView = secondVC.qrImage
139 | dismissAnimator.destinationView = qrButton
140 |
141 | return dismissAnimator
142 | }
143 |
144 | }
145 |
146 | ```
147 |
148 | ---
149 |
150 | ## TODO:
151 |
152 | - [x] Setup basic Structure for project.
153 | - [x] Create demo views and make the relations.
154 | - [x] Create Required Classes and Protocols
155 | - [x] Add License file.
156 | - [x] Add Documentations.
157 | - [x] Add screenshots.
158 | - [ ] Add CI.
159 | - [x] Add Pod support.
160 | - [ ] Make Transitions Interactive.
161 | - [ ] Match Pop Animation to the original animation.
162 | - [ ] Add Present transitioning options for Modal presentations.
163 | - [ ] Add Delegate object and events for transitions.
164 |
--------------------------------------------------------------------------------
/Sources/TransEasy/TransEasySegue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+TransEasy.swift
3 | // TransEasy
4 | //
5 | // Created by Mohammad Porooshani on 7/25/16.
6 | // Copyright © 2016 Porooshani. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Copyright (c) 2016 Mohammad Poroushani
11 | //
12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
13 | // of this software and associated documentation files (the "Software"), to deal
14 | // in the Software without restriction, including without limitation the rights
15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | // copies of the Software, and to permit persons to whom the Software is
17 | // furnished to do so, subject to the following conditions:
18 | //
19 | // The above copyright notice and this permission notice shall be included in all
20 | // copies or substantial portions of the Software.
21 | //
22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | // SOFTWARE.
29 |
30 | import UIKit
31 |
32 | /// A custom segue for EasyTrans animations.
33 | public class TransEasySegue: UIStoryboardSegue {
34 |
35 | /// The source view of the transition. (For TransEasy transitions.)
36 | var sourceView: UIView?
37 |
38 | override public func perform() {
39 |
40 | // Keeping the original frame of the source controller for later use.
41 | let sourceOriginalFrame = source.view.frame
42 | // Starts the actual transition, to generate the required info. We will take control of the transition later on.
43 | super.perform()
44 |
45 | // Find strong references to required objects, or bail on the transitioning if need be.
46 | guard let transitionCoorrdinator = destination.transitionCoordinator,
47 | let secondVC = destination as? TransEasyDestinationViewControllerProtocol,
48 | let sourceV = sourceView ?? (source as? TransEasyDestinationViewControllerProtocol)?.transEasyDestinationView()
49 | else {
50 | // In case something is wrong with the transition, we will perform an appropriate transition animation just in case.
51 | print("Segue is not correctly prepared!")
52 | if let navControl = source.navigationController {
53 | navControl.pushViewController(destination, animated: true)
54 | } else {
55 | source.present(destination, animated: true, completion: nil)
56 | }
57 | return
58 | }
59 |
60 | // Initalizes The view for the destination view controller. (Interestingly enough, this method will cause the destination view controller to be initialized too! should prevent from calling it, because it is causing double instantiations.)
61 | destination.view.layoutIfNeeded()
62 |
63 | let destV = secondVC.transEasyDestinationView()
64 | let containerView = transitionCoorrdinator.containerView
65 |
66 | let originalFrame = sourceV.frame
67 | let destinationFrame = destV.frame
68 |
69 | let sourceSnapshot = sourceV.snapshot()
70 | sourceSnapshot.frame = originalFrame
71 |
72 | let destinationSnapshot = destV.snapshot()
73 | destinationSnapshot.frame = originalFrame
74 | destinationSnapshot.alpha = 0.0
75 |
76 | // Hide original views, while we work on snapshots.
77 | destV.isHidden = true
78 | sourceV.isHidden = true
79 |
80 | let sourceFullSnap = source.view.snapshot()
81 | sourceFullSnap.frame = sourceOriginalFrame
82 |
83 | containerView.addSubview(sourceFullSnap)
84 | source.view.isHidden = true
85 |
86 | // The order of these insertions are important, because that will be the way they are being rendered on top of each other.
87 | containerView.insertSubview(sourceSnapshot, aboveSubview: sourceFullSnap)
88 | containerView.insertSubview(destinationSnapshot, aboveSubview: sourceSnapshot)
89 |
90 | // This is where we start to animate alongside the tranition coordinator.
91 | transitionCoorrdinator.animate(alongsideTransition: { _ in
92 |
93 | containerView.bringSubviewToFront(destinationSnapshot)
94 | containerView.bringSubviewToFront(sourceSnapshot)
95 |
96 | sourceFullSnap.frame = self.source.view.convert(self.source.view.frame, to: containerView)
97 |
98 | sourceSnapshot.frame = destinationFrame
99 | destinationSnapshot.frame = destinationFrame
100 |
101 | sourceSnapshot.alpha = 0.0
102 | destinationSnapshot.alpha = 1.0
103 |
104 | }) { _ in
105 |
106 | sourceSnapshot.removeFromSuperview()
107 | sourceFullSnap.removeFromSuperview()
108 | destinationSnapshot.removeFromSuperview()
109 | destV.isHidden = false
110 | sourceV.isHidden = false
111 | self.source.view.isHidden = false
112 |
113 | }
114 |
115 | }
116 |
117 | }
118 |
119 |
--------------------------------------------------------------------------------
/Sources/TransEasy/UIViewController+TransEasy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+TransEasy.swift
3 | // TransEasy
4 | //
5 | // Created by Mohammad Porooshani on 7/21/16.
6 | // Copyright © 2016 Porooshani. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Copyright (c) 2016 Mohammad Poroushani
11 | //
12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
13 | // of this software and associated documentation files (the "Software"), to deal
14 | // in the Software without restriction, including without limitation the rights
15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | // copies of the Software, and to permit persons to whom the Software is
17 | // furnished to do so, subject to the following conditions:
18 | //
19 | // The above copyright notice and this permission notice shall be included in all
20 | // copies or substantial portions of the Software.
21 | //
22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | // SOFTWARE.
29 |
30 | import UIKit
31 |
32 | /// Holds a reference to the memory for associated delegate object.
33 | var presentAssociatedHandler: UInt8 = 0
34 |
35 | /**
36 | * A simple struct to encapsulate present transition's settings.
37 | * This struct is used only if you use the UIViewController's extension related to TransEasy.
38 | */
39 | public struct TransEasyPresentOptions {
40 | /// The duration of the animation.
41 | public var duration: TimeInterval = 0.4
42 | /// The view present will start from.
43 | public let sourceView: UIView
44 | /// The blur effect to use as background. (If set as nil, won't add blur effect)
45 | public let blurStyle: UIBlurEffect.Style?
46 |
47 | public init(duration: TimeInterval, sourceView: UIView, blurStyle: UIBlurEffect.Style? = nil) {
48 | self.duration = duration
49 | self.sourceView = sourceView
50 | self.blurStyle = blurStyle
51 | }
52 | }
53 |
54 | /**
55 | * A simple struct to encapsulate dismiss transition's settings.
56 | * This struct is used only if you use the UIViewController's extension related to TransEasy.
57 | */
58 | public struct TransEasyDismissOptions {
59 | /// The duration of the animation.
60 | public var duration: TimeInterval = 0.4
61 | /// The view dismiss transition animation will end on.
62 | public let destinationView: UIView
63 | /**
64 | Indicates the dismiss action could be interactive.
65 | - Please be advised that this is a simple suggestion. FOr now, if the presentation style is not modal (push, replace, etc...) this will not add an interactive dismissal.
66 | */
67 | public var interactive = false
68 |
69 | public init(duration: TimeInterval, destinationView: UIView, interactive: Bool = false) {
70 | self.duration = duration
71 | self.destinationView = destinationView
72 | self.interactive = interactive
73 | }
74 |
75 | }
76 |
77 | public extension UIViewController {
78 |
79 | /// The reference to the animator object. The `transitioningDelegate` of the `UIViewController` is of weak type therefore ot will be lost after setup.
80 | internal var easyTransDelegate: EasyPresentHelper? {
81 | get {
82 | return objc_getAssociatedObject(self, &presentAssociatedHandler) as? EasyPresentHelper
83 | }
84 |
85 | set {
86 | objc_setAssociatedObject(self, &presentAssociatedHandler, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
87 |
88 | }
89 | }
90 |
91 | /**
92 | Allows the view controller to have an easy transition from a view controller to another view controller, moving a view in between.
93 |
94 | - parameter targetViewController: The destination of the transition. If you are using a segue, this is the actual `destinationViewController` of the segue.
95 | - parameter presentOptions: The options for present animation.
96 | - parameter dismissOptions: The options for dimiss animation.
97 |
98 | - discussion `targetViewController` should implement `TransEasyDestinationViewControllerProtocol`, because in `prepareForSegue` views are not yet initialized and you wouldn't have access to view's references.
99 |
100 | */
101 | func setupEasyTransition(on targetViewController: UIViewController, presentOptions: TransEasyPresentOptions?, dismissOptions: TransEasyDismissOptions?) {
102 |
103 | let transDel = EasyPresentHelper(presentOptions: presentOptions, dismissOptions: dismissOptions)
104 |
105 | easyTransDelegate = transDel
106 |
107 | if true == dismissOptions?.interactive {
108 | transDel.interactiveAnimator.attach(to: targetViewController)
109 | }
110 |
111 | targetViewController.transitioningDelegate = easyTransDelegate
112 | self.navigationController?.delegate = transDel
113 |
114 | }
115 |
116 | /**
117 | Determines if the presentatin is modal.
118 |
119 | - returns: true if is modally presented, false otherwise.
120 | */
121 | func isModal() -> Bool {
122 | if self.presentingViewController != nil {
123 | return true
124 | }
125 |
126 | if self.presentingViewController?.presentedViewController == self {
127 | return true
128 | }
129 |
130 | if self.navigationController?.presentingViewController?.presentedViewController == self.navigationController {
131 | return true
132 | }
133 |
134 | if self.tabBarController?.presentingViewController is UITabBarController {
135 | return true
136 | }
137 |
138 | return false
139 | }
140 | }
141 |
142 | /// A class that will act as animation controller for the view controllers.
143 | class EasyPresentHelper: NSObject, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
144 |
145 | /// A lazy instance of `EasyPresentAnimationController` that will hold on to present animation controller.
146 | lazy var presentAnimator = EasyPresentAnimationController()
147 | /// A lazy instance of `EasyDismissAnimationController` that will hold on to dimiss animation controller.
148 | lazy var dismissAnimator = EasyDismissAnimationController()
149 | /// A lazy instance of `EasyPopAnimationController` that will hold on to pop animation.
150 | lazy var popAnimator = EasyPopAnimationController()
151 | /// A lazy `EasyInteractiveAnimationController` that will hold on to an interactive animation controller.
152 | lazy var interactiveAnimator = EasyInteractiveAnimationController()
153 | /// The present options.
154 | let presentOptions: TransEasyPresentOptions?
155 | /// The dismiss options.
156 | let dismissOptions: TransEasyDismissOptions?
157 |
158 | init(presentOptions: TransEasyPresentOptions?, dismissOptions: TransEasyDismissOptions?) {
159 | self.presentOptions = presentOptions
160 | self.dismissOptions = dismissOptions
161 | }
162 |
163 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
164 |
165 | /// If setup is not complete, this method will return nil allowing UIKit to use the default transition.
166 | guard let pOptions = presentOptions,
167 | let pDestPro = presented as? TransEasyDestinationViewControllerProtocol
168 | else {
169 | return nil
170 | }
171 |
172 | // Setup animator's settings.
173 | presentAnimator.blurEffectStyle = pOptions.blurStyle
174 | presentAnimator.duration = pOptions.duration
175 | presentAnimator.originalView = pOptions.sourceView
176 | presentAnimator.destinationView = pDestPro.transEasyDestinationView()
177 |
178 | return presentAnimator
179 |
180 | }
181 |
182 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
183 | /// If setup is not complete, this method will return nil allowing UIKit to use the default transition.
184 | guard let dOption = dismissOptions,
185 | let sDestPro = dismissed as? TransEasyDestinationViewControllerProtocol
186 | else {
187 | return nil
188 | }
189 |
190 | // Setup animator's settings.
191 | dismissAnimator.duration = dOption.duration
192 | dismissAnimator.originalView = sDestPro.transEasyDestinationView()
193 | dismissAnimator.destinationView = dOption.destinationView
194 |
195 | return dismissAnimator
196 |
197 | }
198 |
199 | func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
200 |
201 | interactiveAnimator.panDistance = presentAnimator.transitionDistance == 0.0 ? 200.0 : presentAnimator.transitionDistance
202 | return interactiveAnimator.isInteracting ? interactiveAnimator : nil
203 |
204 | }
205 |
206 | func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
207 |
208 | guard operation == .pop else {
209 | return nil
210 | }
211 |
212 | /// If setup is not complete, this method will return nil allowing UIKit to use the default transition.
213 | guard let dOption = dismissOptions,
214 | let sDestPro = fromVC as? TransEasyDestinationViewControllerProtocol
215 | else {
216 | return nil
217 | }
218 |
219 | // Setup animator's settings.
220 | popAnimator.duration = dOption.duration
221 | popAnimator.originalView = sDestPro.transEasyDestinationView()
222 | popAnimator.destinationView = dOption.destinationView
223 |
224 | return popAnimator
225 |
226 | }
227 | }
228 |
229 | /**
230 | * A simple protocol that will be used instead of a property, to let the destination view be selected lazily.
231 | */
232 | public protocol TransEasyDestinationViewControllerProtocol {
233 |
234 | /**
235 | The View that transition will finish on. Please note that this method can be used for dismiss options as well.
236 |
237 | - returns: A UIView that will be used at final view (or initial view for dimiss) transition.
238 | */
239 | func transEasyDestinationView() -> UIView
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/TransEasy/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
48 |
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 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.
110 |
111 |
112 |
113 |
114 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/TransEasy.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 113326CC1D3DE48900D43634 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113326CB1D3DE48900D43634 /* AppDelegate.swift */; };
11 | 113326D11D3DE48900D43634 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 113326CF1D3DE48900D43634 /* Main.storyboard */; };
12 | 113326D31D3DE48900D43634 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 113326D21D3DE48900D43634 /* Assets.xcassets */; };
13 | 113326D61D3DE48900D43634 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 113326D41D3DE48900D43634 /* LaunchScreen.storyboard */; };
14 | 113326E11D3E2DCE00D43634 /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113326E01D3E2DCE00D43634 /* FirstViewController.swift */; };
15 | 113326E31D3E2DDC00D43634 /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113326E21D3E2DDC00D43634 /* SecondViewController.swift */; };
16 | 9517998B26D852C30013C64D /* TransEasyAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9517998826D852C30013C64D /* TransEasyAnimationController.swift */; };
17 | 9517998C26D852C30013C64D /* UIViewController+TransEasy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9517998926D852C30013C64D /* UIViewController+TransEasy.swift */; };
18 | 9517998D26D852C30013C64D /* TransEasySegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9517998A26D852C30013C64D /* TransEasySegue.swift */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | 113326C81D3DE48900D43634 /* TransEasy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TransEasy.app; sourceTree = BUILT_PRODUCTS_DIR; };
23 | 113326CB1D3DE48900D43634 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
24 | 113326D01D3DE48900D43634 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
25 | 113326D21D3DE48900D43634 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
26 | 113326D51D3DE48900D43634 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
27 | 113326D71D3DE48900D43634 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
28 | 113326E01D3E2DCE00D43634 /* FirstViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; };
29 | 113326E21D3E2DDC00D43634 /* SecondViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; };
30 | 9517998826D852C30013C64D /* TransEasyAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransEasyAnimationController.swift; sourceTree = ""; };
31 | 9517998926D852C30013C64D /* UIViewController+TransEasy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+TransEasy.swift"; sourceTree = ""; };
32 | 9517998A26D852C30013C64D /* TransEasySegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransEasySegue.swift; sourceTree = ""; };
33 | /* End PBXFileReference section */
34 |
35 | /* Begin PBXFrameworksBuildPhase section */
36 | 113326C51D3DE48900D43634 /* Frameworks */ = {
37 | isa = PBXFrameworksBuildPhase;
38 | buildActionMask = 2147483647;
39 | files = (
40 | );
41 | runOnlyForDeploymentPostprocessing = 0;
42 | };
43 | /* End PBXFrameworksBuildPhase section */
44 |
45 | /* Begin PBXGroup section */
46 | 113326BF1D3DE48900D43634 = {
47 | isa = PBXGroup;
48 | children = (
49 | 9517998626D852C30013C64D /* Sources */,
50 | 113326CA1D3DE48900D43634 /* TransEasy */,
51 | 113326C91D3DE48900D43634 /* Products */,
52 | );
53 | indentWidth = 2;
54 | sourceTree = "";
55 | tabWidth = 2;
56 | };
57 | 113326C91D3DE48900D43634 /* Products */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 113326C81D3DE48900D43634 /* TransEasy.app */,
61 | );
62 | name = Products;
63 | sourceTree = "";
64 | };
65 | 113326CA1D3DE48900D43634 /* TransEasy */ = {
66 | isa = PBXGroup;
67 | children = (
68 | 113326CB1D3DE48900D43634 /* AppDelegate.swift */,
69 | 113326CF1D3DE48900D43634 /* Main.storyboard */,
70 | 113326D21D3DE48900D43634 /* Assets.xcassets */,
71 | 113326D41D3DE48900D43634 /* LaunchScreen.storyboard */,
72 | 113326D71D3DE48900D43634 /* Info.plist */,
73 | 113326E01D3E2DCE00D43634 /* FirstViewController.swift */,
74 | 113326E21D3E2DDC00D43634 /* SecondViewController.swift */,
75 | );
76 | path = TransEasy;
77 | sourceTree = "";
78 | };
79 | 9517998626D852C30013C64D /* Sources */ = {
80 | isa = PBXGroup;
81 | children = (
82 | 9517998726D852C30013C64D /* TransEasy */,
83 | );
84 | path = Sources;
85 | sourceTree = "";
86 | };
87 | 9517998726D852C30013C64D /* TransEasy */ = {
88 | isa = PBXGroup;
89 | children = (
90 | 9517998826D852C30013C64D /* TransEasyAnimationController.swift */,
91 | 9517998926D852C30013C64D /* UIViewController+TransEasy.swift */,
92 | 9517998A26D852C30013C64D /* TransEasySegue.swift */,
93 | );
94 | path = TransEasy;
95 | sourceTree = "";
96 | };
97 | /* End PBXGroup section */
98 |
99 | /* Begin PBXNativeTarget section */
100 | 113326C71D3DE48900D43634 /* TransEasy */ = {
101 | isa = PBXNativeTarget;
102 | buildConfigurationList = 113326DA1D3DE48900D43634 /* Build configuration list for PBXNativeTarget "TransEasy" */;
103 | buildPhases = (
104 | 113326C41D3DE48900D43634 /* Sources */,
105 | 113326C51D3DE48900D43634 /* Frameworks */,
106 | 113326C61D3DE48900D43634 /* Resources */,
107 | 1127CC7D1D44D02C001686D0 /* ShellScript */,
108 | );
109 | buildRules = (
110 | );
111 | dependencies = (
112 | );
113 | name = TransEasy;
114 | productName = TransEasy;
115 | productReference = 113326C81D3DE48900D43634 /* TransEasy.app */;
116 | productType = "com.apple.product-type.application";
117 | };
118 | /* End PBXNativeTarget section */
119 |
120 | /* Begin PBXProject section */
121 | 113326C01D3DE48900D43634 /* Project object */ = {
122 | isa = PBXProject;
123 | attributes = {
124 | LastSwiftUpdateCheck = 0730;
125 | LastUpgradeCheck = 1250;
126 | ORGANIZATIONNAME = Porooshani;
127 | TargetAttributes = {
128 | 113326C71D3DE48900D43634 = {
129 | CreatedOnToolsVersion = 7.3.1;
130 | LastSwiftMigration = 1250;
131 | };
132 | };
133 | };
134 | buildConfigurationList = 113326C31D3DE48900D43634 /* Build configuration list for PBXProject "TransEasy" */;
135 | compatibilityVersion = "Xcode 3.2";
136 | developmentRegion = en;
137 | hasScannedForEncodings = 0;
138 | knownRegions = (
139 | en,
140 | Base,
141 | );
142 | mainGroup = 113326BF1D3DE48900D43634;
143 | productRefGroup = 113326C91D3DE48900D43634 /* Products */;
144 | projectDirPath = "";
145 | projectRoot = "";
146 | targets = (
147 | 113326C71D3DE48900D43634 /* TransEasy */,
148 | );
149 | };
150 | /* End PBXProject section */
151 |
152 | /* Begin PBXResourcesBuildPhase section */
153 | 113326C61D3DE48900D43634 /* Resources */ = {
154 | isa = PBXResourcesBuildPhase;
155 | buildActionMask = 2147483647;
156 | files = (
157 | 113326D61D3DE48900D43634 /* LaunchScreen.storyboard in Resources */,
158 | 113326D31D3DE48900D43634 /* Assets.xcassets in Resources */,
159 | 113326D11D3DE48900D43634 /* Main.storyboard in Resources */,
160 | );
161 | runOnlyForDeploymentPostprocessing = 0;
162 | };
163 | /* End PBXResourcesBuildPhase section */
164 |
165 | /* Begin PBXShellScriptBuildPhase section */
166 | 1127CC7D1D44D02C001686D0 /* ShellScript */ = {
167 | isa = PBXShellScriptBuildPhase;
168 | buildActionMask = 2147483647;
169 | files = (
170 | );
171 | inputPaths = (
172 | );
173 | outputPaths = (
174 | );
175 | runOnlyForDeploymentPostprocessing = 0;
176 | shellPath = /bin/sh;
177 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
178 | };
179 | /* End PBXShellScriptBuildPhase section */
180 |
181 | /* Begin PBXSourcesBuildPhase section */
182 | 113326C41D3DE48900D43634 /* Sources */ = {
183 | isa = PBXSourcesBuildPhase;
184 | buildActionMask = 2147483647;
185 | files = (
186 | 113326E31D3E2DDC00D43634 /* SecondViewController.swift in Sources */,
187 | 113326CC1D3DE48900D43634 /* AppDelegate.swift in Sources */,
188 | 9517998D26D852C30013C64D /* TransEasySegue.swift in Sources */,
189 | 113326E11D3E2DCE00D43634 /* FirstViewController.swift in Sources */,
190 | 9517998B26D852C30013C64D /* TransEasyAnimationController.swift in Sources */,
191 | 9517998C26D852C30013C64D /* UIViewController+TransEasy.swift in Sources */,
192 | );
193 | runOnlyForDeploymentPostprocessing = 0;
194 | };
195 | /* End PBXSourcesBuildPhase section */
196 |
197 | /* Begin PBXVariantGroup section */
198 | 113326CF1D3DE48900D43634 /* Main.storyboard */ = {
199 | isa = PBXVariantGroup;
200 | children = (
201 | 113326D01D3DE48900D43634 /* Base */,
202 | );
203 | name = Main.storyboard;
204 | sourceTree = "";
205 | };
206 | 113326D41D3DE48900D43634 /* LaunchScreen.storyboard */ = {
207 | isa = PBXVariantGroup;
208 | children = (
209 | 113326D51D3DE48900D43634 /* Base */,
210 | );
211 | name = LaunchScreen.storyboard;
212 | sourceTree = "";
213 | };
214 | /* End PBXVariantGroup section */
215 |
216 | /* Begin XCBuildConfiguration section */
217 | 113326D81D3DE48900D43634 /* Debug */ = {
218 | isa = XCBuildConfiguration;
219 | buildSettings = {
220 | ALWAYS_SEARCH_USER_PATHS = NO;
221 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
222 | CLANG_ANALYZER_NONNULL = YES;
223 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
224 | CLANG_CXX_LIBRARY = "libc++";
225 | CLANG_ENABLE_MODULES = YES;
226 | CLANG_ENABLE_OBJC_ARC = YES;
227 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
228 | CLANG_WARN_BOOL_CONVERSION = YES;
229 | CLANG_WARN_COMMA = YES;
230 | CLANG_WARN_CONSTANT_CONVERSION = YES;
231 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
233 | CLANG_WARN_EMPTY_BODY = YES;
234 | CLANG_WARN_ENUM_CONVERSION = YES;
235 | CLANG_WARN_INFINITE_RECURSION = YES;
236 | CLANG_WARN_INT_CONVERSION = YES;
237 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
238 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
239 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
240 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
241 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
242 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
243 | CLANG_WARN_STRICT_PROTOTYPES = YES;
244 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
245 | CLANG_WARN_UNREACHABLE_CODE = YES;
246 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
247 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
248 | COPY_PHASE_STRIP = NO;
249 | DEBUG_INFORMATION_FORMAT = dwarf;
250 | ENABLE_STRICT_OBJC_MSGSEND = YES;
251 | ENABLE_TESTABILITY = YES;
252 | GCC_C_LANGUAGE_STANDARD = gnu99;
253 | GCC_DYNAMIC_NO_PIC = NO;
254 | GCC_NO_COMMON_BLOCKS = YES;
255 | GCC_OPTIMIZATION_LEVEL = 0;
256 | GCC_PREPROCESSOR_DEFINITIONS = (
257 | "DEBUG=1",
258 | "$(inherited)",
259 | );
260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
261 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
262 | GCC_WARN_UNDECLARED_SELECTOR = YES;
263 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
264 | GCC_WARN_UNUSED_FUNCTION = YES;
265 | GCC_WARN_UNUSED_VARIABLE = YES;
266 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
267 | MTL_ENABLE_DEBUG_INFO = YES;
268 | ONLY_ACTIVE_ARCH = YES;
269 | SDKROOT = iphoneos;
270 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
271 | TARGETED_DEVICE_FAMILY = "1,2";
272 | };
273 | name = Debug;
274 | };
275 | 113326D91D3DE48900D43634 /* Release */ = {
276 | isa = XCBuildConfiguration;
277 | buildSettings = {
278 | ALWAYS_SEARCH_USER_PATHS = NO;
279 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
280 | CLANG_ANALYZER_NONNULL = YES;
281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
282 | CLANG_CXX_LIBRARY = "libc++";
283 | CLANG_ENABLE_MODULES = YES;
284 | CLANG_ENABLE_OBJC_ARC = YES;
285 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
286 | CLANG_WARN_BOOL_CONVERSION = YES;
287 | CLANG_WARN_COMMA = YES;
288 | CLANG_WARN_CONSTANT_CONVERSION = YES;
289 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
290 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
291 | CLANG_WARN_EMPTY_BODY = YES;
292 | CLANG_WARN_ENUM_CONVERSION = YES;
293 | CLANG_WARN_INFINITE_RECURSION = YES;
294 | CLANG_WARN_INT_CONVERSION = YES;
295 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
296 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
297 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
298 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
299 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
300 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
301 | CLANG_WARN_STRICT_PROTOTYPES = YES;
302 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
303 | CLANG_WARN_UNREACHABLE_CODE = YES;
304 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
305 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
306 | COPY_PHASE_STRIP = NO;
307 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
308 | ENABLE_NS_ASSERTIONS = NO;
309 | ENABLE_STRICT_OBJC_MSGSEND = YES;
310 | GCC_C_LANGUAGE_STANDARD = gnu99;
311 | GCC_NO_COMMON_BLOCKS = YES;
312 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
313 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
314 | GCC_WARN_UNDECLARED_SELECTOR = YES;
315 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
316 | GCC_WARN_UNUSED_FUNCTION = YES;
317 | GCC_WARN_UNUSED_VARIABLE = YES;
318 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
319 | MTL_ENABLE_DEBUG_INFO = NO;
320 | SDKROOT = iphoneos;
321 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
322 | TARGETED_DEVICE_FAMILY = "1,2";
323 | VALIDATE_PRODUCT = YES;
324 | };
325 | name = Release;
326 | };
327 | 113326DB1D3DE48900D43634 /* Debug */ = {
328 | isa = XCBuildConfiguration;
329 | buildSettings = {
330 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
331 | CODE_SIGN_IDENTITY = "iPhone Developer";
332 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
333 | DEVELOPMENT_TEAM = "";
334 | INFOPLIST_FILE = TransEasy/Info.plist;
335 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
336 | PRODUCT_BUNDLE_IDENTIFIER = com.porooshani.TransEasy;
337 | PRODUCT_NAME = "$(TARGET_NAME)";
338 | PROVISIONING_PROFILE = "";
339 | SWIFT_VERSION = 5.0;
340 | };
341 | name = Debug;
342 | };
343 | 113326DC1D3DE48900D43634 /* Release */ = {
344 | isa = XCBuildConfiguration;
345 | buildSettings = {
346 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
347 | CODE_SIGN_IDENTITY = "iPhone Developer";
348 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
349 | DEVELOPMENT_TEAM = "";
350 | INFOPLIST_FILE = TransEasy/Info.plist;
351 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
352 | PRODUCT_BUNDLE_IDENTIFIER = com.porooshani.TransEasy;
353 | PRODUCT_NAME = "$(TARGET_NAME)";
354 | PROVISIONING_PROFILE = "";
355 | SWIFT_VERSION = 5.0;
356 | };
357 | name = Release;
358 | };
359 | /* End XCBuildConfiguration section */
360 |
361 | /* Begin XCConfigurationList section */
362 | 113326C31D3DE48900D43634 /* Build configuration list for PBXProject "TransEasy" */ = {
363 | isa = XCConfigurationList;
364 | buildConfigurations = (
365 | 113326D81D3DE48900D43634 /* Debug */,
366 | 113326D91D3DE48900D43634 /* Release */,
367 | );
368 | defaultConfigurationIsVisible = 0;
369 | defaultConfigurationName = Release;
370 | };
371 | 113326DA1D3DE48900D43634 /* Build configuration list for PBXNativeTarget "TransEasy" */ = {
372 | isa = XCConfigurationList;
373 | buildConfigurations = (
374 | 113326DB1D3DE48900D43634 /* Debug */,
375 | 113326DC1D3DE48900D43634 /* Release */,
376 | );
377 | defaultConfigurationIsVisible = 0;
378 | defaultConfigurationName = Release;
379 | };
380 | /* End XCConfigurationList section */
381 | };
382 | rootObject = 113326C01D3DE48900D43634 /* Project object */;
383 | }
384 |
--------------------------------------------------------------------------------
/Sources/TransEasy/TransEasyAnimationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+TransEasy.swift
3 | // TransEasy
4 | //
5 | // Created by Mohammad Porooshani on 7/21/16.
6 | // Copyright © 2016 Porooshani. All rights reserved.
7 | //
8 | // The MIT License (MIT)
9 | //
10 | // Copyright (c) 2016 Mohammad Poroushani
11 | //
12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
13 | // of this software and associated documentation files (the "Software"), to deal
14 | // in the Software without restriction, including without limitation the rights
15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | // copies of the Software, and to permit persons to whom the Software is
17 | // furnished to do so, subject to the following conditions:
18 | //
19 | // The above copyright notice and this permission notice shall be included in all
20 | // copies or substantial portions of the Software.
21 | //
22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | // SOFTWARE.
29 |
30 | import UIKit
31 |
32 | /// Handles animations reqired for the TransEasy Present.
33 | public class EasyPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
34 |
35 | /// The view animation would use as starting point.
36 | public var originalView: UIView?
37 | /// The view that originalView will land to.
38 | public var destinationView: UIView?
39 | /// The duration of animation.
40 | public var duration: TimeInterval = 0.4
41 | /// The background's blur style. If nil, won't add blur effect.
42 | public var blurEffectStyle: UIBlurEffect.Style?
43 |
44 | // Helps figuring the distance views has moved to better handle a possible pan gesture.
45 | internal var transitionDistance: CGFloat = 0.0
46 |
47 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
48 | return duration
49 | }
50 |
51 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
52 |
53 | // Check the integrity of context
54 | guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
55 | let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
56 | let originView = originalView,
57 | let destView = destinationView
58 | else {
59 | print("Transition has not been setup!")
60 | return
61 | }
62 | let containerView = transitionContext.containerView
63 | print("Easy Present")
64 |
65 | // Prepares the presented view before moving on.
66 | toVC.view.frame = transitionContext.finalFrame(for: toVC)
67 | toVC.view.setNeedsDisplay()
68 | toVC.view.layoutIfNeeded()
69 |
70 | // Prepares required snapshots.
71 | let finalFrame = destView.frame
72 | let originalFrame = originView.frame
73 | let fromSnapshot = originView.snapshot()
74 | let toSnapshot = destView.snapshot()
75 |
76 | // Setup snapshot states before starting animations.
77 | fromSnapshot.alpha = 1.0
78 | toSnapshot.alpha = 0.0
79 | toVC.view.alpha = 0.0
80 |
81 | fromSnapshot.frame = originalFrame
82 | toSnapshot.frame = originalFrame
83 |
84 | let xTrans = abs(originalFrame.minX - destView.frame.minX)
85 | let yTrans = abs(originalFrame.minY - destView.frame.minY)
86 | transitionDistance = distance(of: CGPoint(x: xTrans, y: yTrans))
87 |
88 | destView.isHidden = true
89 | originView.isHidden = true
90 |
91 | // Add blur style, in case a blur style has been set.
92 | if let blurStyle = blurEffectStyle {
93 | let fromWholeSnapshot = fromVC.view.snapshot()
94 | let effectView = UIVisualEffectView(effect: UIBlurEffect(style: blurStyle))
95 | effectView.frame = transitionContext.finalFrame(for: toVC)
96 | effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
97 | effectView.addSubview(fromWholeSnapshot)
98 | toVC.view.insertSubview(fromWholeSnapshot, at: 0)
99 | toVC.view.insertSubview(effectView, aboveSubview: fromWholeSnapshot)
100 |
101 | }
102 |
103 | // Adds views to container view to start animations.
104 | containerView.addSubview(toVC.view)
105 | containerView.addSubview(fromSnapshot)
106 | containerView.addSubview(toSnapshot)
107 |
108 | let duration = transitionDuration(using: transitionContext)
109 |
110 | // Animations will be handled with keyframe animations.
111 | UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.calculationModeCubicPaced], animations: {
112 |
113 | // The move animation.
114 | UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1, animations: {
115 | fromSnapshot.frame = finalFrame
116 | toSnapshot.frame = finalFrame
117 | toVC.view.alpha = 1.0
118 | })
119 |
120 | // Fades source view to destination.
121 | UIView.addKeyframe(withRelativeStartTime: 1/2, relativeDuration: 1/2, animations: {
122 | fromSnapshot.alpha = 0.0
123 | toSnapshot.alpha = 1.0
124 | })
125 |
126 | }) { _ in
127 |
128 | // Wrap up final state of the transition.
129 | destView.layoutIfNeeded()
130 | destView.isHidden = false
131 | originView.isHidden = false
132 |
133 | fromSnapshot.removeFromSuperview()
134 | toSnapshot.removeFromSuperview()
135 |
136 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
137 | }
138 |
139 | }
140 |
141 | }
142 |
143 | /// Handles animations reqired for the TransEasy Dismiss.
144 | public class EasyDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
145 |
146 | // The source view dimiss transition starts from.
147 | public var originalView: UIView?
148 | // The view that dimiss will land to.
149 | public var destinationView: UIView?
150 | // Transitions duration.
151 | public var duration: TimeInterval = 0.4
152 |
153 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
154 | return duration
155 | }
156 |
157 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
158 |
159 | // Check the integrity of context
160 | guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
161 | let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
162 | let originView = originalView,
163 | let destView = destinationView
164 | else {
165 | print("Transition has not been setup!")
166 | return
167 | }
168 | let containerView = transitionContext.containerView
169 | print("Easy Dismiss")
170 | // Prepare required info fro transitions.
171 | let finalFrame = destView.frame
172 | let originalFrame = originView.frame
173 | let fromSnapshot = originView.snapshotView(afterScreenUpdates: false)
174 | let toSnapshot = destView.snapshot()
175 |
176 | // Setup initial state of the snapshots and other views.
177 | fromSnapshot?.alpha = 1.0
178 | toSnapshot.alpha = 0.0
179 | fromVC.view.alpha = 1.0
180 | toVC.view.alpha = 1.0
181 | fromSnapshot?.frame = originalFrame
182 | toSnapshot.frame = originalFrame
183 |
184 | originView.isHidden = true
185 | destView.isHidden = true
186 | let fromWholeSnapshot = fromVC.view.snapshot()
187 |
188 | // Add views to transition's container view.
189 | containerView.addSubview(toVC.view)
190 | containerView.addSubview(fromWholeSnapshot)
191 | containerView.addSubview(fromSnapshot!)
192 | containerView.addSubview(toSnapshot)
193 |
194 | let duration = transitionDuration(using: transitionContext)
195 |
196 | // Transition's animation will be handled using keyframe.
197 | UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.calculationModeCubicPaced], animations: {
198 |
199 | // The move transition.
200 | UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1, animations: {
201 | fromSnapshot?.frame = finalFrame
202 | toSnapshot.frame = finalFrame
203 |
204 | })
205 |
206 | UIView.addKeyframe(withRelativeStartTime: 1/4, relativeDuration: 3/4, animations: {
207 | fromWholeSnapshot.alpha = 0.0
208 | })
209 |
210 | // Fade animation from source to destination view.
211 | UIView.addKeyframe(withRelativeStartTime: 1/2, relativeDuration: 1/2, animations: {
212 | fromSnapshot?.alpha = 0.0
213 | toSnapshot.alpha = 1.0
214 | fromVC.view.alpha = 0.0
215 | })
216 |
217 | }) { _ in
218 |
219 | // Wrap up final state of the transitions.
220 | destView.layoutIfNeeded()
221 |
222 | destView.isHidden = false
223 | originView.isHidden = false
224 |
225 | fromSnapshot?.removeFromSuperview()
226 | toSnapshot.removeFromSuperview()
227 | fromWholeSnapshot.removeFromSuperview()
228 |
229 | // For interactive dismissal, we need to know if the transition was cancelled and revert the effect of transition causing source view to be removed from container view.
230 | if transitionContext.transitionWasCancelled {
231 | containerView.addSubview(fromVC.view)
232 | transitionContext.completeTransition(false)
233 | } else {
234 | transitionContext.completeTransition(true)
235 | }
236 |
237 | }
238 |
239 | }
240 |
241 | }
242 |
243 | public class EasyPopAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
244 |
245 | // The source view dimiss transition starts from.
246 | public var originalView: UIView?
247 | // The view that dimiss will land to.
248 | public var destinationView: UIView?
249 | // Transitions duration.
250 | public var duration: TimeInterval = 0.4
251 |
252 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
253 | return duration
254 | }
255 |
256 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
257 |
258 | // Check the integrity of context
259 | guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
260 | let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to),
261 | let originView = originalView,
262 | let destView = destinationView
263 | else {
264 | print("Transition has not been setup!")
265 | return
266 | }
267 | let containerView = transitionContext.containerView
268 | print("Easy Pop")
269 | // Prepare required info fro transitions.
270 | let finalFrame = destView.frame
271 | let originalFrame = originView.frame
272 | let fromSnapshot = originView.snapshotView(afterScreenUpdates: false)
273 | let toSnapshot = destView.snapshot()
274 |
275 | // Setup initial state of the snapshots and other views.
276 | fromSnapshot?.alpha = 1.0
277 | toSnapshot.alpha = 0.0
278 | fromVC.view.alpha = 1.0
279 | toVC.view.alpha = 1.0
280 | fromSnapshot?.frame = originalFrame
281 | toSnapshot.frame = originalFrame
282 |
283 | originView.isHidden = true
284 | destView.isHidden = true
285 | let fromWholeSnapshot = fromVC.view.snapshot()
286 |
287 | // Add views to transition's container view.
288 | toVC.view.frame = toVC.view.frame.offsetBy(dx: -(toVC.view.frame.width / 3.0), dy: 0)
289 | containerView.addSubview(toVC.view)
290 | containerView.addSubview(fromWholeSnapshot)
291 | containerView.addSubview(fromSnapshot!)
292 | containerView.addSubview(toSnapshot)
293 |
294 | let duration = transitionDuration(using: transitionContext)
295 |
296 | // Transition's animation will be handled using keyframe.
297 | UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.calculationModeCubicPaced], animations: {
298 |
299 | // The move transition.
300 | UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1, animations: {
301 | fromSnapshot?.frame = finalFrame
302 | toSnapshot.frame = finalFrame
303 | fromWholeSnapshot.frame = fromWholeSnapshot.frame.offsetBy(dx: fromWholeSnapshot.frame.width, dy: 0)
304 | toVC.view.frame.origin.x = 0
305 | })
306 |
307 | // Fade animation from source to destination view.
308 | UIView.addKeyframe(withRelativeStartTime: 1/2, relativeDuration: 1/2, animations: {
309 | fromSnapshot?.alpha = 0.0
310 | toSnapshot.alpha = 1.0
311 | fromVC.view.alpha = 0.0
312 | })
313 |
314 | }) { _ in
315 |
316 | // Wrap up final state of the transitions.
317 | destView.layoutIfNeeded()
318 |
319 | destView.isHidden = false
320 | originView.isHidden = false
321 |
322 | fromSnapshot?.removeFromSuperview()
323 | toSnapshot.removeFromSuperview()
324 | fromWholeSnapshot.removeFromSuperview()
325 |
326 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
327 | }
328 |
329 | }
330 |
331 | }
332 |
333 | /// Handles Interactive dismissal of Modally presented controllers.
334 | public class EasyInteractiveAnimationController: UIPercentDrivenInteractiveTransition {
335 |
336 | /// Determines whether this instance is interactively dismissing a controller.
337 | var isInteracting = false
338 | /// The amount of pixels user has to pan in order to dismiss the controller. (more than half would be conidered good enough)
339 | var panDistance: CGFloat = 200
340 | /// Determines whether the transition must be finalized (If not cancelled or left before the good enough point in interaction.)
341 | private var shouldFinish = false
342 | /// The view controller to add the interactive dismissal on.
343 | private weak var targetController: UIViewController!
344 |
345 | /**
346 | Applies interactive dismissal to a view controller. uses the view's parameter for gesture recognizer.
347 |
348 | - parameter controller: the controller to add interactive dismissal to.
349 | */
350 | public func attach(to controller: UIViewController) {
351 | targetController = controller
352 | prepareGesture(for: controller.view)
353 | }
354 |
355 | /**
356 | Prepares the required gestures to handle percent driven interactive transitions.
357 |
358 | - parameter view: The view to add the gesture to.
359 | - Currently, we are using pan gesture to handle the touch events.
360 | */
361 | private func prepareGesture(for view: UIView) {
362 | let gesture = UIPanGestureRecognizer(target: self, action: #selector(handle(_:)))
363 | view.addGestureRecognizer(gesture)
364 | }
365 |
366 | /**
367 | Handles the gesture's state change to update the transition's progress.
368 |
369 | - parameter gesture: The gesture events happened on.
370 | */
371 | @objc private func handle(_ gesture: UIPanGestureRecognizer) {
372 |
373 | guard let superView = gesture.view?.superview else {
374 | print("Gesture's not been correctly setup")
375 | return
376 | }
377 |
378 | guard panDistance != 0 else {
379 | print("panLength cannot be 0!")
380 | return
381 | }
382 |
383 | let translation = gesture.translation(in: superView)
384 | var progress: CGFloat = distance(of: translation) / panDistance
385 | progress = min(max(progress, 0.0), 1.0)
386 |
387 | switch gesture.state {
388 |
389 | case .began:
390 | isInteracting = true
391 | targetController.dismiss(animated: true, completion: nil)
392 | case .changed:
393 | shouldFinish = progress > 0.5
394 | update(progress)
395 | case .ended:
396 |
397 | isInteracting = false
398 | if !shouldFinish {
399 | cancel()
400 | } else {
401 | finish()
402 | }
403 | case .cancelled:
404 | isInteracting = false
405 | cancel()
406 | default:
407 | print("Gesture state invalid!")
408 | return
409 | }
410 |
411 | }
412 |
413 | }
414 |
415 | internal func distance(of translation: CGPoint) -> CGFloat {
416 | return hypot(translation.x, translation.y)
417 | }
418 |
419 | // A handy extension to allow snapshotting views. Because UIView's snapshot method messes up auto-layout.
420 | internal extension UIView {
421 | func snapshot() -> UIImageView {
422 | UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
423 | layer.render(in: UIGraphicsGetCurrentContext()!)
424 | let img = UIGraphicsGetImageFromCurrentImageContext()
425 |
426 | return UIImageView(image: img)
427 |
428 | }
429 | }
430 |
431 |
--------------------------------------------------------------------------------