创建一个SwiftUI边栏 [英] Create a SwiftUI Sidebar

查看:119
本文介绍了创建一个SwiftUI边栏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to build a very simple iOS 14 sidebar using SwiftUI. The setup is quite simple, I have three views HomeView, LibraryView and SettingsView and an enum representing each screen.

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

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

My end-goal is to automatically switch between a tab view and a sidebar depending on the size class but some things don't quite work as expected.

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

The global state is owned by the MainNavigationView, which is also the root view for my WindowGroup.

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

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

The SidebarView is a simple List containing three NavigationLink, one for each 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 .

I use the NavigationLink(destination:tag:selection:label) initializer so that the selected screen is set in my MainNavigationView so I can reuse that for my TabView later.

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

However, a lot of things don't quite work as expected.

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

First, when launching the app in a portrait-mode iPad (I used the iPad Pro 11-inch simulator), no screen is selected when launching the app. Only after I click Back in the navigation bar, the initial screen shows and my home view gets shown.

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

The second weird thing is that the binding seems to be set to nil whenever the sidebar gets hidden. In landscape mode the view works as expected, however when toggling the sidebar to hide and then shown again, the selection gets lost. The content view stays correct, but the sidebar selection is lost.

这些仅仅是 SwiftUI 的错误,还是有不同的方式来创建带有 Binding 的边栏?

Are these just SwiftUI bugs or is there a different way to create a sidebar with a Binding?

推荐答案

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

You need to include a default secondary view within the NavigationView { }, usually it would be a placeholder but you could use the HomeScreen, e.g.

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上受支持.您可以看到该信息的标题:

Regarding the cell not re-selecting - as of iOS 14.2 there is no list selection binding (when not in editing mode) so selection is lost. Although the List API has a $selection param, it is only supported on macOS at the moment. You can see that info the header:

/// 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,以解决这些问题,所以现在我们只需要忍受它即可.

It's a bit convoluted but it means that selection binding that we need for a sidebar is only for macOS, on iOS it is only for multi-select (i.e. checkmarks) in edit mode. The reason could be since UITableView's selection is event driven, maybe it wasn't possible to translate into SwiftUI's state driven nature. If you've ever tried to do state restoration with a view already pushed on a nav controller and try to show the cell unhighlight animation when popping back and that table view wasn't loaded and cell was never highlighted in the first place you'll know what I mean. It was a nightmare to load the table synchronously, make the selected cell be drawn and then start the unhighlight animation. I expect that Apple will be reimplementing List, Sidebar and NavigationView in pure SwiftUI to overcome these issues so for now we just have to live with it.

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

Once this has been fixed it will be as simple as List(selection:$screen) { } like how it would work on macOS. As a workaround on iOS you could highlight the icon or text in your own way instead, e.g. try using bold text:

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

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

It doesn't look very nice when in compact because after popping the main view, the bold is removed when the row is un-highlighted. There might be a way to disable using bold in that case.

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

There are 2 other bugs you should be aware of:

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

这篇关于创建一个SwiftUI边栏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆