├── 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 | 20 | 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 | 124 | 129 | 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 | --------------------------------------------------------------------------------