Search code examples
swiftnsattributedstringnsrangensmutableattributedstring

Why does my playground crash on NSRange()


I'm trying to learn about NSMutableAttributedString and NSAttributedString so I've created a simple playground to try some things. However, I have a couple of issues I can't figure out even after looking at a lot of examples on SO (like here and NSRange from Swift Range? elsewhere.

The problem is with the length property. If I specify the length as attribLabelText.length it causes an uncaught exception. If I specify attribLabelText.length - 1 there's no error, but only the letters 'Repor' have the attributes I'm setting:

enter image description here

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let label = getLabel1(labelText: "Report")
        view.addSubview(label)
        self.view = view
    }
}

func getLabel1(labelText: String) -> UILabel {
    let label = UILabel()
    label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
    label.textColor = .black

    let attribLabelText: NSMutableAttributedString = NSMutableAttributedString(string: labelText)
    let attributes = [
        NSAttributedString.Key.foregroundColor : UIColor.gray.cgColor,
        NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 14)
        ] as [NSAttributedString.Key : Any]

    attribLabelText.addAttributes(attributes, range: NSRange(location: 0, length: attribLabelText.length)) <-- this causes an uncaught exception of type NSException

    label.attributedText = attribLabelText

    return label
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

I have a feeling this is going to be something obvious but I'm out of ideas to try. Can anybody point out what I'm doing wrong?


Solution

  • You should be passing a UIColor for the foreground color instead of a CGColor.

    UILabel seems to use the foreground color attribute slightly differently when drawing the string based on whether the attribute covers the entire range of the string or just a subrange.

    The version it uses when the attribute covers the entire string only works with UIColor but the version it uses when the attribute only covers a substring seems to also work with CGColor (though this behavior isn't documented so it shouldn't be relied on) which explains why adding the -1 to the range avoids the exception.