【问题标题】:NavigationLink with isActive creates unusual scrolling behavior in macOS SwiftUI app带有 isActive 的 NavigationLink 在 macOS SwiftUI 应用程序中创建了不寻常的滚动行为
【发布时间】:2021-11-28 21:13:42
【问题描述】:

我有一个具有 3 列视图的 macOS 应用程序。在第一列中,有一个List 很长的项目——可能有几百个项目。

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

例如,如果我向下滚动到第 100 项并单击它,List可能决定向上滚动到第 35 项左右(活动的NavigationLink 不在框架)。这种行为似乎有点不确定——它并不总是滚动到同一个地方。如果我在列表中滚动到我想要的项目,然后等待系统滚动条消失后再点击 NavigationLink,似乎不太可能滚动到一个奇怪的位置,但它没有让问题彻底消失。

如果我删除isActive 参数,则在单击NavigationLinks 时会保持滚动位置。


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

我想保留isActive,因为我有一些程序导航,我希望能够做到这一点取决于它。例如:

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

有什么方法可以在NavigationLink 上使用isActive 并避免不可预知的滚动行为?

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

【问题讨论】:

    标签: swift macos swiftui


    【解决方案1】:

    观察到的效果是因为您的主视图主体已刷新并且所有内部结构都已重建。为了避免这种情况,我们可以将视图层次结构的敏感部分分离到独立视图中,这样 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 测试

    【讨论】:

    • 有趣。谢谢 - 我有一种感觉,可能是你会解决它!我不得不承认,我很难预测这些行为。例如,移动 navigationBindingForItem 并将 Binding&lt;Bool&gt; 传递给 SidebarRowView 会导致问题再次出现,但我很难理解原因。
    • 我在其中发现了另一个变量,我无法完全理解。如果您有时间看stackoverflow.com/questions/69509443/…,我将不胜感激
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 2021-03-31
    • 2020-11-21
    • 2021-08-11
    • 2020-11-12
    • 2022-08-11
    相关资源
    最近更新 更多