1

I have a UITextField and a button, the button highlights the last letter of the text field's text as well as dismissing the keyboard:

private func highlightLast() {
    guard let text = textField.text, text.count > 1 else {
        return
    }
    textField.resignFirstResponder()
    let attributedString = NSMutableAttributedString(string: text)
    attributedString.addAttribute(.backgroundColor, value: UIColor.yellow, range: NSRange(location: text.count - 1, length: 1))
    textField.attributedText = attributedString
}

Tapping the button for the first time works fine, but if I tap on the text field again to have the keyboard visible, then tap the button again, the whole text got highlighted:

With minor alteration, the followings work fine no matter how many times I do it:

  1. Highlight the last letter, or any letter, without dismissing the keyboard.
  2. Highlight any letter that's not the last letter, and also dismiss the keyboard.

It makes me wonder if I did something wrong with the range, or something else like some implementation difference based on keyboard dismissing?

Any help is appreciated!

Update:

I checked view hierarchy and below are when it works (ie. only last letter highlighted) vs it doesn't (ie. text fully highlighted):

When it works as expected:

Tes{
    NSBackgroundColor = "<UIDynamicSystemColor: 0x600001c89b40; name = systemBackgroundColor>";
    NSColor = "<UIDynamicSystemColor: 0x600001c8a780; name = labelColor>";
    NSFont = "<UICTFont: 0x7fad17d0c120> font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; font-size: 14.00pt";
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}";
}t{
    NSBackgroundColor = "UIExtendedSRGBColorSpace 1 1 0 1";
    NSColor = "<UIDynamicSystemColor: 0x600001c8a780; name = labelColor>";
    NSFont = "<UICTFont: 0x7fad17d0c120> font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; font-size: 14.00pt";
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}";
}

When it doesn't work that the whole text is highlighted:

Test{
    NSBackgroundColor = "UIExtendedSRGBColorSpace 1 1 0 1";
    NSColor = "<UIDynamicSystemColor: 0x600001c8a780; name = labelColor>";
    NSFont = "<UICTFont: 0x7fad17d0c120> font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; font-size: 14.00pt";
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}";
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
Heuristic
  • 5,087
  • 9
  • 54
  • 94

1 Answers1

1

From Apple's docs:

Discussion

This property is nil by default. Assigning a new value to this property also replaces the value of the text property with the same string data, albeit without any formatting information. In addition, assigning a new value updates the values in the font, textColor, and other style-related properties so that they reflect the style information starting at location 0 in the attributed string.

So, you need to "reset" the text field's attributes.

One approach:

override func viewDidLoad() {
    super.viewDidLoad()
    textField.addTarget(self, action: #selector(editingDidBegin(_:)), for: .editingDidBegin)
}

@objc func editingDidBegin(_ sender: Any) {
    let s = textField.text ?? ""
    textField.attributedText = NSAttributedString(string: s)
}
DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Thanks! The problem is I do need to maintain the highlighted background when the text field is re-edited, but your answer did point me to the right direction - I have to first add backgroundColor attribute to the whole text range with the system background color, before adding specific highlight background color to the last letter! – Heuristic Aug 24 '21 at 13:26