Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you move the cursor to the end in a SwiftUI TextField?

Tags:

swiftui

I am using a SwiftUI TextField with a Binding String to change the user's input into a phone format. Upon typing, the formatting is happening, but the cursor isn't moved to the end of the textfield, it remains on the position it was when it was entered. For example, if I enter 1, the value of the texfield (after formatting) will be (1, but the cursor stays after the first character, instead of at the end of the line.

Is there a way to move the textfield's cursor to the end of the line?

Here is the sample code:

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()
    }
}
like image 354
njdeveloper Avatar asked Sep 27 '19 14:09

njdeveloper


People also ask

How do I change TextField height in SwiftUI?

You are right! You not able to change height of TextField, its height is dependent on Font used to render it, except applying some custom TextFieldStyle It is not documented, and could change in future version ... and final look, which is what you are looking for ...


1 Answers

You might have to use UITextField instead of TextField. UITextField allows setting custom cursor position. To position the cursor at the end of the text you can use textField.endOfDocument to set UITextField.selectedTextRange when the text content is updated.

@objc func textFieldDidChange(_ textField: UITextField) {        
    let newPosition = textField.endOfDocument
    textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}

The following SwiftUI code snippet shows a sample implementation.

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)
        }
    }
}
like image 185
ddelver Avatar answered Sep 23 '22 04:09

ddelver