Swift UISplitViewController 如何从三列变为双列

lac*_*her 4 uikit ipad uisplitviewcontroller swift

我在弄清楚如何构造 UISplitViewController 时遇到了很多麻烦。

我想:

  • 主视图中的侧边栏(始终)
  • 我希望第一个侧边栏导航项(动物)显示三重(侧边栏、动物列表、动物详细信息)
  • 我希望第二个侧边栏导航项(个人资料)显示双(侧边栏,个人资料视图)

我看到其他应用程序这样做(例如 GitHub),但我真的不知道他们是如何管理它的。资源很难找到,而且我见过的大多数教程都只显示一种或另一种列样式。

我主要是在寻找有关如何构建如此良好的架构的答案,但任何代码也将受到极大的赞赏!

场景委托

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    window = UIWindow(frame: windowScene.coordinateSpace.bounds)
    window?.windowScene = windowScene
    window?.rootViewController = ViewController(style: .tripleColumn)
    window?.makeKeyAndVisible()
}
Run Code Online (Sandbox Code Playgroud)

根视图控制器

class ViewController: UISplitViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        viewControllers = [
            SidebarViewController(),
            AnimalsViewController(),
            AnimalDetailViewController()
        ]
        
        // Example attempt at removing the secondary view
        setViewController(ProfileViewController(), for: .supplementary)
        setViewController(nil, for: .secondary)
        hide(.secondary)
    }
}
Run Code Online (Sandbox Code Playgroud)

期望的行为

动物 三重示例

轮廓 双倍的

干杯!

Wit*_*ski 6

没有“官方”方法可以做到这一点,但这是可能的。据我所知,解决这个问题的最好方法之一是UISplitViewController在根视图控制器中有两个实例,并在需要时在它们之间进行切换。这是我的方法(大约):

免责声明:此代码是在 UIKit 实验室上届 WWDC22 期间向 Apple 工程师咨询的。他们已经确认,非常不幸的是,他们目前没有提供一种方便的方法来做到这一点,并且这种方法可能是最好的方法。反馈已提交,其 ID 已传递给工程师,希望我们能在 iOS 17 中获得官方 API :D

雷达://FB10140263

步骤 1. 初始化UISplitViewControllers

private lazy var doubleColumnSVC: UISplitViewController = {
    $0.primaryBackgroundStyle = .sidebar
    // setup your SVC here
    $0.setViewController(doubleColumnPrimaryNC, for: .primary)
    return $0
}(UISplitViewController(style: .doubleColumn))

private lazy var tripleColumnSVC: UISplitViewController = {
    $0.primaryBackgroundStyle = .sidebar
    // setup your SVC here
    $0.setViewController(tripleColumnPrimaryNC, for: .primary)
    return $0
}(UISplitViewController(style: .tripleColumn))
Run Code Online (Sandbox Code Playgroud)

步骤 2. 初始化你的侧边栏 VC 和两个单独的UINavigationControllers

我发现它是交换侧边栏 VC 的最可靠的解决方案。在单个UINavigationController实例中存在侧边栏随机不出现的错误。两个实例解决了这个问题,同时仍然保持单个 SidebarVC 具有适当的焦点状态和已经布局的内容。

// Sidebar is shared and swapped between two split views
private lazy var sideBar = YourSideBarViewController()
private lazy var doubleColumnPrimaryNC = UINavigationController(
    rootViewController: UIViewController()
)
private lazy var tripleColumnPrimaryNC = UINavigationController(
    rootViewController: UIViewController()
)
Run Code Online (Sandbox Code Playgroud)

步骤3.创建一个属性来存储当前显示的SVC

当下一步在两个实例之间切换时,它将派上用场。

private var current: UISplitViewController?
Run Code Online (Sandbox Code Playgroud)

步骤 4. 需要时实现两种样式之间的切换

每次您想要从侧边栏导航到不同的屏幕时都应该调用此函数。

private func toggleStyleIfNeeded(_ style: UISplitViewController.Style) {
    switch style {
    case .doubleColumn:
        // skip if the desired column style is already set up
        if current === doubleColumnSVC { return }
        // reassign current
        current = doubleColumnSVC

        // here add doubleColumnSVC as child view controller
        // here add doubleColumnSVC.view as subview

        // swap the sidebar
        doubleColumnPrimaryNC.setViewControllers([sideBar], animated: false)

        // here remove tripleColumnSVC from parent
        // here remove tripleColumnSVC.view from superview

    case .tripleColumn:
        // skip if the desired column style is already set up
        if current === tripleColumnSVC { return }
        // reassign current
        current = tripleColumnSVC

        // here add tripleColumnSVC as child view controller
        // here add tripleColumnSVC.view as subview

        // swap the sidebar
        tripleColumnPrimaryNC.setViewControllers([sideBar], animated: false)

        // here remove doubleColumnSVC from parent
        // here remove doubleColumnSVC.view from superview

    default:
        return
    }
    // If you are using UITabBarController for your compact style, assign it here
    current?.setViewController(tabBar, for: .compact)
}
Run Code Online (Sandbox Code Playgroud)

在以“here add”开头的行中,您需要编写自己的代码。我简化了代码示例以使其更短。

步骤 5. 享受带有动态列的 SVC!

现在你基本上已经准备好了!通过根 VC(或处理导航和管理 SVC 的任何一个)上的这个简单的辅助方法,您将拥有实现您想要的目标所需的所有功能,即具有UISplitViewController动态列数!

func setViewController(
    _ viewController: UIViewController,
    for column: UISplitViewController.Column,
    style: UISplitViewController.Style
) {
    toggleStyleIfNeeded(style)
    current?.setViewController(viewController, for: column)
}
Run Code Online (Sandbox Code Playgroud)

我们已经在生产中使用这种方法几个月了,效果很好。该应用程序支持 iOS、iPadOS 和 Mac Catalyst。有一些事情,比如自定义状态栏样式和获得一致的侧边栏按钮体验,要完美地工作有点棘手,但通过一些调整和帮助一切都是UISplitViewControllerDelegate可能的。

祝你好运!

PS 如果有人以前走过这条路并且能够分享建议,请这样做!我很想了解更多有关如何为用户和开发人员改善这种动态分割视图体验的信息。