Измените размер CATextLayer, чтобы соответствовать тексту на iOS - PullRequest
21 голосов
/ 01 июня 2011

Все мои исследования до сих пор показывают, что это невозможно сделать точно. Вначале мне были доступны только два варианта:

a) Использование диспетчера раскладки для CATextLayer - недоступно в iOS с 4.0

b) Используйте sizeWithFont: constrainedToSize: lineBreakMode: и настройте кадр CATextLayer в соответствии с размером, возвращаемым здесь.

Вариант (б), будучи самым простым подходом, должен работать. В конце концов, это прекрасно работает с UILabels. Но когда я применил один и тот же расчет фрейма к CATextLayer, фрейм всегда оказывался немного больше ожидаемого или необходимого.

Как оказалось, межстрочный интервал в CATextLayers и UILabels (для одного и того же шрифта и размера) различен. В результате sizeWithFont (расчеты межстрочного интервала которого будут соответствовать расчетам UILabels) не возвращает ожидаемый размер для CATextLayers.

Это еще раз подтверждается печатью того же текста с использованием UILabel и CATextLayer и сравнением результатов. Текст в первой строке полностью перекрывается (это тот же шрифт), но межстрочный интервал в CATextLayer чуть короче, чем в UILabel. (Извините, я не могу загрузить скриншот прямо сейчас, так как те, что у меня уже есть, содержат конфиденциальные данные, и в настоящее время у меня нет времени, чтобы сделать пример проекта, чтобы получить чистые скриншоты. Я буду загружать их позже для потомков, когда У меня есть время)

Это странное различие, но я подумал, что можно было бы отрегулировать интервал в CATextLayer, указав соответствующий атрибут для NSAttributedString, которую я там использую, но, похоже, это не так. Изучая CFStringAttributes.h, я не могу найти ни одного атрибута, который мог бы быть связан с межстрочным интервалом.

Bottomline:

Так что кажется, что невозможно использовать CATextLayer на iOS в сценарии, где слой должен соответствовать своему тексту. Я прав в этом или я что-то упускаю?

P.S .:

  1. Причина, по которой я хотел использовать CATextLayer и NSAttributedString, заключается в том, что отображаемая строка должна иметь разные цвета в разных точках. Я думаю, мне просто нужно вернуться к рисованию строк вручную, как всегда ... конечно, всегда есть возможность взломать результаты из sizeWithFont, чтобы получить правильную высоту строки.

  2. Немного злоупотребив тегами 'code', чтобы сделать сообщение более читабельным.

  3. Я не могу пометить сообщение с помощью 'CATextLayer' - на удивление, таких тегов в настоящее время не существует. Если кто-то с достаточной репутацией натолкнется на этот пост, отметьте его соответствующим образом.

Ответы [ 3 ]

17 голосов
/ 19 декабря 2011

Попробуйте это:

- (CGFloat)boundingHeightForWidth:(CGFloat)inWidth withAttributedString:(NSAttributedString *)attributedString {
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( (CFMutableAttributedStringRef) attributedString); 
    CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(inWidth, CGFLOAT_MAX), NULL);
    CFRelease(framesetter);
    return suggestedSize.height;
}

Вам придется преобразовать NSString в NSAttributedString.В случае CATextLayer вы можете использовать следующий метод подкласса CATextLayer:

- (NSAttributedString *)attributedString {

    // If string is an attributed string
    if ([self.string isKindOfClass:[NSAttributedString class]]) {
        return self.string;
    }

    // Collect required parameters, and construct an attributed string
    NSString *string = self.string;
    CGColorRef color = self.foregroundColor;
    CTFontRef theFont = self.font;
    CTTextAlignment alignment;

    if ([self.alignmentMode isEqualToString:kCAAlignmentLeft]) {
        alignment = kCTLeftTextAlignment;

    } else if ([self.alignmentMode isEqualToString:kCAAlignmentRight]) {
        alignment = kCTRightTextAlignment;

    } else if ([self.alignmentMode isEqualToString:kCAAlignmentCenter]) {
        alignment = kCTCenterTextAlignment;

    } else if ([self.alignmentMode isEqualToString:kCAAlignmentJustified]) {
        alignment = kCTJustifiedTextAlignment;

    } else if ([self.alignmentMode isEqualToString:kCAAlignmentNatural]) {
        alignment = kCTNaturalTextAlignment;
    }

    // Process the information to get an attributed string
    CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);

    if (string != nil)
        CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef)string);

    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTForegroundColorAttributeName, color);
    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTFontAttributeName, theFont);

    CTParagraphStyleSetting settings[] = {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment};
    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTParagraphStyleAttributeName, paragraphStyle);    
    CFRelease(paragraphStyle);

    NSMutableAttributedString *ret = (NSMutableAttributedString *)attrString;

    return [ret autorelease];
}

HTH.

2 голосов
/ 29 января 2012

У меня есть намного более простое решение, которое может или не может работать для вас.

Если вы не делаете с CATextLayer ничего особенного, что вы не можете сделать UILabel, вместо этого создайте CALayer и добавьте слой UILabel к CALayer

UILabel*label = [[UILabel alloc]init];

//Do Stuff to label

CALayer *layer = [CALayer layer];

//Set Size/Position

[layer addSublayer:label.layer];

//Do more stuff to layer
0 голосов
/ 30 сентября 2011

Эта страница дала мне достаточно для создания простого горизонтально центрированного CATextLayer: http://lists.apple.com/archives/quartz-dev/2008/Aug/msg00016.html

- (void)drawInContext:(CGContextRef)ctx {
  CGFloat height, fontSize;

  height = self.bounds.size.height;
  fontSize = self.fontSize;

  CGContextSaveGState(ctx);
  CGContextTranslateCTM(ctx, 0.0, (fontSize-height)/2.0 * -1.0);
  [super drawInContext:ctx];
  CGContextRestoreGState(ctx);
}
...