Aviran
Aviran

Reputation: 5458

Setting foreground color works only once for NSAttributedString

The phone part of the string gets the underline attribute, but the color remains red. I've separated the color and underline setAttributes() calls, to make things clear, the same happens when it's one call.

    let text = "call "
    let phone = "1800-800-900"

    let attrString = NSMutableAttributedString(string: text + phone, attributes: nil)
    let rangeText = (attrString.string as NSString).range(of: text)
    let rangePhone = (attrString.string as NSString).range(of: phone)

    attrString.setAttributes([NSAttributedStringKey.foregroundColor: UIColor.red],
                             range: rangeText)

    attrString.setAttributes([NSAttributedStringKey.foregroundColor: UIColor.blue],
                             range: rangePhone)

    attrString.setAttributes([NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue],
                             range: rangePhone)

Upvotes: 0

Views: 1719

Answers (2)

Larme
Larme

Reputation: 26026

From the doc of setAttributes():

These new attributes replace any attributes previously associated with the characters in aRange.

So in other words, it replace them, erasing all previously set, so when you add the underline, it removes the color at that range.

Solution, use addAttributes() instead of setAttributes():

let text = "call "
let phone = "1800-800-900"

let attrString = NSMutableAttributedString(string: text + phone, attributes: nil)
let rangeText = (attrString.string as NSString).range(of: text)
let rangePhone = (attrString.string as NSString).range(of: phone)

attrString.addAttributes([NSAttributedStringKey.foregroundColor: UIColor.red],
                         range: rangeText)

attrString.addAttributes([NSAttributedStringKey.foregroundColor: UIColor.blue],
                         range: rangePhone)

attrString.addAttributes([NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue],
                         range: rangePhone)

Other solution, use two NSAttributedString (I also remove the NSAttributedStringKey in the enum)

let textAttrStr = NSAttributedString(string:text, attributes:[.foregroundColor: UIColor.red])
let phoneAttrStr = NSAttributedString(string:phone, attributes:[.foregroundColor: UIColor.blue,
                                                               .underlineStyle: NSUnderlineStyle.styleSingle.rawValue])

let finalAttrStr = NSMutableAttributedString.init(attributedString: textAttrStr)
finalAttrStr.append(phoneAttrStr)

Possible issue with the first solution:
range(of:) returns the range of the first occurence only. In other words, if text = "1800 " and phone = "18", you'll get unwanted results. because rangePhone would be from index 0 to 1, and not 7 to 8 in 1800 18. This issue won't happen in the second one.

Upvotes: 3

glyvox
glyvox

Reputation: 58029

You should not separate the second and the third call of setAttributes, since the latter will overwrite the earlier. Combine the styles in one array:

attrString.setAttributes([.foregroundColor: UIColor.blue,
                          .underlineStyle: NSUnderlineStyle.styleSingle.rawValue],
                          range: rangePhone)

Result:

screenshot of the result

Upvotes: 0

Related Questions