iOS 14 UISplitViewController侧边栏,带有三栏侧边栏切换图标行为

问题描述

我正在实现iOS 14(iPadOS 14)侧边栏(带有TripleColumn的UISplitViewController),并且具有奇怪的“侧边栏切换图标”行为。 在iOS 13中,我使用带有一些拆分视图和表格视图的选项卡栏,因此我需要三列而不是双列来工作。

例如,使用“航班”标签中的侧栏需要三列:

iOS 14 sidebar example with Triple Column

有些选项卡只有一列(在iOS 13中,它是一个表视图而不是拆分视图)。我将补充视图设置为nil,并通过调用iOS 14中实现的“ hide”方法隐藏视图(请参见下面的代码):

iOS 14 sidebar example with triple column but hide the supplementary column

自动显示左上方的“侧栏切换图标”。单击切换图标后,边栏正确隐藏,但是在我的辅助视图(嵌入在UINavigationController中的UITableViewController)上创建了一个“后退按钮”:

iOS 14 sidebar example after toggle icon clicked

按下(单击)后退按钮没有响应。用户仍然可以从屏幕的左边缘滑动以重新显示边栏,但“后退按钮”令人困惑。我的预期行为是,在侧边栏中选择了切换图标之后,在辅助视图中显示了“侧边栏切换图标”而不是“后退按钮”。然后,在辅助视图中按下“侧栏切换图标”后,侧栏会重新出现。

类似于iOS 14(iPadOS 14)中的“照片”应用程序,显示了切换按钮,而不是后退按钮。然后单击切换图标将再次显示侧边栏。 (但这是一个双列拆分视图)

iPadOS 14 Photos app with sidebar hidden

我的代码

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)
    }
}

解决方法

讨论到后期,但...我遇到过类似的行为。

就在设置你的次要之前,将它设置为 nil。奇怪,我知道,但它为我修好了。像这样:

splitViewController?.setViewController(nil,for: .secondary)
splitViewController?.setViewController(dashboardVC,for: .secondary)
,

我能够重现该问题...但无法使用可用的 API 来规避它。 Apple 顽固地决定,在 3 列布局中,侧边栏图标将始终位于辅助控制器中。

话虽如此,我已经编写了一个 hack 来修复它。我设法创建了一个 UISplitViewController 子类,它“伪造”了一个可编辑的 style 属性,从而允许我们设置列数(hack 只是创建了一个全新的控制器并使其成为新的 rootViewController)。

hack 让我们可以随意切换 2 列和 3 列布局,似乎解决了侧边栏图标问题。

链接到下面的 Xcode 项目。但这是它的要点:

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
       }
    }
}

如果这有帮助,请告诉我。

Link to the zipped Xcode sample project