├── DemoAssets
├── SWScreenRecord.gif
├── SWScreenRecord2.gif
└── SWScreenshot1.png
├── LICENCE
├── README.md
├── SWNavigationController.podspec
├── SWNavigationController.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcuserdata
│ └── cewende.xcuserdatad
│ └── xcschemes
│ ├── SWNavigationController.xcscheme
│ └── xcschememanagement.plist
├── SWNavigationController
├── AppDelegate.h
├── AppDelegate.m
├── Base.lproj
│ ├── LaunchScreen.xib
│ └── Main.storyboard
├── Images.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── LastViewController.h
├── LastViewController.m
├── PodFiles
│ ├── SWNavigationController.h
│ ├── SWNavigationController.m
│ ├── SWPushAnimatedTransitioning.h
│ └── SWPushAnimatedTransitioning.m
├── TableViewController.h
├── TableViewController.m
└── main.m
└── SWNavigationControllerTests
├── Info.plist
└── SWNavigationControllerTests.m
/DemoAssets/SWScreenRecord.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CEWendel/SWNavigationController/35b0f6ab79a6b7224e3ddbfafb7b0344b7a922de/DemoAssets/SWScreenRecord.gif
--------------------------------------------------------------------------------
/DemoAssets/SWScreenRecord2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CEWendel/SWNavigationController/35b0f6ab79a6b7224e3ddbfafb7b0344b7a922de/DemoAssets/SWScreenRecord2.gif
--------------------------------------------------------------------------------
/DemoAssets/SWScreenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CEWendel/SWNavigationController/35b0f6ab79a6b7224e3ddbfafb7b0344b7a922de/DemoAssets/SWScreenshot1.png
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Christopher Wendel
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SWNavigationController
2 | ======================
3 |
4 |
5 |
6 | A UINavigationController subclass and corresponding UINavigationControllerDelegate that implements drop-in support for swiping left and right through a view hierarchy.
7 |
8 | ##Installation
9 | In your Podfile:
10 | pod 'SWNavigationController'
11 |
12 | Or just close this repo and manually add the files from the `PodFiles` directory to your project
13 |
14 | ##Usage
15 |
16 | To use `SWNavigationController`, simply change the type of the `UINavigationController`
17 |
18 | * Either in your Storyboard
19 |
20 |
21 |
22 | * Or programmatically in your `AppDelegate`
23 | ```objc
24 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
25 | SWNavigationController *navController = [[SWNavigationController alloc] initWithRootViewController:rootViewController];
26 |
27 | [self.window setRootViewController:navController];
28 |
29 | return YES;
30 | }
31 | ```
32 |
33 | ##Functionality
34 |
35 | ### API
36 | #### Interactive Push Gesture
37 |
38 | ```objc
39 | @property (nonatomic, strong, readonly) UIGestureRecognizer *interactivePushGestureRecognizer;
40 | ```
41 | `SWNavigationController` contains a interactive push gesture recognizer, which behaves opposite of `UINavigationController`'s existing interactive pop gesture recognizer. When a view controller has been popped off the navigation stack it can be pulled back onto the top of the navigation stack by a right edge swipe.
42 |
43 | The interactive push by default attempts to behave similarly to the built-in interactive pop.
44 |
45 |
46 |
47 | #### Push Transition Class
48 |
49 | ```objc
50 | @property (nonatomic, strong) Class pushAnimatedTransitioningClass;
51 | ```
52 | `pushAnimatedTransitioningClass` can be set to override the default interactive push transition that is used by default by `SWNavigationController` when pulling from the right edge of the screen. This class must implement the protocol `UIViewControllerAnimatedTransitioning`.
53 |
54 | #### Pop Transition Class
55 |
56 | ```objc
57 | @property (nonatomic, strong) Class popAnimatedTransitioningClass;
58 | ```
59 | `popAnimatedTransitioningClass` can be set to override `UINavigationController`'s default interactive pop transition when pulling from the left edge of the screen. This class must implement the protocol `UIViewControllerAnimatedTransitioning`.
60 |
61 | ###Features
62 | * Easy drop-in creation using either Storyboards or programmatically
63 | * Default push transition that mimics `UINavigationController`'s pop transition
64 | * Customizable push and pop transitions
65 | * Easily enable and disable pulling view controllers back onto the stack
66 | * iOS 7 and above
67 |
68 | ##Contributing
69 | Use [Github issues](https://github.com/cewendel/SWNavigationController/issues) to track bugs and feature requests.
70 |
71 | ##Contact
72 |
73 | Chris Wendel
74 |
75 | - http://twitter.com/CEWendel
76 |
77 | ## Licence
78 |
79 | MIT
80 |
81 |
82 |
--------------------------------------------------------------------------------
/SWNavigationController.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'SWNavigationController'
3 | s.version = '0.0.1'
4 | s.author = { 'Chris Wendel' => 'chriwend@umich.edu' }
5 | s.homepage = 'https://github.com/CEWendel/SWNavigationController'
6 | s.summary = 'A UINavigationController subclass and corresponding UINavigationControllerDelegate that provides drop-in support for edge-swiping left and right through a view hierarchy.'
7 | s.license = 'MIT'
8 | s.source = { :git => 'https://github.com/CEWendel/SWNavigationController.git', :tag => s.version.to_s }
9 | s.source_files = 'SWNavigationController/PodFiles/*.{h,m}'
10 | s.platform = :ios
11 | s.ios.deployment_target = '7.0'
12 | s.requires_arc = true
13 | end
14 |
--------------------------------------------------------------------------------
/SWNavigationController.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0F10961E1A55DC2800CA67D0 /* LastViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F10961D1A55DC2800CA67D0 /* LastViewController.m */; };
11 | 0F6F14741A548CD400B216EF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F6F14731A548CD400B216EF /* main.m */; };
12 | 0F6F14771A548CD400B216EF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F6F14761A548CD400B216EF /* AppDelegate.m */; };
13 | 0F6F147A1A548CD400B216EF /* TableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F6F14791A548CD400B216EF /* TableViewController.m */; };
14 | 0F6F147D1A548CD400B216EF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0F6F147B1A548CD400B216EF /* Main.storyboard */; };
15 | 0F6F147F1A548CD400B216EF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0F6F147E1A548CD400B216EF /* Images.xcassets */; };
16 | 0F6F14821A548CD400B216EF /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0F6F14801A548CD400B216EF /* LaunchScreen.xib */; };
17 | 0F6F148E1A548CD500B216EF /* SWNavigationControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F6F148D1A548CD500B216EF /* SWNavigationControllerTests.m */; };
18 | 0F6F14A81A55CD4E00B216EF /* SWNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F6F14A41A55CD4E00B216EF /* SWNavigationController.m */; };
19 | 0F6F14A91A55CD4E00B216EF /* SWPushAnimatedTransitioning.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F6F14A61A55CD4E00B216EF /* SWPushAnimatedTransitioning.m */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXContainerItemProxy section */
23 | 0F6F14881A548CD400B216EF /* PBXContainerItemProxy */ = {
24 | isa = PBXContainerItemProxy;
25 | containerPortal = 0F6F14661A548CD400B216EF /* Project object */;
26 | proxyType = 1;
27 | remoteGlobalIDString = 0F6F146D1A548CD400B216EF;
28 | remoteInfo = SWNavigationController;
29 | };
30 | /* End PBXContainerItemProxy section */
31 |
32 | /* Begin PBXFileReference section */
33 | 0F10961C1A55DC2800CA67D0 /* LastViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LastViewController.h; sourceTree = ""; };
34 | 0F10961D1A55DC2800CA67D0 /* LastViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LastViewController.m; sourceTree = ""; };
35 | 0F6F146E1A548CD400B216EF /* SWNavigationController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SWNavigationController.app; sourceTree = BUILT_PRODUCTS_DIR; };
36 | 0F6F14721A548CD400B216EF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
37 | 0F6F14731A548CD400B216EF /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
38 | 0F6F14751A548CD400B216EF /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
39 | 0F6F14761A548CD400B216EF /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
40 | 0F6F14781A548CD400B216EF /* TableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableViewController.h; sourceTree = ""; };
41 | 0F6F14791A548CD400B216EF /* TableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TableViewController.m; sourceTree = ""; };
42 | 0F6F147C1A548CD400B216EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
43 | 0F6F147E1A548CD400B216EF /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
44 | 0F6F14811A548CD400B216EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
45 | 0F6F14871A548CD400B216EF /* SWNavigationControllerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SWNavigationControllerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
46 | 0F6F148C1A548CD500B216EF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
47 | 0F6F148D1A548CD500B216EF /* SWNavigationControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SWNavigationControllerTests.m; sourceTree = ""; };
48 | 0F6F14A41A55CD4E00B216EF /* SWNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SWNavigationController.m; path = PodFiles/SWNavigationController.m; sourceTree = ""; };
49 | 0F6F14A51A55CD4E00B216EF /* SWNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SWNavigationController.h; path = PodFiles/SWNavigationController.h; sourceTree = ""; };
50 | 0F6F14A61A55CD4E00B216EF /* SWPushAnimatedTransitioning.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SWPushAnimatedTransitioning.m; path = PodFiles/SWPushAnimatedTransitioning.m; sourceTree = ""; };
51 | 0F6F14A71A55CD4E00B216EF /* SWPushAnimatedTransitioning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SWPushAnimatedTransitioning.h; path = PodFiles/SWPushAnimatedTransitioning.h; sourceTree = ""; };
52 | /* End PBXFileReference section */
53 |
54 | /* Begin PBXFrameworksBuildPhase section */
55 | 0F6F146B1A548CD400B216EF /* Frameworks */ = {
56 | isa = PBXFrameworksBuildPhase;
57 | buildActionMask = 2147483647;
58 | files = (
59 | );
60 | runOnlyForDeploymentPostprocessing = 0;
61 | };
62 | 0F6F14841A548CD400B216EF /* Frameworks */ = {
63 | isa = PBXFrameworksBuildPhase;
64 | buildActionMask = 2147483647;
65 | files = (
66 | );
67 | runOnlyForDeploymentPostprocessing = 0;
68 | };
69 | /* End PBXFrameworksBuildPhase section */
70 |
71 | /* Begin PBXGroup section */
72 | 0F6F14651A548CD400B216EF = {
73 | isa = PBXGroup;
74 | children = (
75 | 0F6F14701A548CD400B216EF /* SWNavigationController */,
76 | 0F6F148A1A548CD500B216EF /* SWNavigationControllerTests */,
77 | 0F6F146F1A548CD400B216EF /* Products */,
78 | );
79 | sourceTree = "";
80 | };
81 | 0F6F146F1A548CD400B216EF /* Products */ = {
82 | isa = PBXGroup;
83 | children = (
84 | 0F6F146E1A548CD400B216EF /* SWNavigationController.app */,
85 | 0F6F14871A548CD400B216EF /* SWNavigationControllerTests.xctest */,
86 | );
87 | name = Products;
88 | sourceTree = "";
89 | };
90 | 0F6F14701A548CD400B216EF /* SWNavigationController */ = {
91 | isa = PBXGroup;
92 | children = (
93 | 0F6F14971A54910500B216EF /* PodFiles */,
94 | 0F6F14751A548CD400B216EF /* AppDelegate.h */,
95 | 0F6F14761A548CD400B216EF /* AppDelegate.m */,
96 | 0F6F14781A548CD400B216EF /* TableViewController.h */,
97 | 0F6F14791A548CD400B216EF /* TableViewController.m */,
98 | 0F10961C1A55DC2800CA67D0 /* LastViewController.h */,
99 | 0F10961D1A55DC2800CA67D0 /* LastViewController.m */,
100 | 0F6F147B1A548CD400B216EF /* Main.storyboard */,
101 | 0F6F147E1A548CD400B216EF /* Images.xcassets */,
102 | 0F6F14801A548CD400B216EF /* LaunchScreen.xib */,
103 | 0F6F14711A548CD400B216EF /* Supporting Files */,
104 | );
105 | path = SWNavigationController;
106 | sourceTree = "";
107 | };
108 | 0F6F14711A548CD400B216EF /* Supporting Files */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 0F6F14721A548CD400B216EF /* Info.plist */,
112 | 0F6F14731A548CD400B216EF /* main.m */,
113 | );
114 | name = "Supporting Files";
115 | sourceTree = "";
116 | };
117 | 0F6F148A1A548CD500B216EF /* SWNavigationControllerTests */ = {
118 | isa = PBXGroup;
119 | children = (
120 | 0F6F148D1A548CD500B216EF /* SWNavigationControllerTests.m */,
121 | 0F6F148B1A548CD500B216EF /* Supporting Files */,
122 | );
123 | path = SWNavigationControllerTests;
124 | sourceTree = "";
125 | };
126 | 0F6F148B1A548CD500B216EF /* Supporting Files */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 0F6F148C1A548CD500B216EF /* Info.plist */,
130 | );
131 | name = "Supporting Files";
132 | sourceTree = "";
133 | };
134 | 0F6F14971A54910500B216EF /* PodFiles */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 0F6F14A51A55CD4E00B216EF /* SWNavigationController.h */,
138 | 0F6F14A41A55CD4E00B216EF /* SWNavigationController.m */,
139 | 0F6F14A71A55CD4E00B216EF /* SWPushAnimatedTransitioning.h */,
140 | 0F6F14A61A55CD4E00B216EF /* SWPushAnimatedTransitioning.m */,
141 | );
142 | name = PodFiles;
143 | sourceTree = "";
144 | };
145 | /* End PBXGroup section */
146 |
147 | /* Begin PBXNativeTarget section */
148 | 0F6F146D1A548CD400B216EF /* SWNavigationController */ = {
149 | isa = PBXNativeTarget;
150 | buildConfigurationList = 0F6F14911A548CD500B216EF /* Build configuration list for PBXNativeTarget "SWNavigationController" */;
151 | buildPhases = (
152 | 0F6F146A1A548CD400B216EF /* Sources */,
153 | 0F6F146B1A548CD400B216EF /* Frameworks */,
154 | 0F6F146C1A548CD400B216EF /* Resources */,
155 | );
156 | buildRules = (
157 | );
158 | dependencies = (
159 | );
160 | name = SWNavigationController;
161 | productName = SWNavigationController;
162 | productReference = 0F6F146E1A548CD400B216EF /* SWNavigationController.app */;
163 | productType = "com.apple.product-type.application";
164 | };
165 | 0F6F14861A548CD400B216EF /* SWNavigationControllerTests */ = {
166 | isa = PBXNativeTarget;
167 | buildConfigurationList = 0F6F14941A548CD500B216EF /* Build configuration list for PBXNativeTarget "SWNavigationControllerTests" */;
168 | buildPhases = (
169 | 0F6F14831A548CD400B216EF /* Sources */,
170 | 0F6F14841A548CD400B216EF /* Frameworks */,
171 | 0F6F14851A548CD400B216EF /* Resources */,
172 | );
173 | buildRules = (
174 | );
175 | dependencies = (
176 | 0F6F14891A548CD400B216EF /* PBXTargetDependency */,
177 | );
178 | name = SWNavigationControllerTests;
179 | productName = SWNavigationControllerTests;
180 | productReference = 0F6F14871A548CD400B216EF /* SWNavigationControllerTests.xctest */;
181 | productType = "com.apple.product-type.bundle.unit-test";
182 | };
183 | /* End PBXNativeTarget section */
184 |
185 | /* Begin PBXProject section */
186 | 0F6F14661A548CD400B216EF /* Project object */ = {
187 | isa = PBXProject;
188 | attributes = {
189 | LastUpgradeCheck = 0610;
190 | ORGANIZATIONNAME = "Christopher Wendel";
191 | TargetAttributes = {
192 | 0F6F146D1A548CD400B216EF = {
193 | CreatedOnToolsVersion = 6.1;
194 | };
195 | 0F6F14861A548CD400B216EF = {
196 | CreatedOnToolsVersion = 6.1;
197 | TestTargetID = 0F6F146D1A548CD400B216EF;
198 | };
199 | };
200 | };
201 | buildConfigurationList = 0F6F14691A548CD400B216EF /* Build configuration list for PBXProject "SWNavigationController" */;
202 | compatibilityVersion = "Xcode 3.2";
203 | developmentRegion = English;
204 | hasScannedForEncodings = 0;
205 | knownRegions = (
206 | en,
207 | Base,
208 | );
209 | mainGroup = 0F6F14651A548CD400B216EF;
210 | productRefGroup = 0F6F146F1A548CD400B216EF /* Products */;
211 | projectDirPath = "";
212 | projectRoot = "";
213 | targets = (
214 | 0F6F146D1A548CD400B216EF /* SWNavigationController */,
215 | 0F6F14861A548CD400B216EF /* SWNavigationControllerTests */,
216 | );
217 | };
218 | /* End PBXProject section */
219 |
220 | /* Begin PBXResourcesBuildPhase section */
221 | 0F6F146C1A548CD400B216EF /* Resources */ = {
222 | isa = PBXResourcesBuildPhase;
223 | buildActionMask = 2147483647;
224 | files = (
225 | 0F6F147D1A548CD400B216EF /* Main.storyboard in Resources */,
226 | 0F6F14821A548CD400B216EF /* LaunchScreen.xib in Resources */,
227 | 0F6F147F1A548CD400B216EF /* Images.xcassets in Resources */,
228 | );
229 | runOnlyForDeploymentPostprocessing = 0;
230 | };
231 | 0F6F14851A548CD400B216EF /* Resources */ = {
232 | isa = PBXResourcesBuildPhase;
233 | buildActionMask = 2147483647;
234 | files = (
235 | );
236 | runOnlyForDeploymentPostprocessing = 0;
237 | };
238 | /* End PBXResourcesBuildPhase section */
239 |
240 | /* Begin PBXSourcesBuildPhase section */
241 | 0F6F146A1A548CD400B216EF /* Sources */ = {
242 | isa = PBXSourcesBuildPhase;
243 | buildActionMask = 2147483647;
244 | files = (
245 | 0F6F147A1A548CD400B216EF /* TableViewController.m in Sources */,
246 | 0F6F14A81A55CD4E00B216EF /* SWNavigationController.m in Sources */,
247 | 0F6F14771A548CD400B216EF /* AppDelegate.m in Sources */,
248 | 0F6F14A91A55CD4E00B216EF /* SWPushAnimatedTransitioning.m in Sources */,
249 | 0F10961E1A55DC2800CA67D0 /* LastViewController.m in Sources */,
250 | 0F6F14741A548CD400B216EF /* main.m in Sources */,
251 | );
252 | runOnlyForDeploymentPostprocessing = 0;
253 | };
254 | 0F6F14831A548CD400B216EF /* Sources */ = {
255 | isa = PBXSourcesBuildPhase;
256 | buildActionMask = 2147483647;
257 | files = (
258 | 0F6F148E1A548CD500B216EF /* SWNavigationControllerTests.m in Sources */,
259 | );
260 | runOnlyForDeploymentPostprocessing = 0;
261 | };
262 | /* End PBXSourcesBuildPhase section */
263 |
264 | /* Begin PBXTargetDependency section */
265 | 0F6F14891A548CD400B216EF /* PBXTargetDependency */ = {
266 | isa = PBXTargetDependency;
267 | target = 0F6F146D1A548CD400B216EF /* SWNavigationController */;
268 | targetProxy = 0F6F14881A548CD400B216EF /* PBXContainerItemProxy */;
269 | };
270 | /* End PBXTargetDependency section */
271 |
272 | /* Begin PBXVariantGroup section */
273 | 0F6F147B1A548CD400B216EF /* Main.storyboard */ = {
274 | isa = PBXVariantGroup;
275 | children = (
276 | 0F6F147C1A548CD400B216EF /* Base */,
277 | );
278 | name = Main.storyboard;
279 | sourceTree = "";
280 | };
281 | 0F6F14801A548CD400B216EF /* LaunchScreen.xib */ = {
282 | isa = PBXVariantGroup;
283 | children = (
284 | 0F6F14811A548CD400B216EF /* Base */,
285 | );
286 | name = LaunchScreen.xib;
287 | sourceTree = "";
288 | };
289 | /* End PBXVariantGroup section */
290 |
291 | /* Begin XCBuildConfiguration section */
292 | 0F6F148F1A548CD500B216EF /* Debug */ = {
293 | isa = XCBuildConfiguration;
294 | buildSettings = {
295 | ALWAYS_SEARCH_USER_PATHS = NO;
296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
297 | CLANG_CXX_LIBRARY = "libc++";
298 | CLANG_ENABLE_MODULES = YES;
299 | CLANG_ENABLE_OBJC_ARC = YES;
300 | CLANG_WARN_BOOL_CONVERSION = YES;
301 | CLANG_WARN_CONSTANT_CONVERSION = YES;
302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
303 | CLANG_WARN_EMPTY_BODY = YES;
304 | CLANG_WARN_ENUM_CONVERSION = YES;
305 | CLANG_WARN_INT_CONVERSION = YES;
306 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
307 | CLANG_WARN_UNREACHABLE_CODE = YES;
308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
309 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
310 | COPY_PHASE_STRIP = NO;
311 | ENABLE_STRICT_OBJC_MSGSEND = YES;
312 | GCC_C_LANGUAGE_STANDARD = gnu99;
313 | GCC_DYNAMIC_NO_PIC = NO;
314 | GCC_OPTIMIZATION_LEVEL = 0;
315 | GCC_PREPROCESSOR_DEFINITIONS = (
316 | "DEBUG=1",
317 | "$(inherited)",
318 | );
319 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
320 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
321 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
322 | GCC_WARN_UNDECLARED_SELECTOR = YES;
323 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
324 | GCC_WARN_UNUSED_FUNCTION = YES;
325 | GCC_WARN_UNUSED_VARIABLE = YES;
326 | IPHONEOS_DEPLOYMENT_TARGET = 8.1;
327 | MTL_ENABLE_DEBUG_INFO = YES;
328 | ONLY_ACTIVE_ARCH = YES;
329 | SDKROOT = iphoneos;
330 | TARGETED_DEVICE_FAMILY = "1,2";
331 | };
332 | name = Debug;
333 | };
334 | 0F6F14901A548CD500B216EF /* Release */ = {
335 | isa = XCBuildConfiguration;
336 | buildSettings = {
337 | ALWAYS_SEARCH_USER_PATHS = NO;
338 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
339 | CLANG_CXX_LIBRARY = "libc++";
340 | CLANG_ENABLE_MODULES = YES;
341 | CLANG_ENABLE_OBJC_ARC = YES;
342 | CLANG_WARN_BOOL_CONVERSION = YES;
343 | CLANG_WARN_CONSTANT_CONVERSION = YES;
344 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
345 | CLANG_WARN_EMPTY_BODY = YES;
346 | CLANG_WARN_ENUM_CONVERSION = YES;
347 | CLANG_WARN_INT_CONVERSION = YES;
348 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
349 | CLANG_WARN_UNREACHABLE_CODE = YES;
350 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
351 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
352 | COPY_PHASE_STRIP = YES;
353 | ENABLE_NS_ASSERTIONS = NO;
354 | ENABLE_STRICT_OBJC_MSGSEND = YES;
355 | GCC_C_LANGUAGE_STANDARD = gnu99;
356 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
357 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
358 | GCC_WARN_UNDECLARED_SELECTOR = YES;
359 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
360 | GCC_WARN_UNUSED_FUNCTION = YES;
361 | GCC_WARN_UNUSED_VARIABLE = YES;
362 | IPHONEOS_DEPLOYMENT_TARGET = 8.1;
363 | MTL_ENABLE_DEBUG_INFO = NO;
364 | SDKROOT = iphoneos;
365 | TARGETED_DEVICE_FAMILY = "1,2";
366 | VALIDATE_PRODUCT = YES;
367 | };
368 | name = Release;
369 | };
370 | 0F6F14921A548CD500B216EF /* Debug */ = {
371 | isa = XCBuildConfiguration;
372 | buildSettings = {
373 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
374 | INFOPLIST_FILE = SWNavigationController/Info.plist;
375 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
376 | PRODUCT_NAME = "$(TARGET_NAME)";
377 | };
378 | name = Debug;
379 | };
380 | 0F6F14931A548CD500B216EF /* Release */ = {
381 | isa = XCBuildConfiguration;
382 | buildSettings = {
383 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
384 | INFOPLIST_FILE = SWNavigationController/Info.plist;
385 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
386 | PRODUCT_NAME = "$(TARGET_NAME)";
387 | };
388 | name = Release;
389 | };
390 | 0F6F14951A548CD500B216EF /* Debug */ = {
391 | isa = XCBuildConfiguration;
392 | buildSettings = {
393 | BUNDLE_LOADER = "$(TEST_HOST)";
394 | FRAMEWORK_SEARCH_PATHS = (
395 | "$(SDKROOT)/Developer/Library/Frameworks",
396 | "$(inherited)",
397 | );
398 | GCC_PREPROCESSOR_DEFINITIONS = (
399 | "DEBUG=1",
400 | "$(inherited)",
401 | );
402 | INFOPLIST_FILE = SWNavigationControllerTests/Info.plist;
403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
404 | PRODUCT_NAME = "$(TARGET_NAME)";
405 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SWNavigationController.app/SWNavigationController";
406 | };
407 | name = Debug;
408 | };
409 | 0F6F14961A548CD500B216EF /* Release */ = {
410 | isa = XCBuildConfiguration;
411 | buildSettings = {
412 | BUNDLE_LOADER = "$(TEST_HOST)";
413 | FRAMEWORK_SEARCH_PATHS = (
414 | "$(SDKROOT)/Developer/Library/Frameworks",
415 | "$(inherited)",
416 | );
417 | INFOPLIST_FILE = SWNavigationControllerTests/Info.plist;
418 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
419 | PRODUCT_NAME = "$(TARGET_NAME)";
420 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SWNavigationController.app/SWNavigationController";
421 | };
422 | name = Release;
423 | };
424 | /* End XCBuildConfiguration section */
425 |
426 | /* Begin XCConfigurationList section */
427 | 0F6F14691A548CD400B216EF /* Build configuration list for PBXProject "SWNavigationController" */ = {
428 | isa = XCConfigurationList;
429 | buildConfigurations = (
430 | 0F6F148F1A548CD500B216EF /* Debug */,
431 | 0F6F14901A548CD500B216EF /* Release */,
432 | );
433 | defaultConfigurationIsVisible = 0;
434 | defaultConfigurationName = Release;
435 | };
436 | 0F6F14911A548CD500B216EF /* Build configuration list for PBXNativeTarget "SWNavigationController" */ = {
437 | isa = XCConfigurationList;
438 | buildConfigurations = (
439 | 0F6F14921A548CD500B216EF /* Debug */,
440 | 0F6F14931A548CD500B216EF /* Release */,
441 | );
442 | defaultConfigurationIsVisible = 0;
443 | defaultConfigurationName = Release;
444 | };
445 | 0F6F14941A548CD500B216EF /* Build configuration list for PBXNativeTarget "SWNavigationControllerTests" */ = {
446 | isa = XCConfigurationList;
447 | buildConfigurations = (
448 | 0F6F14951A548CD500B216EF /* Debug */,
449 | 0F6F14961A548CD500B216EF /* Release */,
450 | );
451 | defaultConfigurationIsVisible = 0;
452 | defaultConfigurationName = Release;
453 | };
454 | /* End XCConfigurationList section */
455 | };
456 | rootObject = 0F6F14661A548CD400B216EF /* Project object */;
457 | }
458 |
--------------------------------------------------------------------------------
/SWNavigationController.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SWNavigationController.xcodeproj/xcuserdata/cewende.xcuserdatad/xcschemes/SWNavigationController.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
75 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
94 |
100 |
101 |
102 |
103 |
105 |
106 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/SWNavigationController.xcodeproj/xcuserdata/cewende.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SWNavigationController.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 0F6F146D1A548CD400B216EF
16 |
17 | primary
18 |
19 |
20 | 0F6F14861A548CD400B216EF
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/SWNavigationController/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/SWNavigationController/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // 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.
25 | // 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.
26 | }
27 |
28 | - (void)applicationDidEnterBackground:(UIApplication *)application {
29 | // 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.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | - (void)applicationWillEnterForeground:(UIApplication *)application {
34 | // 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.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application {
38 | // 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.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/SWNavigationController/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/SWNavigationController/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
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 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
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 |
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/SWNavigationController/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/SWNavigationController/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.CEWendel.$(PRODUCT_NAME:rfc1034identifier)
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 |
--------------------------------------------------------------------------------
/SWNavigationController/LastViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // LastViewController.h
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 1/1/15.
6 | // Copyright (c) 2015 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LastViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/SWNavigationController/LastViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // LastViewController.m
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 1/1/15.
6 | // Copyright (c) 2015 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import "LastViewController.h"
10 |
11 | @interface LastViewController ()
12 |
13 | @end
14 |
15 | @implementation LastViewController
16 |
17 | - (void)viewDidLoad {
18 | [super viewDidLoad];
19 | // Do any additional setup after loading the view.
20 | }
21 |
22 | - (void)didReceiveMemoryWarning {
23 | [super didReceiveMemoryWarning];
24 | // Dispose of any resources that can be recreated.
25 | }
26 |
27 | #pragma mark - IBActions
28 |
29 | - (IBAction)popPressed:(id)sender
30 | {
31 | [self.navigationController popToRootViewControllerAnimated:YES];
32 | }
33 |
34 | /*
35 | #pragma mark - Navigation
36 |
37 | // In a storyboard-based application, you will often want to do a little preparation before navigation
38 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
39 | // Get the new view controller using [segue destinationViewController].
40 | // Pass the selected object to the new view controller.
41 | }
42 | */
43 |
44 | @end
45 |
--------------------------------------------------------------------------------
/SWNavigationController/PodFiles/SWNavigationController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SWNavigationController.h
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SWNavigationController : UINavigationController
12 |
13 | /** A gesture recognizer responsible for pushing the most recently popped view controller back onto the navigation stack. (read-only)
14 | *
15 | * Handles the right-to-left edge swipe gesture. Disable this gesture recognizer to disable the interactive push of the next view controller.
16 | * Enabled by default
17 | */
18 | @property (nonatomic, strong, readonly) UIGestureRecognizer *interactivePushGestureRecognizer;
19 |
20 | /** The Class that handles the push transition when a view controller is pushed onto the navigation stack.
21 | *
22 | * A new instance of this Class is initialized when a push occurs.
23 | * The Class must implement the protocol UIViewControllerAnimatedTransitioning.
24 | * By default this is set to SWPushAnimatedTransitioning.
25 | */
26 | @property (nonatomic, strong) Class pushAnimatedTransitioningClass;
27 |
28 |
29 | /** The Class that handles the pop transition when a view controller is popped off the navigation stack.
30 | *
31 | * A new instance of this Class is initialized when a pop occurs.
32 | * The Class must implement the protocol UIViewControllerAnimatedTransitioning
33 | * If this is nil, UINavigationController's default pop animation will be used.
34 | */
35 | @property (nonatomic, strong) Class popAnimatedTransitioningClass;
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/SWNavigationController/PodFiles/SWNavigationController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SWNavigationController.m
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import "SWNavigationController.h"
10 | #import "SWPushAnimatedTransitioning.h"
11 |
12 | #define kSWGestureVelocityThreshold 800
13 |
14 | typedef void (^SWNavigationControllerPushCompletion)(void);
15 |
16 | @interface SWNavigationController () {
17 | UIScreenEdgePanGestureRecognizer *_interactivePushGestureRecognizer;
18 | }
19 |
20 | @property (nonatomic, strong) UIPercentDrivenInteractiveTransition *percentDrivenInteractiveTransition;
21 | @property (nonatomic, strong, readwrite) UIScreenEdgePanGestureRecognizer *interactivePushGestureRecognizer;
22 |
23 | @property (nonatomic, strong) NSMutableArray *pushableViewControllers; // View controllers we can push onto the navigation stack by pulling in from the right screen edge.
24 |
25 | // Extra state used to implement completion blocks on pushViewController:
26 | @property (nonatomic, copy) SWNavigationControllerPushCompletion pushCompletion;
27 | @property (nonatomic, strong) UIViewController *pushedViewController;
28 |
29 | @end
30 |
31 | @implementation SWNavigationController
32 |
33 | #pragma mark - Initializers
34 |
35 | -(instancetype)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass
36 | {
37 | self = [super initWithNavigationBarClass:navigationBarClass toolbarClass:toolbarClass];
38 |
39 | if (self) {
40 | [self setup];
41 | }
42 |
43 | return self;
44 | }
45 |
46 | - (instancetype)initWithCoder:(NSCoder *)aDecoder
47 | {
48 | self = [super initWithCoder:aDecoder];
49 |
50 | if (self) {
51 | [self setup];
52 | }
53 |
54 | return self;
55 | }
56 |
57 | - (instancetype)initWithRootViewController:(UIViewController *)rootViewController
58 | {
59 | self = [super initWithRootViewController:rootViewController];
60 |
61 | if (self) {
62 | [self setup];
63 | }
64 |
65 | return self;
66 | }
67 |
68 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
69 | {
70 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
71 |
72 | if (self) {
73 | [self setup];
74 | }
75 |
76 | return self;
77 | }
78 |
79 | - (void)setup
80 | {
81 | _pushableViewControllers = [NSMutableArray array];
82 |
83 | self.delegate = self;
84 |
85 | // By default, we use SWPushAnimatedTransitioning, which is a clone of the default push transition
86 | self.pushAnimatedTransitioningClass = [SWPushAnimatedTransitioning class];
87 | }
88 |
89 | - (void)viewDidLoad {
90 | [super viewDidLoad];
91 |
92 | _interactivePushGestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightSwipe:)];
93 | _interactivePushGestureRecognizer.edges = UIRectEdgeRight;
94 | self.interactivePushGestureRecognizer.delegate = self;
95 | [self.view addGestureRecognizer:self.interactivePushGestureRecognizer];
96 |
97 | // To ensure swipe-back is still recognized
98 | self.interactivePopGestureRecognizer.delegate = self;
99 | }
100 |
101 | - (void)didReceiveMemoryWarning {
102 | [super didReceiveMemoryWarning];
103 |
104 | [_pushableViewControllers removeAllObjects];
105 | }
106 |
107 | #pragma mark - Gesture Handlers
108 |
109 | - (void)handleRightSwipe:(UIScreenEdgePanGestureRecognizer *)swipeGestureRecognizer
110 | {
111 | CGFloat progress = ABS(-[swipeGestureRecognizer translationInView:self.view].x / self.view.frame.size.width); // 1.0 When the pushable vc has been pulled into place
112 |
113 | // Start, update, or finish the interactive push transition
114 | switch (swipeGestureRecognizer.state) {
115 | case UIGestureRecognizerStateBegan:
116 | [self pushNextViewControllerFromRight];
117 | break;
118 | case UIGestureRecognizerStateChanged:
119 | [self.percentDrivenInteractiveTransition updateInteractiveTransition:progress];
120 | break;
121 | case UIGestureRecognizerStateEnded:
122 | // Figure out if we should finish the transition or not
123 | [self handleEdgeSwipeEndedWithProgress:progress velocity:[swipeGestureRecognizer velocityInView:self.view].x];
124 | break;
125 | case UIGestureRecognizerStateFailed:
126 | [self.percentDrivenInteractiveTransition cancelInteractiveTransition];
127 | break;
128 | case UIGestureRecognizerStateCancelled:
129 | case UIGestureRecognizerStatePossible:
130 | default:
131 | break;
132 | }
133 | }
134 |
135 | - (void)handleEdgeSwipeEndedWithProgress:(CGFloat)progress velocity:(CGFloat)velocity
136 | {
137 | // kSWGestureVelocityThreshold threshold indicates how hard the finger has to flick left to finish the push transition
138 | if (velocity < 0 && (progress > 0.5 || velocity < -kSWGestureVelocityThreshold))
139 | [self.percentDrivenInteractiveTransition finishInteractiveTransition];
140 | else
141 | [self.percentDrivenInteractiveTransition cancelInteractiveTransition];
142 | }
143 |
144 | #pragma mark - UIGestureRecognizerDelegate
145 |
146 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
147 | {
148 | BOOL shouldBegin = NO;
149 |
150 | if (gestureRecognizer == self.interactivePushGestureRecognizer) {
151 | shouldBegin = self.pushableViewControllers.count > 0 && !(self.pushableViewControllers.lastObject == self.topViewController);
152 | } else {
153 | shouldBegin = self.viewControllers.count > 1;
154 | }
155 |
156 | return shouldBegin;
157 | }
158 |
159 | #pragma mark - UINavigationController
160 |
161 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
162 | {
163 | [self.pushableViewControllers removeAllObjects];
164 |
165 | [super pushViewController:viewController animated:animated];
166 | }
167 |
168 | - (UIViewController *)popViewControllerAnimated:(BOOL)animated
169 | {
170 | // Dismiss the current view controllers keyboard (if it is displaying one), to avoid first responder problems when pushing back onto the stack
171 | [self.topViewController.view endEditing:YES];
172 |
173 | UIViewController *poppedViewController = [super popViewControllerAnimated:animated];
174 | [self.pushableViewControllers addObject:poppedViewController];
175 | return poppedViewController;
176 | }
177 |
178 | - (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
179 | {
180 | NSArray *poppedViewControllers = [super popToViewController:viewController animated:animated];
181 |
182 | self.pushableViewControllers = [NSMutableArray arrayWithArray:[[poppedViewControllers reverseObjectEnumerator] allObjects]];
183 |
184 | return poppedViewControllers;
185 | }
186 |
187 | - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
188 | {
189 | NSArray *poppedViewControllers = [super popToRootViewControllerAnimated:YES];
190 |
191 | self.pushableViewControllers = [NSMutableArray arrayWithArray:[[poppedViewControllers reverseObjectEnumerator] allObjects]];
192 |
193 | return poppedViewControllers;
194 | }
195 |
196 | - (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated
197 | {
198 | [super setViewControllers:viewControllers animated:animated];
199 |
200 | [self.pushableViewControllers removeAllObjects];
201 | }
202 |
203 | #pragma mark -
204 |
205 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(SWNavigationControllerPushCompletion)completion
206 | {
207 | self.pushedViewController = viewController;
208 | self.pushCompletion = completion;
209 | [super pushViewController:viewController animated:animated];
210 | }
211 |
212 | - (void)pushNextViewControllerFromRight
213 | {
214 | UIViewController *pushedViewController = [self.pushableViewControllers lastObject];
215 |
216 | if (pushedViewController && self.visibleViewController && !self.visibleViewController.isBeingPresented && !self.visibleViewController.isBeingDismissed) {
217 | [self pushViewController:pushedViewController animated:YES completion:^{
218 | [self.pushableViewControllers removeLastObject];
219 | }];
220 | }
221 | }
222 |
223 | #pragma mark - UINavigationControllerDelegate
224 |
225 | - (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
226 | {
227 | // If we are either pulling in a new VC onto the stack or we have a custom pushAnimatedTransitioningClass that we want to use to transition
228 | if (operation == UINavigationControllerOperationPush && ([[(SWNavigationController *)navigationController interactivePushGestureRecognizer] state] == UIGestureRecognizerStateBegan || (self.pushAnimatedTransitioningClass != [SWPushAnimatedTransitioning class]))) {
229 | return [self.pushAnimatedTransitioningClass new];
230 | } else if (operation == UINavigationControllerOperationPop && self.popAnimatedTransitioningClass) {
231 | return [self.popAnimatedTransitioningClass new];
232 | }
233 |
234 | return nil;
235 | }
236 |
237 | - (id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id)animationController
238 | {
239 | SWNavigationController *navController = (SWNavigationController *)navigationController;
240 | if (navController.interactivePushGestureRecognizer.state == UIGestureRecognizerStateBegan) {
241 | navController.percentDrivenInteractiveTransition = [UIPercentDrivenInteractiveTransition new];
242 | navController.percentDrivenInteractiveTransition.completionCurve = UIViewAnimationCurveEaseOut;
243 | } else {
244 | navController.percentDrivenInteractiveTransition = nil;
245 | }
246 |
247 | return navController.percentDrivenInteractiveTransition;
248 | }
249 |
250 | - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
251 | {
252 | if (self.pushedViewController != viewController) {
253 | self.pushedViewController = nil;
254 | self.pushCompletion = nil;
255 | }
256 | }
257 |
258 | - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
259 | {
260 | if (self.pushCompletion && self.pushedViewController == viewController) {
261 | self.pushCompletion();
262 | }
263 |
264 | self.pushCompletion = nil;
265 | self.pushedViewController = nil;
266 | }
267 |
268 | /*
269 | #pragma mark - Navigation
270 |
271 | // In a storyboard-based application, you will often want to do a little preparation before navigation
272 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
273 | // Get the new view controller using [segue destinationViewController].
274 | // Pass the selected object to the new view controller.
275 | }
276 | */
277 |
278 | @end
279 |
--------------------------------------------------------------------------------
/SWNavigationController/PodFiles/SWPushAnimatedTransitioning.h:
--------------------------------------------------------------------------------
1 | //
2 | // SWPushAnimatedTransitioning.h
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface SWPushAnimatedTransitioning : NSObject
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/SWNavigationController/PodFiles/SWPushAnimatedTransitioning.m:
--------------------------------------------------------------------------------
1 | //
2 | // SWPushAnimatedTransitioning.m
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | // Based off of 'SlideAnimatedTransitioning' https://github.com/visnup/swipe-left/blob/master/SwipeLeft/SlideAnimatedTransitioning.m
8 | //
9 |
10 | #import "SWPushAnimatedTransitioning.h"
11 |
12 | #define kSWToLayerShadowRadius 5
13 | #define kSWToLayerShadowOpacity 0.5
14 | #define kSWFromLayerShadowOpacity 0.1
15 | #define kSWPushTransitionDuration 0.2
16 |
17 | @implementation SWPushAnimatedTransitioning
18 |
19 | - (void)animateTransition:(id)transitionContext
20 | {
21 | UIView *containerView = [transitionContext containerView];
22 | UIView *toView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;
23 | UIView *fromView = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view;
24 |
25 | CGFloat containerViewWidth = containerView.frame.size.width;
26 | UIView *snapshotToView = [toView snapshotViewAfterScreenUpdates:YES];
27 |
28 | [containerView addSubview:snapshotToView];
29 |
30 | // Calculate new frame for fromView
31 | CGRect fromViewFinalFrame = fromView.frame;
32 | fromViewFinalFrame.origin.x = -containerViewWidth/3.f;
33 |
34 | // Calculate the new frame for toView snapshot
35 | CGRect toViewFinalFrame = toView.frame;
36 | toViewFinalFrame.origin.x = containerViewWidth;
37 |
38 | // Use a shadow path to make rendering during the interactive transition better
39 | snapshotToView.frame = toViewFinalFrame;
40 | snapshotToView.layer.shadowRadius = kSWToLayerShadowRadius;
41 | snapshotToView.layer.shadowOpacity = kSWFromLayerShadowOpacity;
42 | CGRect shadowFrame = snapshotToView.layer.bounds;
43 | CGPathRef shadowPath = [UIBezierPath bezierPathWithRect:shadowFrame].CGPath;
44 | snapshotToView.layer.shadowPath = shadowPath;
45 |
46 | // Tries to mimic the shadow animation on the default pop animation
47 | CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
48 | anim.fromValue = [NSNumber numberWithFloat:kSWFromLayerShadowOpacity];
49 | anim.toValue = [NSNumber numberWithFloat:kSWToLayerShadowOpacity];
50 | anim.duration = [self transitionDuration:transitionContext];
51 | [snapshotToView.layer addAnimation:anim forKey:@"shadowOpacity"];
52 | snapshotToView.layer.shadowOpacity = kSWToLayerShadowOpacity;
53 |
54 | [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
55 | // Move views to final frames
56 | snapshotToView.frame = fromView.frame;
57 | fromView.frame = fromViewFinalFrame;
58 | } completion:^(BOOL finished) {
59 | snapshotToView.layer.shadowOpacity = 0;
60 |
61 | [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
62 |
63 | // If transition was not cancelled, actually add the toView to our view hierarchy
64 | if (![transitionContext transitionWasCancelled]) {
65 | [containerView addSubview:toView];
66 | [snapshotToView removeFromSuperview];
67 | }
68 | }];
69 | }
70 |
71 | - (NSTimeInterval)transitionDuration:(id)transitionContext
72 | {
73 | return kSWPushTransitionDuration;
74 | }
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/SWNavigationController/TableViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TableViewController : UITableViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/SWNavigationController/TableViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import "TableViewController.h"
10 |
11 | @interface TableViewController ()
12 |
13 | @end
14 |
15 | @implementation TableViewController
16 |
17 | - (void)viewDidLoad {
18 | [super viewDidLoad];
19 | // Do any additional setup after loading the view, typically from a nib.
20 | }
21 |
22 | - (void)didReceiveMemoryWarning {
23 | [super didReceiveMemoryWarning];
24 | // Dispose of any resources that can be recreated.
25 | }
26 |
27 | #pragma mark - UITableViewDataSource
28 |
29 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
30 | {
31 | return 100;
32 | }
33 |
34 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
35 | {
36 | static NSString *CellIdentifier = @"CellIdentifier";
37 |
38 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
39 |
40 | cell.textLabel.text = [NSString stringWithFormat:@"Cell %ld", indexPath.row];
41 |
42 | return cell;
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/SWNavigationController/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // SWNavigationController
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/SWNavigationControllerTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.CEWendel.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SWNavigationControllerTests/SWNavigationControllerTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // SWNavigationControllerTests.m
3 | // SWNavigationControllerTests
4 | //
5 | // Created by Christopher Wendel on 12/31/14.
6 | // Copyright (c) 2014 Christopher Wendel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface SWNavigationControllerTests : XCTestCase
13 |
14 | @end
15 |
16 | @implementation SWNavigationControllerTests
17 |
18 | - (void)setUp {
19 | [super setUp];
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 |
23 | - (void)tearDown {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | [super tearDown];
26 | }
27 |
28 | - (void)testExample {
29 | // This is an example of a functional test case.
30 | XCTAssert(YES, @"Pass");
31 | }
32 |
33 | - (void)testPerformanceExample {
34 | // This is an example of a performance test case.
35 | [self measureBlock:^{
36 | // Put the code you want to measure the time of here.
37 | }];
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------