6

I am building an application that is for iOS6+. The application will have a main View Controller at one point in the application.

I would like this main view controller to handle a swipe left and swipe right on screen to then show another view controller.

Is there an easy way to accomplish this in core iOS6+, or should I look for another library etc.

I already use a slide in menu style else where in the application. I also understand and can find a million alternatives to these.

What I am looking for is to have one View Controller (which acts in the 'middle'). Then when they swipe left/right another view controller is shown. They can then swipe back the opposite direction or click a back button to return to the main controller.

EDIT-
Specifically I am looking for the functionality to do the following: Pre-load the controller that will slide in. When the swipe occurs (is happening)... the controller to drag/slide in with the touch. The same drag/swipe to occur either way the controller is swiped (lefT/right).

EDIT 2 -
I am looking for the functionality of dragging the view controller in with the finger. Dependant on which way the drag is occurring, it would be pulling the same view controller in.

I.e the layout would be: [VC for Drag] [Main controller] [VC for Drag].
If the user swipes from left to right, or right to left the other controller is dragged over the top and they can return to the Main controller using the opposite entry swipe.

StuartM
  • 6,743
  • 18
  • 84
  • 160

5 Answers5

6

My favorite side-drawer controller: https://github.com/mutualmobile/MMDrawerController

MMDrawerController is highly configurable and does all the things you mention:

  • support for left and right controllers
  • preloads side controllers
  • "dragging" open with a gesture

If you're using a storyboard you can use this extension to have storyboard support: https://github.com/TomSwift/MMDrawerController-Storyboard

EDIT:

Another option might be to use a UIPageViewController with a transition style of UIPageViewControllerTransitionStyleScroll: https://developer.apple.com/library/ios/documentation/uikit/reference/UIPageViewControllerClassReferenceClassRef/UIPageViewControllerClassReference.html

This will have the behavior of "pulling" in the side view controllers vs. "uncovering" them.

EDIT 2: example per request

The only real complicating requirement you have is that the same view controller is used for both left and right. That means we have to track where the view controller is being presented so that we can correctly manage our data source. Without this requirement we could just back our data source with an array and derive next/prev from that.

First, the storyboard. The storyboard has three view controllers: 1) the UIPageViewController, which I've subclassed as TSPageViewController. Don't forget to set the page controller transition-style property to 'scroll'. 2) the "center" view controller, and 3) the "side" view controller. For center and side I've set the storyboard ID of each to "center" and "side", respectively. For this sample, both the center and side controllers are plain vanilla UIViewControllers, and I've set their view backgroundColor's to tell them apart.

enter image description here

Second, the page view controller:

.h

@interface TSPageViewController : UIPageViewController
@end

.m

@interface TSPageViewController () <UIPageViewControllerDataSource>
@end

@implementation TSPageViewController
{
    UIViewController* _side;

    UIViewController* _center;
}

- (void) viewDidLoad
{
    [super viewDidLoad];

    self.dataSource = self;

    _side = [self.storyboard instantiateViewControllerWithIdentifier: @"side"];
    _center = [self.storyboard instantiateViewControllerWithIdentifier: @"center"];

    [self setViewControllers: @[_center]
                   direction: UIPageViewControllerNavigationDirectionForward
                    animated: NO
                  completion: nil];
}

- (UIViewController*) pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    if ( viewController == _center )
    {
        _side.title = @"right";
        return _side;
    }

    if ( viewController == _side && [_side.title isEqualToString: @"left"] )
    {
        return _center;
    }

    return nil;
}

- (UIViewController*) pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    if ( viewController == _center )
    {
        _side.title = @"left";
        return _side;
    }

    if ( viewController == _side && [_side.title isEqualToString: @"right"] )
    {
        return _center;
    }

    return nil;
}

@end

Again, the only thing here that's special is the tracking of the whether the side controller is currently "left" or "right". This implementation has an issue in that it relies on the behavior of the UIPageViewController to not "page ahead" and cache view controllers (if it did, our logic would get confused). So you might want to consider a more robust mechanism to track which side the view controller is currently on. For that you'd likely have to introduce your own swipe gesture recognizer and use the data from that to drive tracking left/right.

If you can toss your requirement of using the same view controller for left and right, then you can have separate left/right/center controllers, stored in an array, and return next/prev controllers based on what you see in the array. Much easier and more robust!

TomSwift
  • 39,369
  • 12
  • 121
  • 149
  • I think this is a little overkill for the task in hand. I have looked through a pile of slide in view controllers and actually use one for a menu system in the app. I am simply looking for the ability to load a main controller, then have a second controller 'underneath' as such, that is shown when the user drags/slides to left or right. Then when they slide back the opposite way, the main controller is shown again. Is this not something I can simply implement as opposed to the different routes that each of these slide in menu styled gits offer. – StuartM Oct 21 '13 at 22:43
  • It doesn't seem like overkill to me. What you're describing is what these controllers are designed for. You could roll your own, but why? – TomSwift Oct 21 '13 at 22:47
  • Tom I would like to implement this approach but I am having difficulties working how how to implement this, do you have any other information. I am using storyboards and would have two controllers setup. One for the main and the other controller that can be pulled in from either side. I am not sure how to set this up, as the Apple example uses a DataSource etc when this is a simple UIViewController approach. Any other information or useful links on setting this up like this. – StuartM Oct 25 '13 at 16:22
  • @StuartM - I've added a sample for you. Good luck! – TomSwift Oct 25 '13 at 18:56
  • Great, thanks Tom this works exactly how I am wanting it to. That is great and I do not need to implement any third party to complete this - even better! – StuartM Oct 28 '13 at 15:15
1

for iOS6+:

I'd be inclined to use https://github.com/pkluz/PKRevealController and just set both left and right viewControllers to be pointers to a single viewController.


for iOS7+:

I think you should be looking into custom UIViewController transitions.

https://github.com/ColinEberhardt/VCTransitionsLibrary

http://www.teehanlax.com/blog/custom-uiviewcontroller-transitions/

There is a good WWDC 2013 video on this subject entitled "Custom transitions using view controllers", it's session 218.

ader
  • 5,403
  • 1
  • 21
  • 26
0

You can accomplish that with some of the cocoa controls such as:

https://github.com/Inferis/ViewDeck

https://github.com/gotosleep/JASidePanels

EDIT: Proposal number two, use a controller with a scrollView:

// Allocate all the controlelrs you need
MyFirstViewController *first = [[MyFirstViewController alloc] init];
MySecondViewController *second= [[MySecondViewController alloc] init];
MyThirdViewController *third = [[MyThirdViewController alloc] init];

// Adjust the frames of the controllers
first.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
second.view.frame = CGRectMake(0, self.view.frame.size.width, self.view.frame.size.width, self.view.frame.size.height);
third .view.frame = CGRectMake(0, 2 * self.view.frame.size.width, self.view.frame.size.width, self.view.frame.size.height);

// Add controllers as subViews to the scrollView
[self.scrollView addSubview:self.first.view];
[self.scrollView addSubview:self.second.view];
[self.scrollView addSubview:self.third.view];

// Set the scrollView contentSize and paging
self.scrollView.contentSize = CGRectMake(self.view.frame.size.width * 3, self.view.frame.size.height);
self.scrollView.pagingEnabled = YES;

// Scroll to the middle view initally
[self.scrollView scrollRectToVisible:CGRectMake(0, self.view.frame.size.width, self.view.frame.size.width, self.view.frame.size.height) animated:NO];

The code above is written by heart, I have probably named a few things wrongly, and there is more than one way to handle the subviews. The mentioned pageViewController in the comments will work as well.

Adis
  • 4,512
  • 2
  • 33
  • 40
  • These are just slide in menu controllers. In fact I already use one in my app. I am specifically looking for the ability to have a drag/slide in effect from both sides, so thoughts on creating this manually or a specific example that does this functionality. I have been through about 50 of these already. – StuartM Oct 21 '13 at 22:34
  • Why not just use a simple scrollView with paging then? – Adis Oct 21 '13 at 22:36
  • I do not see why that would not work? Do you have any examples on how to set these up, am I able to set up the paging but without icons to indicate this. I simply need a main controller loaded in the middle and the ability to drag the 'under' VC in from left or right, and return to the middle from the opposite direction they initially dragged from – StuartM Oct 21 '13 at 22:42
  • Simply make a viewController with a scrollView (it can even be your center controller or any other), add the other controllers' views with adjusted frames as subviews and scroll to the middle one programmatically. I'll update the answer with some example code. – Adis Oct 21 '13 at 22:46
  • Rather than rolling your own UIScrollView solution, you could just use a UIPageViewController. https://developer.apple.com/library/ios/documentation/uikit/reference/UIPageViewControllerClassReferenceClassRef/UIPageViewControllerClassReference.html – TomSwift Oct 21 '13 at 22:55
  • @TomSwift - Are you able to provide an example of how to implement this with Page Controller. The approach that Apple provides is not easy to understand or translate into this approach. – StuartM Oct 25 '13 at 16:20
0

What you are looking to do sounds a lot like the iPhone version of Apple's UIPageControl Demo;

Holly
  • 5,270
  • 1
  • 24
  • 27
-1

Sounds like a job for UISwipeGestureRecognizer.

Edward
  • 319
  • 4
  • 13
  • I guess the question is on the layout of the View Controllers in the storyboard. How would they be layed out. IF I want a swipe to load the same controller. Ideally I am looking for a way to swipe and 'drag' the new VC in with the finger still down, so it drags in. Same both was for swipe. – StuartM Oct 12 '13 at 13:58