I am transitioning my app to iOS 13, and the UISplitViewController collapses onto the detail view, rather than the master at launch — only on iPad. Also, the back button is not shown - as if it is the root view controller.
My app consists of a UISplitViewController
which has been subclassed, conforming to UISplitViewControllerDelegate
. The split view contains two children — both UINavigationControllers
, and is embedded in a UITabBarController
(subclassed TabViewController
)
In the split view viewDidLoad
, the delegate is set to self
and preferredDisplayMode
is set to .allVisible
.
For some reason, the method splitViewController(_:collapseSecondary:onto:)
not being called.
In iOS 12 on iPhone and iPad, the method splitViewController(_:collapseSecondary:onto:)
is correctly called at launch, in between application(didFinishLaunchingWithOptions)
and applicationDidBecomeActive
.
In iOS 13 on iPhone, the method splitViewController(_:collapseSecondary:onto:)
is correctly called at launch, in between scene(willConnectTo session:)
and sceneWillEnterForeground
.
In iOS 13 on iPad, however, if the window has compact width at launch e.g. new scene created as a split view, the splitViewController(_:collapseSecondary:onto:)
method is not called at all. Only when expanding the window to regular width, and then shrinking is the method called.
class SplitViewController: UISplitViewController, UISplitViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
preferredDisplayMode = .allVisible
}
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
print("Split view controller function")
guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
if topAsDetailController.passedEntry == nil {
return true
}
return false
}
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Setup split controller
let tabViewController = self.window!.rootViewController as! TabViewController
let splitViewController = tabViewController.viewControllers![0] as! SplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
navigationController.topViewController!.navigationItem.leftBarButtonItem?.tintColor = UIColor(named: "Theme Colour")
splitViewController.preferredDisplayMode = .allVisible
}
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13.0, *) {
} else {
let tabViewController = self.window!.rootViewController as! TabViewController
let splitViewController = tabViewController.viewControllers![0] as! SplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
navigationController.topViewController!.navigationItem.leftBarButtonItem?.tintColor = UIColor(named: "Theme Colour")
splitViewController.preferredDisplayMode = .allVisible
}
return true
}
It stumps me why the method is being called in iPhone, but not in iPad! I am a new developer and this is my first post, so apologies if my code doesn't give enough detail or is not correctly formatted!