@Environment Dismiss 的存在会导致列表在滚动时不断重建其内容

Ser*_*gey 6 ios swiftui

我需要构建一个 TextFields 列表,其中每个字段都与焦点 id 相关联,以便我可以在接收焦点时自动滚动到这样的文本字段。实际上,真正的应用程序有点复杂,其中还包括文本编辑器和许多其他控件。

现在,我发现如果我的视图定义了@Environment(\.dismiss) private var dismiss,那么列表在手动滚动期间会一直重建。如果我只是注释掉该行,@Environment(\.dismiss) private var dismiss那么当我滚动时不会重建列表。显然,我希望能够在用户单击某个按钮时关闭我的视图。在真正的应用程序中,情况更糟:在滚动期间,一切都滞后,我无法平滑滚动。我的清单并不大,只有 10 件左右。

这是一个演示示例:

struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink {
                DismissListView()
            } label: {
                Text("Go to see the list")
            }
        }
    }
}

struct DismissListView: View {
    @Environment(\.dismiss) private var dismiss
    
    enum Field: Hashable {
        case line(Int)
    }
    
    @FocusState private var focus: Field?
    @State private var text: String = ""
    
    var body: some View {
        ScrollViewReader { proxy in
            List {
                let _ = print("body is rebuilding")
                
                Button("Dismiss me") {
                    dismiss()
                }
                
                Section("Section") {
                    ForEach((1...100), id: \.self) {num in
                        TextField("text", text: $text)
                            .id(Field.line(num))
                            .focused($focus, equals: .line(num))
                    }
                }
            }
            .listStyle(.insetGrouped)
            .onChange(of: focus) {_ in
                withAnimation {
                    proxy.scrollTo(focus, anchor: .center)
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是:

  • 为什么在定义时手动来回滚动期间会重建列表@Environment(\.dismiss) private var dismiss,而在未定义解雇时不会发生同样的情况?
  • 有没有解决方法:当焦点发生变化时,我需要能够使用 ScrollProxyReader 来聚焦任何文本字段,并且我需要能够关闭视图,但同时我需要避免不断重建列表在滚动期间,因为它会降低应用程序性能并且滚动变得锯齿状......

当定义解雇并且滚动列表时,PS 演示应用程序不断输出“主体正在重建”,但如果任何文本字段手动获得焦点,则即使仍然定义了解雇,也不会再打印“主体正在重建”。

Asp*_*eri 4

  1. 我可以做出一个假设,但这实际上只是一个猜测(基于经验、观察等)。事实上,所有诸如“为什么会发生这个问题”之类的“为什么”都应该在https://developer.apple.com/forums/上询问(那里有 Apple 的工程师)或报告给https://developer .apple.com/bug-reporting/

  2. 解决方案是将dismiss依赖部分分离到专用视图中,因此将其从父体中隐藏(因此不会影响它)

    struct DismissView: View {
        // visible only for this view !!
        @Environment(\.dismiss) private var dismiss

        var body: some View {
            Button("Dismiss me") {
                // affects current context, so it does not matter
                // in which sub-view is called
                dismiss()           
            }
        }
    }

    var body: some View {
        ScrollViewReader { proxy in
            List {
                let _ = print("body is rebuilding")
                DismissView()   // << here !!

    // ... other code

Run Code Online (Sandbox Code Playgroud)