How can I create a segue that will use a popover on iPad, and push onto the navigation stack on the iPhone?

In my app, there are certain view controllers where on iPad (or to be more specific, a regular horizontal size class) it makes sense to present them as popovers, but on iPhone (or a compact horizontal size class) it makes sense to push them onto the navigation stack. Is there an elegant way to support this? By default, if I use a "Present as Popover" segue, it will display modally on iPhone, which isn't what I want.

I've found a way to get the behavior I want, but it's ugly and seems error-prone. I choose between two different segues based on what size class I'm currently in. In order to support iOS 9 multitasking, I implement [UIViewController willTransitionToTraitCollection:withTransitionCoordinator] and manually move the view controller between a popover and the navigation controller (this part seems particularly error-prone).

It seems like there should be some simple way to implement either a custom segue to handle this, or some sort of custom adaptive presentation controller, but I haven't been able to wrap my head around it. Has anyone had success doing this?

Answers


According to me this is simplest way,

Step 1: Create two segues from your one controller to another. Step 2: Set one segue's segue property to push and popover of another Step 3: Now call perform segue according to your requirements, i.e.iPad or iPhone

Here is a sample code

Sample code Note : Change bool condition to false to check another condition in didSelectRowAtIndexPath.


Here's what I ended up building. I'm not super happy with it, which is why I haven't posted it until now. It won't support two segues going to view controllers with the same class, and it requires you to keep track of the source rect and source view for the popover yourself. But maybe it will be a good starting point for someone else.

PushPopoverSegue.swift

import UIKit

class PushPopoverSegue: UIStoryboardSegue {

    var sourceBarButtonItem: UIBarButtonItem!
    var permittedArrowDirections: UIPopoverArrowDirection = .Any

    override func perform() {
        assert( self.sourceViewController.navigationController != nil )
        assert( self.sourceBarButtonItem != nil )

        if self.sourceViewController.traitCollection.horizontalSizeClass == .Compact {
            self.sourceViewController.navigationController!.pushViewController(self.destinationViewController, animated: true)
        }
        else {
            let navigationController = UINavigationController(rootViewController: self.destinationViewController)
            let popover = UIPopoverController(contentViewController: navigationController)
            popover.presentPopoverFromBarButtonItem(self.sourceBarButtonItem, permittedArrowDirections: self.permittedArrowDirections, animated: true)
        }
    }

}

UIViewController+PushPopoverTransition.h

#import <UIKit/UIKit.h>

@interface UIViewController (PushPopoverTransition)

- (void) transitionPushPopoversToHorizontalSizeClass: (UIUserInterfaceSizeClass) sizeClass withMapping: (NSDictionary*) mapping;

@end

UIViewController+PushPopoverTransition.m

#import "UIViewController+PushPopoverTransition.h"

@implementation UIViewController (PushPopoverTransition)

- (void) transitionPushPopoversToHorizontalSizeClass: (UIUserInterfaceSizeClass) sizeClass withMapping: (NSDictionary*) mapping
{
    if ( sizeClass == UIUserInterfaceSizeClassCompact )
    {
        if ( self.presentedViewController == nil )
            return;

        NSParameterAssert( [self.presentedViewController isKindOfClass:[UINavigationController class]] );
        UINavigationController* navigationController = (UINavigationController*) self.presentedViewController;
        NSArray* viewControllers = navigationController.viewControllers;
        UIViewController* topOfStack = viewControllers[0];

        if ( [mapping.allKeys containsObject:NSStringFromClass( [topOfStack class] ) ] )
        {
            [self.presentedViewController dismissViewControllerAnimated:NO completion:^{
                for ( UIViewController* viewController in viewControllers )
                    [self.navigationController pushViewController:viewController animated:NO];
            }];
        }
    }
    else if ( sizeClass == UIUserInterfaceSizeClassRegular )
    {
        NSUInteger indexOfSelf = [self.navigationController.viewControllers indexOfObject:self];

        if ( indexOfSelf < self.navigationController.viewControllers.count  - 1 )
        {
            UIViewController* topOfStack = self.navigationController.viewControllers[indexOfSelf + 1];
            if ( [mapping.allKeys containsObject:NSStringFromClass( [topOfStack class] )] )
            {
                NSArray* poppedControllers = [self.navigationController popToViewController:self animated:NO];
                UINavigationController* navigationController = [[UINavigationController alloc] init];
                navigationController.modalPresentationStyle = UIModalPresentationPopover;
                navigationController.viewControllers = poppedControllers;

                id popoverSource = mapping[NSStringFromClass( [topOfStack class] )];
                if ( [popoverSource isKindOfClass:[UIBarButtonItem class]] )
                {
                    navigationController.popoverPresentationController.barButtonItem = popoverSource;
                }
                else if ( [popoverSource isKindOfClass:[NSArray class]] )
                {
                    NSArray* popoverSourceArray = (NSArray*) popoverSource;
                    NSParameterAssert(popoverSourceArray.count == 2);
                    UIView* sourceView = popoverSourceArray[0];
                    CGRect sourceRect = [(NSValue*) popoverSourceArray[1] CGRectValue];
                    navigationController.popoverPresentationController.sourceView = sourceView;
                    navigationController.popoverPresentationController.sourceRect = sourceRect;
                }

                [self presentViewController:navigationController animated:NO completion:nil];
            }
        }
    }
}

@end

Example Usage

Create a segue in interface builder, and set its "Kind" to Custom, and its "Class" to PushPopoverSegue.

ViewController.m

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    ((PushPopoverSegue*) segue).sourceView = /* source view */;
    ((PushPopoverSegue*) segue).sourceRect = /* source rect */;
}

-(void) willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    if ( newCollection.horizontalSizeClass == UIUserInterfaceSizeClassUnspecified )
        return;

    [self transitionPushPopoversToHorizontalSizeClass:newCollection.horizontalSizeClass withMapping:@{
        @"MyDestinationViewController": @[ /* source view */,
                                       [NSValue valueWithCGRect:/* source rect*/] ]
    }];
}

Need Your Help

java, getting user input through scanner and system.in

java java.util.scanner system.in

how can I give a condition on an input from "system.in" that will halt the program until the right value is inserted?

Displaying google images on my website?

php arrays image search web

so basically I'm trying to display images on my website from google images based on a word stored in an array. Right now, I have this code