如何将光标移动到SwiftUI文本字段的末尾?

时间:2019-09-27 14:41:13

标签: swiftui

我正在使用带有绑定字符串的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()
    }
}

2 个答案:

答案 0 :(得分:0)

您可能必须使用UITextField而不是TextFieldUITextField允许设置自定义光标位置。要将光标置于文本的末尾,可以在文本内容更新时使用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