CATextLayer + NSAttributtedString + CTParagraphStyleRef
-
30-05-2021 - |
Question
I want to have some text with a custom line-spacing, so I wrote an attribute string with CTParagraphStyleAttributte
and pass it to my CATextLayer
:
UIFont *font = [UIFont systemFontOfSize:20];
CTFontRef ctFont = CTFontCreateWithName((CFStringRef)font.fontName,
font.pointSize, NULL);
CGColorRef cgColor = [UIColor whiteColor].CGColor;
CGFloat leading = 25.0;
CTTextAlignment alignment = kCTRightTextAlignment; // just for test purposes
const CTParagraphStyleSetting styleSettings[] = {
{kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof(CGFloat), &leading},
{kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment}
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(styleSettings, 2));
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(id)ctFont, (id)kCTFontAttributeName,
(id)cgColor, (id)kCTForegroundColorAttributeName,
(id)paragraphStyle, (id)kCTParagraphStyleAttributeName,
nil];
CFRelease(ctFont);
CFRelease(paragraphStyle);
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc]
initWithString:string
attributes:attributes];
_textLayer.string = attrStr;
[attrStr release];
But the line height is not changing. I think I am missing something here but I don't know what.
I've tried with kCTParagraphStyleSpecifierLineSpacingAdjustment
and kCTParagraphStyleSpecifierLineSpacing
but either of them don't seem to work (?). I tried also to set the alignment using kCTParagraphStyleSpecifierAlignment
(I know CATextLayer
has a property for that) just to test kCTParagraphStyleAttributeName
is indeed working and it didn't.
I've noticed that even if I pass some crazy values (for example: CTParagraphStyleCreate(styleSettings, -555);
) which leads me to ask myself: Does CATextLayer
support paragraph attributes? If so, what am I missing here?
Solution
I tried your code, putting the NSAttributedString in a CATextLayer, and it ignored the formatting, as you said.
Then I tried drawing the exact same attributed string to a UIView drawRect
method using CTFrameDraw
, and it obeyed all your formatting. I can only assume that CATextLayer ignores the majority of its formatting. The CATextLayer Class Reference has a number of warnings about what it does in the interests of efficiency.
If you really need to draw to a CALayer
, not a UIView
, you may be able to create your own CALayer subclass or delegate and do the drawing there.
- (void)drawRect:(CGRect)rect
{
//
// Build attrStr as before.
//
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect bounds = [self bounds];
// Text ends up drawn inverted, so we have to reverse it.
CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);
CGContextTranslateCTM( ctx, bounds.origin.x, bounds.origin.y+bounds.size.height );
CGContextScaleCTM( ctx, 1, -1 );
// Build a rectangle for drawing in.
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, bounds);
// Create the frame and draw it into the graphics context
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attrStr);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
CFRelease(framesetter);
CFRelease(path);
// Finally do the drawing.
CTFrameDraw(frame, ctx);
CFRelease(frame);
}