使用 SwiftUI 将 TextField 限制为 x 个字符

Limit TextField to x amount of characters using SwiftUI

使用 iOS13.2、Swift-5.1.2、Xcode-11.2,我尝试以下操作:

我想使用 TextField。用户只能在 TextField 中输入 x 个字符。

我的代码如下所示:

import Combine
import SwiftUI

class Entry: ObservableObject {

  @Published var entry = "" {
    didSet {
        entry = String(entry.prefix(6)) // trying to limit to 6 characters
    }
  }
}

并且在上面的代码中,已经有了异常行。

我可以看出 didSet{...} 是错误的(因为我们最终陷入无限循环 setting/didSetting 一次又一次)...

将 TextField 限制为 x 个字符的更好方法是什么?

这是代码的其余部分:

struct NumberView: View {
    var body: some View {

        Group {
            HStack {
                Spacer()
                NumberIcon(number: 1)
                NumberIcon(number: 2)
                NumberIcon(number: 3)
                NumberIcon(number: 4)
                NumberIcon(number: 5)
                NumberIcon(number: 6)
                Spacer()
            }
         }
    }
}
struct NumberIcon: View {
    @ObservedObject private var entry = Entry()
    var number: Int = 0
    var body: some View {
        TextField(" ", text: $entry.entry, onEditingChanged: { editing in
            print(editing)
            print(self.$entry)
        })
            .padding()
            .foregroundColor(Color.black)
            .background(Color.green)
            .font(.largeTitle)
            .lineLimit(1)
            .cornerRadius(16.0)
            .clipped()
            .keyboardType(.numberPad)
    }
}

struct NumberView_Previews: PreviewProvider {
    static var previews: some View {
        NumberView()
    }
}

我知道有 UIKit 包装器可以使用旧的 shouldChangeCharactersIn 委托方法形式 UITextFieldDelegate - 但我想纯粹用SwiftUI(无 UI 套件代码)。我该怎么做?

感谢,我找到了解决方案:

条目class可以重写:

class Entry: ObservableObject {

    let characterLimit = 6   // limiting to 6 characters
    @Published var entry = "" {
        didSet {
            if entry.count > characterLimit && oldValue.count <= characterLimit {
                entry = oldValue
            }
        }
    }
}

在 SwiftUI 中实现此目的的最简单方法是在达到最大字符数时禁用 TextField:

struct LimittedTextField: View {

    @State private var entry = ""

    let characterLimit = 6

    var body: some View {
        TextField("", text: $entry)
            .disabled(entry.count > (characterLimit - 1))
    }
}

我已经为这些类型的用例编写了 library

import DataField

struct ContentView: View {

    @State private var text = ""
    private let characterLimit = 6

    var body: some View {
        DataField("My Field", data: $text) { text in text.count < 6 } 
    }
}

你可以用这个:

.onReceive(variable.publisher.collect()) {
        variable = String([=10=].prefix(4))
}