创建一个SwiftUI侧边栏

时间:2020-07-06 17:30:42

标签: ios swiftui swiftui-navigationlink

我想使用SwiftUI构建一个非常简单的iOS 14侧边栏。 设置非常简单,我有三个视图HomeViewLibraryViewSettingsView,还有一个代表每个屏幕的枚举。

enum Screen: Hashable {
   case home, library, settings
}

我的最终目标是根据大小级别自动在选项卡视图和侧边栏之间切换,但是某些功能并不能按预期工作。

全局状态由MainNavigationView拥有,这也是我的WindowGroup的根视图。

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

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

我使用NavigationLink(destination:tag:selection:label)初始化程序,以便在我的MainNavigationView中设置选定的屏幕,以便以后可以将其重新用于TabView

但是,很多事情并没有达到预期效果。

首先,在人像模式iPad(我使用11英寸iPad Pro仿真器)中启动应用程序时,在启动应用程序时未选择任何屏幕。只有在导航栏中单击 Back 后,才会显示初始屏幕,并显示我的主视图。

First bug: HomeView is only shown after the Back button was tapped

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

Toggling the sidebar resets the state

这些仅仅是SwiftUI的错误还是使用Binding创建侧边栏的另一种方式?

1 个答案:

答案 0 :(得分:4)

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

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

关于未重新选择单元格的问题-从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.

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

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

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

enter image description here

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

还有两个您应该注意的错误:

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