Базовый текст / NSStringDrawing - CFAttributedStringRef / NSAttributedString - изменение размера шрифта

Случай: приписанная строка уже создана. Как изменить размер строки?

Я думаю, мы могли бы либо

A) Обновление pointSize для всех шрифтов в приписанной строке

Б) Нарисуйте приписанную строку с некоторым преобразованием

3 ответа

Решение

Это отлично работает как на Mac OS, так и на 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

Удачного кодирования

У меня это работает со следующим кодом. Одна ошибка, хотя, если некоторый текст в приписанной строке не был установлен атрибут шрифта, он не будет обновлен. Поэтому я должен был все заключить в атрибуты шрифта.

- (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); 

 }

Я считаю, что разбор это единственный способ. Приписанная строка может иметь довольно сложный формат, ваша задача - увеличить размер шрифта.

Однако, если вам нужен этот трюк для рендеринга, вы можете избежать разбора строк - используйте масштабное преобразование для увеличения размера текста.

Другие вопросы по тегам