Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adaptive show detail segue transformed to modal instead of push on iPhone when master view controller is a UITabBarController

Tags:

ios

iphone

In XCode 6, if you create a new project based on the Master-Detail Application template, you get a universal storyboard that is supposed to be good for all devices.

When selecting a cell in the master view, the detail view is updated via an adaptive "show detail" segue. On an iPhone 4, 5, 6 or 6+ in portrait, this segue will take the form of a push as expected. On an iPad or an iPhone 6+ in landscape, it will cause the detail view to be updated as expected.

Now, if you insert a UITabBarController as the master view controller which has a tab to the original master view controller, the adaptive segue that occurs when selecting a cell in the master view does not behave as expected on iPhones. Instead of getting a push transition, you now get a modal transition. How can I fix that? Seems odd that this is not supported by default.

I found the following post useful: iOS8 TabbarController inside a UISplitviewController Master But when using the suggested method, I don't get the right behaviour on an iPhone 6 Plus when I rotate to landscape after a push in portrait. The content of the detail view appears in the master view which is not surprising since that's what the suggested solution does.

Thanks!

like image 255
phamel Avatar asked Oct 05 '14 18:10

phamel


2 Answers

Re-watching videos from WWDC14 I think I've found a better answer.

  1. Use a custom UISplitViewController (subclass)
  2. Override the showDetailViewController operation
  3. Use the traitCollection to determine the class of the UISplitViewController
  4. If the horizontal class is Compact, get the navigationController to call showViewController

Here is the the code of the custom UISplitViewController :

import UIKit

class CustomSplitViewController: UISplitViewController {

    override func showDetailViewController(vc: UIViewController!, sender: AnyObject!) {

        if (self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact) {
            if let tabBarController = self.viewControllers[0] as? UITabBarController {
                if let navigationController = tabBarController.selectedViewController as? UINavigationController {
                    navigationController.showViewController(vc, sender: sender)
                    return
                }
            }
        }

        super.showDetailViewController(vc, sender: sender)
    }
}

Do not forget to the set the custom class in the storyboard.

Tested in the simulator of iPhone 6, iPhone 6+ and iPad Air and worked as expected.

like image 84
Darth Philou Avatar answered Nov 18 '22 08:11

Darth Philou


Unfortunately, the selected answer didn't work for me. However, I did eventually manage to solve the problem:

  1. Subclass UISplitViewController and set the new class in Interface Builder.
  2. Make the new class conform to UISplitViewControllerDelegate:

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.delegate = self
    }
    
  3. Implement these two methods:

    func splitViewController(_ splitViewController: UISplitViewController,
                             collapseSecondary secondaryViewController:UIViewController,
                             onto primaryViewController:UIViewController) -> Bool {
        return true
    }
    
    func splitViewController(_ splitViewController: UISplitViewController,
                             showDetail vc: UIViewController,
                             sender: Any?) -> Bool {
    
        if splitViewController.isCollapsed {
            guard let tabBarController = splitViewController.viewControllers.first as? UITabBarController else { return false }
            guard let selectedNavigationViewController = tabBarController.selectedViewController as? UINavigationController else { return false }
    
            // Push view controller
            var detailViewController = vc
            if let navController = vc as? UINavigationController, let topViewController = navController.topViewController {
                detailViewController = topViewController
            }
            selectedNavigationViewController.pushViewController(detailViewController, animated: true)
            return true
        }
        return false
    }
    
like image 3
nikolovski Avatar answered Nov 18 '22 08:11

nikolovski