【问题标题】:SwiftUI List with @FocusState and focus change handling带有 @FocusState 和焦点更改处理的 SwiftUI 列表
【发布时间】:2022-08-23 20:54:19
【问题描述】:

我想使用List@FocusState 跟踪焦点,并使用.onChanged(of: focus) 确保当前焦点字段在ScrollViewReader 中可见。问题是:当所有东西都设置在一起时,List 在滚动过程中不断重建,使得滚动不像它需要的那样平滑。

当我附加.onChanged(of: focus) 时,我发现List 在滚动时会重建。如果我用ScrollView 替换List,问题就消失了,但我喜欢List 的外观,我需要部分支持,我需要编辑功能(例如删除、移动项目),所以我需要坚持@987654331 @ 看法。

我使用Self._printChanges() 来查看是什么让身体在滚动时重建自身,输出如下:

ContentView: _focus changed.
ContentView: _focus changed.
ContentView: _focus changed.
ContentView: _focus changed.
...

附在.onChanged(of: focus) 的封盖上没有打印任何内容。下面是一个简化的例子,在这个例子中滚动的平滑度不是问题,但是,一旦列表内容或多或少复杂,平滑滚动就会消失,这真的是由于.onChanged(of: focus) :(

问题:是否有机会监听焦点变化并且不会引发列表在滚动时重建自身?

struct ContentView: View {
    enum Field: Hashable {
        case fieldId(Int)
    }
    
    @FocusState var focus: Field?
    @State var text: String = \"\"
    
    var body: some View {
        List {
            let _ = Self._printChanges()
            ForEach(0..<100) {
                TextField(\"Enter the text for \\($0)\", text: $text)
                    .id(Field.fieldId($0))
                    .focused($focus, equals: .fieldId($0))
            }
        }
        .onChange(of: focus) { _ in
            print(\"Not printed unless focused manually\")
        }
    }
}
  • 我没有观察到 Xcode 13.4 / iOS 15.5 的描述行为。滚动时没有输出,在任何文本字段中点击一个“ContentView:_focus changed”和一个“Not print unless手动聚焦”按预期打印。
  • @Asperi 这个想法是不要关注任何领域,请滚动列表。让我知道您是否仍然在滚动时看不到“ContentView: _focus changed.\”。一旦一个字段获得焦点,滚动就真的没有输出了。 (Xcode 13.4.1 /iOS 15.5)
  • 是的,有趣的效果......不能说它是否是一个错误。
  • 我注意到带有 LazyVStack 的 ScrollView 提供与 List 相同的效果,因此这与视图的动态特性有关:行是动态创建的,而 .focused 修饰符 \"changes\" 焦点 var 反过来又是由于存在of .onChange(of: focus) 触发重建...这是我的理论:) 但是如何解决呢?我正在考虑仅在未滚动 List 时附加 .onChange(of:focus) ,但我不知道该怎么做。

标签: swiftui


【解决方案1】:

我建议考虑将列表行内容分离到独立视图中,并使用类似焦点“选择”的方法。每行都有FocusState internal 可以防止父视图进行不必要的更新(我假设类似于预先“设置”)。

使用 Xcode 13.4 / iOS 15.5 测试

struct ContentView: View {

    enum Field: Hashable {
        case fieldId(Int)
    }

    @State private var inFocus: Field?

    var body: some View {
        List {
            let _ = Self._printChanges()
            ForEach(0..<100, id: \.self) {
                ExtractedView(i: $0, inFocus: $inFocus)
            }
        }
        .onChange(of: inFocus) { _ in
            print("Not printed unless focused manually")
        }
    }

    struct ExtractedView: View {
        let i: Int
        @Binding var inFocus: Field?

        @State private var text: String = ""
        @FocusState private var focus: Bool     // << internal !!

        var body: some View {
            TextField("Enter the text for \(i)", text: $text)
                .focused($focus)
                .id(Field.fieldId(i))
                .onChange(of: focus) { _ in
                    inFocus = .fieldId(i)     // << report selection outside
                }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2022-01-14
    • 1970-01-01
    • 2022-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多