【问题标题】:.focused does not work if TextField is obscured by they iOS keyboard如果 TextField 被 iOS 键盘遮挡,.focused 不起作用
【发布时间】:2022-01-08 09:04:28
【问题描述】:

编辑:

在下面运行@Yrb 的代码后,我意识到 ForEach 循环是一个红鲱鱼。这里的实际问题是,如果您尝试聚焦被键盘遮挡的字段, .focused 只会关闭键盘。可使用以下代码重现:

struct TestFields: View {
    @State private var testField: String = ""
    @FocusState private var focusedField: Int?
    
    var body: some View {
        Form {
            ForEach(0..<20) { i in
                TextField("test", text: $testField)
                    .focused($focusedField, equals: i)
            }
        }
        .onSubmit {
            if let current = focusedField {
                focusedField = current + 1
            }
        }
    }
}

运行上述程序,您将看到正常行为,直到您的设备尝试聚焦您看不到的字段。

不确定我是否应该关闭它并打开一个具有正确标题的新问题。

原始问题:

在将 .focused 用于动态数量的文本字段时,我遇到了一些奇怪的行为:

struct TestView: View {
    @State private var options: [String] = Array(repeating: "", count: 7)
    @FocusState private var focusedField: Int?
    
    var body: some View {
        Form {
            ForEach($options.indices) { i in
                TextField("Option \(i + 1)", text: $options[i])
                    .focused($focusedField, equals: i)
            }
        }
        .onSubmit {
            if let current = focusedField {
                focusedField = current + 1
            }
        }
    }
}

上面的代码完全按照预期工作。您可以将计数更改为任何数字,然后点击返回进度到下一个字段。但是,将任何其他文本字段添加到此表单会破坏这一点。例如:

struct TestView: View {
    @State private var title: String = ""
    @State private var description: String = ""
    @State private var options: [String] = Array(repeating: "", count: 7)
    @FocusState private var focusedField: Int?
    
    var body: some View {
        Form {
            TextField("Title", text: $title)
                .focused($focusedField, equals: 0)
            TextField("Description", text: $description)
                .focused($focusedField, equals: 1)
            ForEach($options.indices) { i in
                TextField("Option \(i + 1)", text: $options[i])
                    .focused($focusedField, equals: 2 + i)
            }
        }
        .onSubmit {
            if let current = focusedField {
                print("setting focused field to \(current + 1)")
                focusedField = current + 1
            }
        }
        .onChange(of: focusedField) { newValue in
            print("focused field changed to \(newValue)")
        }
    }
}

在这种情况下,表单会在最后一个字段之前停止并关闭键盘。通过观察focusedField,我可以看到当更改为最后一个字段时它被设置为nil。您添加的文本字段越多,此逻辑越早中断。我不确定是什么问题。

focused field changed to Optional(0)
setting focused field to 1
focused field changed to Optional(1)
setting focused field to 2
focused field changed to Optional(2)
setting focused field to 3
focused field changed to Optional(3)
setting focused field to 4
focused field changed to Optional(4)
setting focused field to 5
focused field changed to Optional(5)
setting focused field to 6
focused field changed to Optional(6)
setting focused field to 7
focused field changed to Optional(7)
setting focused field to 8
focused field changed to nil

【问题讨论】:

  • 你绝对可以使用一个@FocusedState 和多个TextFields,这就是为什么它有一个修饰符接收Hashable 绑定而不仅仅是Bool。

标签: ios swift swiftui swiftui-form


【解决方案1】:

我认为您的猜测是“超出范围”错误。在此前提下,我提出了以下修复程序,似乎工作正常,并在最后一个返回时将您回滚到第一个 TextField。

struct FocusedForEach: View {
    @State private var title: String = ""
    @State private var description: String = ""
    @State private var options: [String] = Array(repeating: "", count: 7)
    @FocusState private var focusedField: Int?
    
    var body: some View {
        ScrollViewReader { reader in
            Form {
                TextField("Title", text: $title)
                    .id(0)
                    .focused($focusedField, equals: 0)
                TextField("Description", text: $description)
                    .id(1)
                    .focused($focusedField, equals: 1)
                ForEach($options.indices) { i in
                    TextField("Option \(i + 1)", text: $options[i])
                        .id(i + 1)
                        .focused($focusedField, equals: 2 + i)
                }
            }
            .onSubmit {
                if let current = focusedField {
                    if current == options.count + 1 {
                        reader.scrollTo(0, anchor: .bottom)
                        focusedField = 0
                        print("focusedField set to 0")
                    } else {
                        reader.scrollTo(current + 1, anchor: .bottom)
                        focusedField = current + 1
                        print("focusedField set to \(current + 1)")
                    }
                }
            }
            .onChange(of: focusedField) { newValue in
                guard let newValue = newValue else { return }
                print("focused field changed to \(newValue)")
            }
        }
    }
}

您显然不能将 focusedField 设置为 9,因为没有 10 个字段。我怀疑当您尝试关注不存在的TextField 9 时,.focused() 导致focusedField 恢复为零。这是一种预感,但似乎在结果中应运而生。

编辑:

嗯,看来键盘是问题的一部分,或者是问题的一部分。我用.scrollTo() 编辑了代码,以便在字段变为.focused() 之前移动它们,这样它们就不会被键盘隐藏。

【讨论】:

  • 哇哦。这次真是万分感谢。我运行了你的代码,它就像一个魅力。然后我把它放在我的开发设备上,同样的问题。经过一番调查,我找到了原因(但还没有解决方案)。我的开发设备处于横向模式。如果您在模拟器上运行代码并将其旋转到横向,您将看到与我看到的相同的错误。它似乎发生在键盘覆盖的任何第一个字段上。至少我现在有原因并且可以调查。使用新信息更新了原始问题。
  • 代码已更新。
  • 这似乎在模拟器中使用 enter 键工作,但使用软件键盘或使用真实设备时问题仍然存在。我在想我必须在 UIViewRepresentable 中包装 Textfields
猜你喜欢
  • 2023-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-22
  • 2016-09-01
  • 1970-01-01
  • 2014-03-27
  • 1970-01-01
相关资源
最近更新 更多