创建一个 SwiftUI 侧边栏

jls*_*ert 12 ios swiftui swiftui-navigationlink

我想使用 SwiftUI 构建一个非常简单的 iOS 14 侧边栏。该设置是相当简单,我有三个观点HomeViewLibraryViewSettingsView和枚举代表每个屏幕。

enum Screen: Hashable {
   case home, library, settings
}
Run Code Online (Sandbox Code Playgroud)

我的最终目标是根据大小类在选项卡视图和侧边栏之间自动切换,但有些事情并没有按预期工作。

全局状态由 拥有MainNavigationView,它也是 my 的根视图WindowGroup

struct MainNavigationView: View {
    @State var screen: Screen? = .home
   
    var body: some View {
        NavigationView {
            SidebarView(state: $screen)
        }
        .navigationViewStyle(DoubleColumnNavigationViewStyle())
    }
}
Run Code Online (Sandbox Code Playgroud)

SidebarView是一个简单的List包含三个NavigationLink,每个一个Screen

struct SidebarView: View {
    @Binding var state: Screen?
    var body: some View {
        List {
            NavigationLink(
                destination: HomeView(),
                tag: Screen.home,
                selection: $state,
                label: {
                    Label("Home", systemImage: "house" )
                })
            NavigationLink(
                destination: LibraryView(),
                tag: Screen.library,
                selection: $state,
                label: {
                    Label("Library", systemImage: "book")
                })
            NavigationLink(
                destination: SettingsView(),
                tag: Screen.settings,
                selection: $state,
                label: {
                    Label("Settings", systemImage: "gearshape")
                })
        }
        .listStyle(SidebarListStyle())
        .navigationTitle("Sidebar")
    
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用NavigationLink(destination:tag:selection:label)初始值设定项,MainNavigationView以便在我的中设置选定的屏幕,以便我可以在TabView以后重复使用它。

然而,很多事情并不像预期的那样奏效。

首先,在竖屏iPad(我用的是iPad Pro 11英寸模拟器)启动应用程序时,启动应用程序时没有选择屏幕。只有在我单击导航栏中的返回后,才会显示初始屏幕并显示我的主视图。

第一个错误:HomeView 仅在点击后退按钮后显示

第二个奇怪的事情是绑定似乎nil在侧边栏被隐藏时设置为。在横向模式下,视图按预期工作,但是当切换侧边栏隐藏然后再次显示时,选择会丢失。内容视图保持正确,但侧边栏选择丢失。

切换侧边栏会重置状态

这些只是SwiftUI错误还是有不同的方法来创建带有Binding?

mal*_*hal 7

您需要在 中包含一个默认的辅助视图NavigationView { },通常它是一个占位符,但您可以使用HomeScreen,例如

struct MainNavigationView: View {
    @State var screen: Screen? = .home
   
    var body: some View {
        NavigationView {
            SidebarView(state: $screen)
            HomeScreen()
        }
        .navigationViewStyle(DoubleColumnNavigationViewStyle())
    }
}
Run Code Online (Sandbox Code Playgroud)

关于未重新选择的单元格 - 从 iOS 14.2 开始,没有列表选择绑定(未处于编辑模式时),因此选择丢失。虽然 List API 有一个$selection参数,但目前仅在 macOS 上支持。您可以在标题中看到该信息:

/// On iOS and tvOS, you must explicitly put the list into edit mode for
/// the selection to apply.
Run Code Online (Sandbox Code Playgroud)

这有点令人费解,但这意味着我们需要的侧边栏选择绑定仅适用于 macOS,在 iOS 上仅适用于编辑模式下的多选(即复选标记)。原因可能是因为UITableView的选择是事件驱动的,也许无法转化为 SwiftUI 的状态驱动性质。如果您曾经尝试使用已推送到导航控制器上的视图进行状态恢复,并尝试在弹出时显示单元格取消突出显示动画,并且该表格视图未加载且单元格从未高亮显示,您将明白我的意思了吗。同步加载表格,绘制选定的单元格然后开始取消突出显示动画是一场噩梦。我希望 Apple 将在纯 SwiftUI 中重新实现 List、Sidebar 和 NavigationView 来克服这些问题,所以现在我们只需要忍受它。

修复此问题后,它将List(selection:$screen) { }像在 macOS 上的工作一样简单。作为 iOS 上的解决方法,您可以用自己的方式突出显示图标或文本,例如尝试使用粗体文本:

    NavigationLink(
        destination: HomeView(),
        tag: Screen.home,
        selection: $state,
        label: {
            Label("Home", systemImage: "house" )
        })
        .font(Font.headline.weight(state == Screen.home ? .bold : .regular))
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

它在紧凑时看起来不太好,因为在弹出主视图后,当行未突出显示时,粗体将被删除。在这种情况下,可能有一种方法可以禁用粗体。

您还应该注意其他 2 个错误:

  1. 在纵向中,侧边栏仅在第二次点击侧边栏导航按钮时显示。
  2. 在纵向中,如果您显示侧边栏并选择已经显示的相同项目,侧边栏不会关闭。