Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 14 UISplitViewController (sidebar) with triple column sidebar toggle icon behavior

I'm implementing the iOS 14 (iPadOS 14) sidebar (UISplitViewController with TripleColumn) and having strange "sidebar toggle icon" behavior. In iOS 13 I'm using the tab bar with some split views and table views so I need the Triple Column instead of the Double Column to work.

For example, using the sidebar in "flights" tab needs three columns: iOS 14 sidebar example with Triple Column

And there are some tabs with only one column (in iOS 13, it was a table view instead of a split view). I set the supplementary view to nil, and hide the view by calling "hide" method implemented in iOS 14. (See below for code): iOS 14 sidebar example with triple column but hide the supplementary column

The "sidebar toggle icon" on the upper left is automatically displayed. After clicking the toggle icon, the sidebar hides correctly but an "back button" was created on my secondary view(a UITableViewController embedded in a UINavigationController): iOS 14 sidebar example after toggle icon clicked

Pressing (clicking) the back button has no response. User can still swipe from the left edge of the screen to make sidebar reappear but the "back button" is confusing. My expected behavior is, after the toggle icon selected in sidebar, display the "sidebar toggle icon" instead of the "back button" in the secondary view. And after pressing the "sidebar toggle icon" in secondary view, the sidebar reappears.

Like the Photos app in iOS 14 (iPadOS 14), the toggle button is shown instead of the back button. And clicking the toggle icon will make the sidebar shown again. (but it's a double column split view) iPadOS 14 Photos app with sidebar hidden

My code:

SceneDelegate.swift:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        if #available(iOS 14.0, *) {
            let main = UIStoryboard(name: "Main", bundle: nil)
            
            let splitViewController = UISplitViewController(style: .tripleColumn)

            splitViewController.preferredDisplayMode = .twoBesideSecondary
            splitViewController.preferredSplitBehavior = .tile
            splitViewController.setViewController(SideBarViewController(), for: .primary)

            // fall back for compact screen
            splitViewController.setViewController(main.instantiateInitialViewController(), for: .compact)
            window.rootViewController = splitViewController

            self.window = window
            window.makeKeyAndVisible()
        }
    }
}

SideBarViewController:

// if the first tab (dashboard) was selected
private func selectDashboardTab() {
    if #available(iOS 14.0, *) {
        
        let dashboardVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DashboardTab") as? UINavigationController

        splitViewController?.preferredPrimaryColumnWidth = 250.0
        splitViewController?.preferredDisplayMode = .twoBesideSecondary
        splitViewController?.preferredSplitBehavior = .tile

        splitViewController?.setViewController(dashboardVC, for: .secondary)
        splitViewController?.setViewController(nil, for: .supplementary)
        splitViewController?.hide(.supplementary)
    }
}
like image 695
benck Avatar asked Sep 20 '20 18:09

benck


1 Answers

I was able to reproduce the problem... but was unable to circumvent it using the available API. Apple stubbornly decided that the sidebar icon would always be in the supplementary controller when in a 3-column layout, it seems.

That being said, I've coded a hack to fix it. I managed to create a UISplitViewController subclass that "fakes" an editable style property, thus allowing us to set the number of columns (the hack just creates a brand new controller and makes it the new rootViewController).

The hack allows us to switch between 2-colulm and 3-column layout at will, and seems to solve the sidebar icon problem.

Link to the Xcode project below. But here's the gist of it:

class AdaptableSplitViewController: UISplitViewController {
    override var style: Style {
        get {
            super.style
        }
        set {
            guard newValue != style else { return }
        
            let primaryController       = viewController(for: .primary)
            let supplementaryController = viewController(for: .supplementary)
            let secondaryController     = viewController(for: .secondary)
            let newSplitController      = AdaptableSplitViewController(style: newValue)
                     
            newSplitController.setViewController(primaryController  , for: .primary)
            newSplitController.setViewController(secondaryController, for: .secondary)
            if newValue == .tripleColumn {
                newSplitController.setViewController(supplementaryController, for: .supplementary)
            }
                    
            UIApplication.shared.windows[0].rootViewController = newSplitController
       }
    }
}

Let me know if this helps.

Link to the zipped Xcode sample project

like image 63
Jean Le Moignan Avatar answered Nov 20 '22 11:11

Jean Le Moignan