I have several view controllers embedded in a UINavigationController (some modal, some pushed) and am navigating through them using swipe gestures as such:
// Gesture recognizers
UISwipeGestureRecognizer *downGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(dismissButton)];
downGesture.direction = UISwipeGestureRecognizerDirectionDown;
[downGesture setCancelsTouchesInView:NO];
[self.view addGestureRecognizer:downGesture];
This works fine, however I want the user to be able to physically drag the modally presented view controller, for example, down and off the screen instead of just a flick and an animation doing the rest, or dragging right across the screen and snapping to the previous view instead of tapping the back button.
I've tried implementing this using a pan gesture on the view but of course the previous view controller isn't visible behind it, which it needs to be. How is this effect achieved properly? With view controller containment? If so how would that work when pushing a few view controllers on to the stack? An example of the type of navigation I'm talking about can be found in the LetterPress app.
thanks.
Yes, custom container view is the way to go (iOS 5 and greater). You basically write your own custom container, using the built-in
childViewControllers
property to keep track of all of the child view controllers. You may want your own property, saycurrentChildIndex
, to keep track of which child controller you're currently on:Your parent controller should probably have some push and pop methods for non-swipe related navigation, such as:
Personally, I have a protocol defined for these two methods, and make sure that my parent controller is configured to conform to that protocol:
Then, when a child wants to push a new child on, it can do it like so:
When a child wants to pop itself off, it can do it like so:
And then the parent view controller has a pan gesture set up, to handle the user panning from one child to another:
It looks like you're doing up and down panning, rather than the left-right panning that I used above, but hopefully you get the basic idea.
By the way, in iOS 6, the user interface you're asking about (the sliding between views using gestures), could probably be done more efficiently using a built-in container controller,
UIPageViewController
. Just use a transition style ofUIPageViewControllerTransitionStyleScroll
and a navigation orientation ofUIPageViewControllerNavigationOrientationHorizontal
. Unfortunately, iOS 5 only allows page curl transitions, and Apple only introduced the scrolling transitions that you want in iOS 6, but if that's all you need,UIPageViewController
gets the job done even more efficiently than what I've laid out above (you don't have to do any custom container calls, no writing of gesture recognizers, etc).For example, you can drag a "page view controller" onto your storyboard, create a
UIPageViewController
subclass and then inviewDidLoad
, you need to configure the first page:Then you need to define the following
UIPageViewControllerDataSource
methods:Your implementation will vary (at the very least different class names and different storyboard identifiers; I'm also letting the page view controller instantiate the next page's controller when the user asks for it and because I'm not retaining any strong reference to them, the'll be released when I'm done transitioning to the other page ... you could alternatively just instantiate both at startup and then these
before
andafter
routines would obviously not instantiate, but rather look them up in an array), but hopefully you get the idea.But the key issue is that I don't have any gesture code, no custom container view controller code, etc. Much simpler.