创建一个 SwiftUI 侧边栏

14

我想使用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应用程序时(我使用了iPad Pro 11英寸模拟器),当启动应用程序时没有选择任何屏幕。只有在我点击导航栏中的"返回"后,初始屏幕才会显示并显示我的主页视图。

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

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

Toggling the sidebar resets the state

这些只是{{SwiftUI}}的错误还是有其他方法可以使用{{Binding}}创建侧边栏?

1
似乎是与此处描述的问题相同:链接 - jlsiewert
1个回答

9
您需要在NavigationView { }中包含一个默认的次要视图,通常它是一个占位符,但您可以使用HomeScreen,例如。
struct MainNavigationView: View {
    @State var screen: Screen? = .home
   
    var body: some View {
        NavigationView {
            SidebarView(state: $screen)
            HomeScreen()
        }
        .navigationViewStyle(DoubleColumnNavigationViewStyle())
    }
}

关于单元格未重新选择的问题 - 截至iOS 14.2,不存在列表选择绑定(当不处于编辑模式时),因此选择会丢失。尽管列表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))

在紧凑模式下,当弹出主视图后取消行高亮时,加粗字体会被删除,看起来不太好看。也许有一种方法可以禁用该情况下的加粗.

你还需要注意两个其他的bug:

  1. 在竖屏模式下,侧边栏导航按钮需要点击两次才能显示边栏导航栏。
  2. 在竖屏模式下,如果显示侧边栏并选择已经显示的同一项,则侧边栏不会消失。

1
感谢您提供详细的答案,尽管它看起来只是一个错误。在我尝试了这些想法之后,我会在这里发布更新。 - jlsiewert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接