我想对输入到 SwiftUI TextField 中的值进行实时格式化,因此我使用 introspect 添加自定义委托,就像在 UIKit 中一样。这导致 .onChange 停止工作。如果我注释掉委托,.onChange 块就会起作用。如何让绑定与自定义委托一起使用?
文本字段视图:
import Foundation
import SwiftUI
struct TextFieldView: View {
@State private var inputString: String = ""
var customDelegate = CustomTextFieldDelegate()
var body: some View {
VStack {
TextField("", text: $inputString)
.addTextFieldDelegate(delegate: customDelegate)
}
.onChange(of: inputString) { _ in
print("inputString: \(inputString)")
}
}
}
反省:
extension View {
func addTextFieldDelegate(delegate: UITextFieldDelegate) -> some View {
introspect(.textField, on: .iOS(.v15...)) { textfield in
textfield.delegate = delegate
}
}
}
自定义代表:
class CustomTextFieldDelegate: NSObject, UITextFieldDelegate {
override init() { }
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text, let textRange = Range(range, in: text) else {
return false
}
var updatedText = text.replacingCharacters(in: textRange, with: string)
updatedText.removeAll(where: {$0 == ":"})
let finalLength = updatedText.count + updatedText.count/2 - 1
if finalLength > 8 {
return false
}
for i in stride(from: 2, to: finalLength, by: 3) {
let index = updatedText.index(updatedText.startIndex, offsetBy: i)
updatedText.insert(":", at: index)
}
textField.text = updatedText
return false
}
}
编辑01:
我想要实现的是在将输入内容键入文本字段时添加一个“:”每两个字符。我不想使用内省,但不确定如何修改变量 inputString 而不再次触发 onChange 处理程序,从而导致无限循环。
您不应该依赖 SwiftUI 是基于 UIKit 构建的事实 - 这很可能在未来发生变化。
您不需要使用
UITextFieldDelegate
- 您可以在 onChange
处理程序中执行处理。
例如:
struct ContentView: View {
@State var oldValue = ""
@State var inputString = ""
var body: some View {
VStack {
TextField("XX:XX:XX:XX", text:$inputString)
}
.onChange(of: inputString) { newValue in
self.formatTextField(newValue)
}
}
func formatTextField(_ newValue: String) {
guard self.oldValue != self.code else {
return
}
var finalValue = newValue
if newValue.count > self.oldValue.count {
finalValue = self.processInsert(newValue: newValue, oldValue: self.oldValue)
} else {
finalValue = self.processDelete( newValue: newValue, oldValue: self.oldValue)
}
self.oldValue = finalValue
self.inputString = finalValue
}
func processInsert(newValue: String, oldValue:String) -> String {
guard newValue.count < 12 else {
return oldValue
}
var characters = newValue
characters.removeAll { $0 == ":"}
var finalValue = ""
for i in 0..<characters.count {
finalValue.append(characters[characters.index(characters.startIndex,offsetBy: i)])
if i < 7 && i > 0 && (i-1) % 2 == 0 {
finalValue.append(":")
}
}
return finalValue
}
func processDelete(newValue: String, oldValue: String) -> String {
var finalValue = newValue
var input = finalValue
input.removeAll { $0 == ":"}
let length = input.count
if length > 0 && length % 2 == 0 {
finalValue = String(finalValue.prefix(finalValue.count-1))
}
return finalValue
}
}