NSTextField: Multiple Colors of Text

Hi Community,
I’m working on a new ASOC project and I need to know, is there a way to make an NSTextField Label with multicolored text? Right now I’m using “setTextColor:”, but it’s changing the color of the whole string. I’ve been looking through Apple’s developer reference docs and I can’t seem to find anything. Is there any way to accomplish this? Thanks.

Sadly, no. Well, not with setColor:.

What you need is NSMutableAttributedString. Create a new NSMutableAttributedString, then use setAttributes:range: to set the color of texts by giving it ranges. In your case, you’ll need to use NSForegroundColorAttributeName to set the text’s color. Then set the text field’s contents with setObjectValue: (and not setStringValue or similar methods, since it is not an NSString object, but rather an id type object).

There is plenty of examples around in these forums, you should find what you need with these pointers.

Cheers!

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

Also check these references in the Xcode Documentation:

  • NSAttributedString Application Kit Additions Reference, specifically the Constants at the bottom.
  • Attributed String Programming Guide, for guidance and exemples in Cocoa.

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

Ok, so I named the NSTextField Label “theLabel” and I’m using the code below. When I just try to do one of the two colored strings, it works fine, but when I try to do both colors, the function errors out every time. Do you know what’s happening? Here’s the code I’m using:

tell current application's NSColorList to set AppleColors to colorListNamed_("Apple")
		set redColor to AppleColors's colorWithKey_("Red")
		set greenColor to AppleColors's colorWithKey_("Green")
		tell current application's NSFont to set myFont to fontWithName_size_("Verdana", 11)
		set attrsDict to current application's NSDictionary's dictionaryWithObjects_forKeys_({myFont, redColor}, {current application's NSFontAttributeName, current application's NSForegroundColorAttributeName})
		tell current application's NSAttributedString to set thisText to alloc()'s initWithString_attributes_("THIS", attrsDict)
		set attrsDict to current application's NSDictionary's dictionaryWithObjects_forKeys_({myFont, greenColor}, {current application's NSFontAttributeName, current application's NSForegroundColorAttributeName})
		tell current application's NSAttributedString to set thatText to (alloc()'s initWithString_attributes_("... is a test", attrsDict))
		
		theLabel's setObjectValue_(thisText & thatText)

I believe you can’t set the text field’s content with “thisText & thatText” because they are two cocoa objects that can’t really be coerced to anything corresponding in Applescript. I would append one to the other. Try:

theLabel’s setObjectValue_(thisText’s appendAttributedString_(thatText))

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

Hmm, well it’s giving me a different error this time, so i suppose that’s some progress. Here’s the error I’m getting:

-[NSConcreteAttributedString appendAttributedString:]: unrecognized selector sent to instance 0x2000fcca0

My mistake :). This method is for an NSMutableAttributedString object. You created two NSAttributedString objects but I assumed you had mutable objects. You should change them to mutable, then it should work. Cuz I don’t see a method to do the same quickly with the non-mutable version.

Awesome, it’s working perfectly now. Thank you so much!!

You’re welcome. Personally, I find the mutable version simpler and more powerful to use than the non-mutable version of attributed strings. There is a LOT of things you can do with this, believe me!

Good luck with your project!

If the Mutable version is so much better, why ever use the Non-Mutable? Is there some advantage to that one?

Also (I promise this will be my last question), is there any way to pull out individual characters of the NSMutableAttributedString by specifying their character number? Thanks again!

It’s not that it’s so much better, it’s just my personal preference. Usually, when I need to use attributed strings, it’s easier with mutable versions, thus I became accustomed to them.

And to answer your last question, if you look in the docs, every methods of the parent class (in this case NSAttributedString) are available, and one is aptly named:

- (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange

and to create a range in ASOC:

{|location|:0, |length|:3}

location refers to where your text cursor would be just before the letter you need to start extracting from and the length refers to how many characters from this location should it get.

Remember to always add checks before to see if there is enough characters in the string, like using “length” method, otherwise you’ll end up with out of range errors.

Good luck!

Perfect, works like a charm! Thank you so much for everything!

If you want to change attributes in a mutable attributed string it would be easier to use the addAttribute_value_range_ method (or setAttributes:range:). attributedSubstringFromRange: creates a new attributed string for which you would then have to set the attributes, and after you did that for each character or group of characters, you would have to reassemble the whole string.

By using addAttribute_value_range_, you could easily loop through the whole string, setting a new color for each character. For instance, you could have an array of color objects, and use their index in the array to set the colors in a loop:

property parent : class "NSObject"
    property nsc:class "NSColor"
    property tf:missing value
	
	on applicationWillFinishLaunching_(aNotification)
		set aString to current application's NSMutableAttributedString's alloc()'s initWithString_("This is my string")
        set colorArray to current application's NSArray's arrayWithObjects_(nsc's redColor(),nsc's orangeColor(),nsc's yellowColor(),nsc's greenColor(),nsc's blueColor,nsc's purpleColor(),missing value)
        repeat with i from 0 to aString's |length|()-1
            aString's  addAttribute_value_range_(current application's NSForegroundColorAttributeName,colorArray's objectAtIndex_(i mod 6),{i,1})
        end repeat
        tf's setAttributedStringValue_(aString)
	end applicationWillFinishLaunching_

Ric