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

2

10

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)
    }
}
Toffee answered 20/9, 2020 at 18:43 Comment(4)
Hey, did you manage to fix it somehow? I have the same issue :/.Containerize
no solution yet... @ContainerizeToffee
Any solution yet?Taro
I am currently experiencing the same issue. I need to implement something like you mentioned @benck. There are some view that need the 3 column layout whereas others only need the 2 column layoutAngelicaangelico
S
1

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

Satiable answered 5/4, 2021 at 15:45 Comment(4)
It works, thank you! But i'm not sure what's the "cost" to create a new SplitViewController every time when the column style is changed? Does the old SplitViewController get cleaned up in the memory?Toffee
Hey @benck, glad it worked. :) The deinit should be called. If you want to make sure, add a custom deinit that prints something.Satiable
The link to the sample project doesn't work. I've been searching for a complete 3 column sample project and this sounds like it would fit the bill. Please update the link if possible.Onus
Hey Lewis. Unfortunately I no longer have this project. But all you have to do is add the class above to your project, add a splitviewcontroller to your storyboard, and then change its class to "AdaptableSplitViewController". Let me know if you need further help.Satiable
S
0

Late in the discussion but... I've encountered a similar behavior.

Just before setting you secondary, set it to nil. Strange, I know, but it fixed it for me. Like this:

splitViewController?.setViewController(nil        , for: .secondary)
splitViewController?.setViewController(dashboardVC, for: .secondary)
Satiable answered 3/4, 2021 at 23:29 Comment(2)
I tried but it does not work. The "back button" is still there instead of the toggle button. Could you provide a working example project code if possible? Thanks.Toffee
My case was different from yours, sorry. Now, I've reproduced your case: I'll try and find a solution when I have a minute.Satiable

© 2022 - 2024 — McMap. All rights reserved.