简体   繁体   中英

Core Text / NSStringDrawing - CFAttributedStringRef / NSAttributedString - changing font size

Case: The attributed string is already created. How can the size of the string be altered?

I'm guessing we could either

A) Update pointSize for all fonts in the attributed string

B) Draw the attributed string with some transform

This works fine on both Mac OS and iOS

@implementation NSAttributedString (Scale)

- (NSAttributedString *)attributedStringWithScale:(double)scale
{
    if(scale == 1.0)
    {
        return self;
    }

    NSMutableAttributedString *copy = [self mutableCopy];
    [copy beginEditing];

    NSRange fullRange = NSMakeRange(0, copy.length);

    [self enumerateAttribute:NSFontAttributeName inRange:fullRange options:0 usingBlock:^(UIFont *oldFont, NSRange range, BOOL *stop) {
        double currentFontSize = oldFont.pointSize;
        double newFontSize = currentFontSize * scale;

        // don't trust -[UIFont fontWithSize:]
        UIFont *scaledFont = [UIFont fontWithName:oldFont.fontName size:newFontSize];

        [copy removeAttribute:NSFontAttributeName range:range];
        [copy addAttribute:NSFontAttributeName value:scaledFont range:range];
    }];

    [self enumerateAttribute:NSParagraphStyleAttributeName inRange:fullRange options:0 usingBlock:^(NSParagraphStyle *oldParagraphStyle, NSRange range, BOOL *stop) {

        NSMutableParagraphStyle *newParagraphStyle = [oldParagraphStyle mutableCopy];
        newParagraphStyle.lineSpacing *= scale;
        newParagraphStyle.paragraphSpacing *= scale;
        newParagraphStyle.firstLineHeadIndent *= scale;
        newParagraphStyle.headIndent *= scale;
        newParagraphStyle.tailIndent *= scale;
        newParagraphStyle.minimumLineHeight *= scale;
        newParagraphStyle.maximumLineHeight *= scale;
        newParagraphStyle.paragraphSpacing *= scale;
        newParagraphStyle.paragraphSpacingBefore *= scale;

        [copy removeAttribute:NSParagraphStyleAttributeName range:range];
        [copy addAttribute:NSParagraphStyleAttributeName value:newParagraphStyle range:range];
    }];

    [copy endEditing];
    return copy;
}

@end

Happy coding

I've got it working with the following code. One miss though is if some text in the attributedstring has not been set a font-attribute it will not be updated. So i had to encapsulate everything with font-attributes.

- (void)recalculateSizeChangeInAttributedString {

    if(self.attributedStringOriginal == nil) {
        self.attributedStringOriginal = [self.attributedString copy];
    }

    CFMutableAttributedStringRef tempString = CFAttributedStringCreateMutableCopy(CFAllocatorGetDefault(), self.attributedStringOriginal.length, (CFMutableAttributedStringRef)self.attributedStringOriginal);

    int lastIndex = 0;
    int limit = CFAttributedStringGetLength(tempString);
    for (int index = 0; index < limit;) {

        CFRange inRange = CFRangeMake(0, limit - index);
        CFRange longestEffective;
        CTFontRef font = (CTFontRef)CFAttributedStringGetAttribute(tempString, index, kCTFontAttributeName, &longestEffective);

        if(font != nil) {

            // log for testing
            NSLog(@"index: %i, range: %i - %i, longest: %i - %i, attribute: %@", 
                  index, inRange.location, 
                  inRange.location + inRange.length, 
                  longestEffective.location, longestEffective.location + longestEffective.length, 
                  @"..." 
                  );


            // alter the font and set the altered font/attribute
            int rangeEnd = longestEffective.length != 0 ? longestEffective.length : 1;
            CTFontRef modifiedFont = CTFontCreateCopyWithAttributes(font, CTFontGetSize((CTFontRef)font) * sizeFactor, NULL, NULL); 
            CFAttributedStringSetAttribute(tempString, CFRangeMake(index, rangeEnd), kCTFontAttributeName, modifiedFont); 
            CFRelease(modifiedFont);

        }

        // make next loop continue where current attribute ended
        index += longestEffective.length; 

        if(index == lastIndex)
            index ++;
        lastIndex = index;

    }

    self.attributedString = (NSMutableAttributedString *)tempString;

    CFRelease(tempString); 

 }

I believe parsing is the only way. Attributed string can have quite complex format, it's your job to increase the font size.

However, if you need this trick for rendering, you can avoid string parsing - use a scale transform to increase the text size.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM