我需要构建一个 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,而在未定义解雇时不会发生同样的情况?当定义解雇并且滚动列表时,PS 演示应用程序不断输出“主体正在重建”,但如果任何文本字段手动获得焦点,则即使仍然定义了解雇,也不会再打印“主体正在重建”。
我可以做出一个假设,但这实际上只是一个猜测(基于经验、观察等)。事实上,所有诸如“为什么会发生这个问题”之类的“为什么”都应该在https://developer.apple.com/forums/上询问(那里有 Apple 的工程师)或报告给https://developer .apple.com/bug-reporting/
解决方案是将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)