代码之家  ›  专栏  ›  技术社区  ›  jnpdx

使用iActive的NavigationLink在macOS SwiftUI应用程序中创建了不寻常的滚动行为

  •  0
  • jnpdx  · 技术社区  · 3 年前

    我有一个macOS应用程序,有三列视图。在第一列中,有一个 List 有很多很长的东西,可能有几百件。

    enter image description here

    如果我的 NavigationLink 每个项目包含一个 isActive 参数,当单击NavigationLinks时,列表上会出现不可预测/不需要的滚动行为。

    例如,如果我向下滚动到项目100并单击它,则 列表 也许 决定向上滚动至第35项左右(其中 导航链接 不在画面中)。这种行为似乎有点不确定——它并不总是滚动到同一个位置。看来 不太可能 如果我在列表中滚动到所需的项目,然后等待系统滚动条消失,然后单击 导航链接 ,但这并不能使问题完全消失。

    如果我移除 活跃的 参数时,当单击 导航链接 s

    
    struct ContentView : View {
        var body: some View {
            NavigationView {
                SidebarList()
                Text("No selection")
                Text("No selection")
                    .frame(minWidth: 300)
            }
        }
    }
    
    struct Item : Identifiable, Hashable {
        let id = UUID()
        var name : String
    }
    
    struct SidebarList : View {
        @State private var items = Array(0...300).map { Item(name: "Item \($0)") }
        @State private var activeItem : Item?
        
        func navigationBindingForItem(item: Item) -> Binding<Bool> {
            .init {
                activeItem == item
            } set: { newValue in
                if newValue { activeItem = item }
            }
        }
        
        var body: some View {
            List(items) { item in
                NavigationLink(destination: InnerSidebar(),
                               isActive: navigationBindingForItem(item: item) //<-- Remove this line and everything works as expected
                ) {
                    Text(item.name)
                }
            }.listStyle(SidebarListStyle())
        }
    }
    
    struct InnerSidebar : View {
        @State private var items = Array(0...100).map { Item(name: "Inner item \($0)") }
        
        var body: some View {
            List(items) { item in
                NavigationLink(destination: Text("Detail")) {
                    Text(item.name)
                }
            }
        }
    }
    

    我想继续 活跃的 ,因为我有一些程序导航,我想能够做到这取决于它。例如:

    Button("Go to item 10") {
      activeItem = items[10]
    }
    

    有没有办法使用 活跃的 导航链接 避免不可预知的滚动行为?

    (使用macOS 11.3和Xcode 13.0构建和测试)

    0 回复  |  直到 3 年前
        1
  •  1
  •   Asperi    3 年前

    观察到的效果是因为主视图的主体被刷新,所有内部都被重建。为了避免这种情况,我们可以将视图层次结构的敏感部分分离为独立视图,这样SwiftUI引擎就可以看到依赖关系没有改变,视图也不应该更新。

    下面是一个固定部件:

    struct SidebarList : View {
        @State private var items = Array(0...300).map { Item(name: "Item \($0)") }
        @State private var activeItem : Item?
    
        var body: some View {
            List(items) {
              SidebarRowView(item: $0, activeItem: $activeItem)  // << here !!
            }.listStyle(SidebarListStyle())
        }
    }
    
    struct SidebarRowView: View {
        let item: Item
        @Binding var activeItem: Item?
    
        func navigationBindingForItem(item: Item) -> Binding<Bool> {
            .init {
                activeItem == item
            } set: { newValue in
                if newValue {
                        activeItem = item
                    }
            }
        }
    
        var body: some View {
            NavigationLink(destination: InnerSidebar(),
                                isActive: navigationBindingForItem(item: item)) {
                Text(item.name)
            }
        }
    }
    
    

    使用Xcode 13/macOS 11.6进行测试