我正在使用带有绑定字符串的SwiftUI TextField将用户输入更改为电话格式。键入时,正在进行格式化,但是光标没有移动到文本字段的末尾,而是保持在输入时所在的位置。例如,如果我输入1
,则texfield的值(格式化后)将为(1
,但是光标停留在第一个字符之后,而不是在行尾。
是否可以将文本字段的光标移动到行尾?
这是示例代码:
import SwiftUI
import AnyFormatKit
struct ContentView: View {
@State var phoneNumber = ""
let phoneFormatter = DefaultTextFormatter(textPattern: "(###) ###-####")
var body: some View {
let phoneNumberProxy = Binding<String>(
get: {
return (self.phoneFormatter.format(self.phoneNumber) ?? "")
},
set: {
self.phoneNumber = self.phoneFormatter.unformat($0) ?? ""
}
)
return TextField("Phone Number", text: phoneNumberProxy)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
答案 0 :(得分:0)
您可能必须使用UITextField
而不是TextField
。 UITextField
允许设置自定义光标位置。要将光标置于文本的末尾,可以在文本内容更新时使用textField.endOfDocument
来设置UITextField.selectedTextRange
。
@objc func textFieldDidChange(_ textField: UITextField) {
let newPosition = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
以下SwiftUI代码片段显示了示例实现。
import SwiftUI
import UIKit
//import AnyFormatKit
struct ContentView: View {
@State var phoneNumber = ""
let phoneFormatter = DefaultTextFormatter(textPattern: "(###) ###-####")
var body: some View {
let phoneNumberProxy = Binding<String>(
get: {
return (self.phoneFormatter.format(self.phoneNumber) ?? "")
},
set: {
self.phoneNumber = self.phoneFormatter.unformat($0) ?? ""
}
)
return TextFieldContainer("Phone Number", text: phoneNumberProxy)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
/************************************************/
struct TextFieldContainer: UIViewRepresentable {
private var placeholder : String
private var text : Binding<String>
init(_ placeholder:String, text:Binding<String>) {
self.placeholder = placeholder
self.text = text
}
func makeCoordinator() -> TextFieldContainer.Coordinator {
Coordinator(self)
}
func makeUIView(context: UIViewRepresentableContext<TextFieldContainer>) -> UITextField {
let innertTextField = UITextField(frame: .zero)
innertTextField.placeholder = placeholder
innertTextField.text = text.wrappedValue
innertTextField.delegate = context.coordinator
context.coordinator.setup(innertTextField)
return innertTextField
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<TextFieldContainer>) {
uiView.text = self.text.wrappedValue
}
class Coordinator: NSObject, UITextFieldDelegate {
var parent: TextFieldContainer
init(_ textFieldContainer: TextFieldContainer) {
self.parent = textFieldContainer
}
func setup(_ textField:UITextField) {
textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
}
@objc func textFieldDidChange(_ textField: UITextField) {
self.parent.text.wrappedValue = textField.text ?? ""
let newPosition = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
}
}
答案 1 :(得分:0)
不幸的是,我无法评论ddelver的出色答案,但我只是想为我补充一下,当我更改绑定字符串时,这是行不通的。
我的用例是,我有一个自定义文本字段组件,该组件用于编辑列表中的所选项目,因此,当您更改所选项目时,绑定字符串也会更改。这意味着只要绑定发生更改,就会调用TextFieldContainer
的{{1}}方法,但是协调器内部的init
仍然引用了初始父对象。
我是Swift的新手,所以可能对此有更好的解决方法,但是我通过向Coordinator添加方法来解决了此问题:
parent
,然后像这样从func updateParent(_ parent : TextFieldContainer) {
self.parent = parent
}
调用它:
func updateUIView