iosswiftuikituitextfielduitextfielddelegate

UITextField selects all text on focus when the content is long — how to keep the caret at the end?


I’m building a custom input field using UITextField. When the user taps to focus the field and the text is long, the entire text becomes selected by default. This is the same behavior you can see in iOS Safari’s search field or the Messages app search field.

What I want: when the field becomes first responder, the caret should be placed at the end of the text (latest word), without selecting all the text.

Here’s the code that builds my text field:

public func makeTextField() -> UITextField {
    let textField = UITextField()

    textField.autocorrectionType = .no
    textField.setContentCompressionResistancePriority(.required, for: .horizontal)
    textField.setContentCompressionResistancePriority(.required, for: .vertical)
    if #available(iOS 13.0, *) {
        textField.smartInsertDeleteType = .no
    }
    textField.smartQuotesType = .no
    textField.smartDashesType = .no
    textField.autocapitalizationType = .none
    textField.contentMode = .scaleToFill

    if let font = attributes[.font] as? UIFont {
        textField.font = font
    }
    if let color = attributes[.foregroundColor] as? UIColor {
        textField.textColor = color
    }

    // Truncate long text at the head
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineBreakMode = .byTruncatingHead
    textField.defaultTextAttributes[.paragraphStyle] = paragraphStyle

    textField.delegate = self
    textField.backgroundColor = .clear
    textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
    return textField
}

What I’ve tried Forcing the caret to the end in textFieldDidBeginEditing:

func textFieldDidBeginEditing(_ textField: UITextField) {
    let end = textField.endOfDocument
    textField.selectedTextRange = textField.textRange(from: end, to: end)
}

Doing the same asynchronously (next runloop) to avoid the system overriding selection:

func textFieldDidBeginEditing(_ textField: UITextField) {
    DispatchQueue.main.async {
        let end = textField.endOfDocument
        textField.selectedTextRange = textField.textRange(from: end, to: end)
    }
}

Despite these, the system still selects all text on focus when the string is long/truncated at the head.


Solution

  • After trying different configuration for the textField, I found the solution by setting

    textField.autocorrectionType = .no
    textField.spellCheckingType = .no