How to set colored text segments on MSTextLayer via Mocha?

Hi everyone,

I’m building a Sketch plugin that needs to apply rich text with different colors to text layers programmatically. I need to parse HTML and apply inline styles (colors, fonts, etc.) to different segments of text.

What I’m trying to achieve: Set text like “this is red, this is blue” where each part has a different color applied. (This is just a basic thing i want to get to work, the end goal is to set more styling props like font size, weight etc)

**What I’ve tried:

  1. Creating NSMutableAttributedString with attributes:**
const mutaString = NSMutableAttributedString.alloc().init()
mutaString.replaceCharactersInRange_withString_(NSMakeRange(0, 0), plainString)

// Apply attributes using Sketch’s keys
mutaString.addAttribute_value_range_(“MSAttributedStringColorAttribute”, redColor, redRange)
mutaString.addAttribute_value_range_(“MSAttributedStringFontAttribute”, currentFont, redRange)

This works fine - the attributed string is created successfully.

2. Setting it on the layer - this is where it fails:

const msTextLayer = textLayer.sketchObject
msTextLayer.setAttributedString_(mutaString)  // ❌ Causes Obj-C exception

What doesn’t work:

  • msTextLayer.setAttributedString_() - Objective-C exception

  • MSAttributedString.alloc().initWithString_() - method doesn’t exist

  • MSAttributedString.alloc().initWithAttributedString_() - crashes

  • msAttribStr.mutableCopy() - Objective-C exception (when called on the result of msTextLayer.attributedString())_

Questions:

  1. What’s the correct way to set an attributed string on an MSTextLayer via the Mocha bridge?

  2. Should I be using MSColor instead of NSColor for the color attributes?

  3. Is there a different method signature or approach for setAttributedString that works?

  4. Is there an alternative API I should be using instead?

I found a snippet showing how to read attributes from an MSAttributedString, but I can’t find working examples of how to write them back.

Any help would be greatly appreciated!

Hey Peter,

So let’s start with defining fonts and colors that we’re going to apply to a text layer:

const text = NSString.stringWithString("hello, world");

const desiredFont = NSFont.fontWithName_size("Helvetica Neue", 12);
const desiredColor1 = NSColor.systemRedColor();
const desiredColor2 = NSColor.systemBlueColor();

Since those are native NSColor and NSFont instances, we’d need to turn them into Sketch-specific classes before applying them to an attributed string – we’ll get to it in a second.

Next, the attributed string itself is created exactly as you’ve suggested:

const attrstr = NSMutableAttributedString.new();
attrstr.replaceCharactersInRange_withString_(NSMakeRange(0, 0), text);

Now, font attributes:

attrstr.addAttribute_value_range_(
  "MSAttributedStringFontAttribute",
  desiredFont.fontDescriptor(), 
  NSMakeRange(0, text.length())
);

Note the .fontDescriptor() call.

Then, color attributes:

attrstr.addAttribute_value_range_(
  "MSAttributedStringColorAttribute",
  MSColor.colorWithNSColor(desiredColor1).immutableModelObject(),
  NSMakeRange(0, 5)
);
attrstr.addAttribute_value_range_(
  "MSAttributedStringColorAttribute",
  MSColor.colorWithNSColor(desiredColor2).immutableModelObject(),
  NSMakeRange(7, 5)
);

Just remember to wrap an NSColor object into MSColor.colorWithNSColor(...).immutableModelObject() – everything else should work as expected. Alternatively, if you have, say, a hex color string:

attrstr.addAttribute_value_range_(
  "MSAttributedStringColorAttribute",
  MSImmutableColor.colorWithSVGString("#0000ff"),
  NSMakeRange(7, 5)
);

Finally:

const textLayer = // ...
textLayer.sketchObject.setAttributedStringValue(attrstr);

And that’s it:

You know what, there’s actually a much more pleasant way to achieve the same result, without all this CocoaScript nonsense.

Let’s start by creating 3 Text objects and styling them however we want using the JS Sketch API:

const first = new sketch.Text({
  text: "Hello",
  style: {
    fontFamily: 'Comic Sans MS',
    fontSize: 14,
    fontWeight: 9,
    textColor: '#ff0000'
  }
})

const second = new sketch.Text({
  text: ", ",
  style: {
    fontFamily: 'Helvetica Neue',
    fontSize: 9,
    textUnderline: 'double dot'
  }
})

const third = new sketch.Text({
  text: "World!",
  style: {
    fontFamily: 'Helvetica Neue',
    kerning: 0.4,
    textColor: '#0000ff',
    fontStyle: 'italic',
  }
})

Then we grab an attributed string from each of those Texts and combine them into one:



const attrstr = NSMutableAttributedString.new()

attrstr.appendAttributedString(first.sketchObject.attributedStringValue())
attrstr.appendAttributedString(second.sketchObject.attributedStringValue())
attrstr.appendAttributedString(third.sketchObject.attributedStringValue())

Which we then apply to our text layer:

const textLayer = // ...
textLayer.sketchObject.setAttributedStringValue(attrstr)

And now that’s it: