Измените размер CATextLayer, чтобы он соответствовал тексту на iOS

#ios4 #sizewithfont #catextlayer

#ios4 #размер с помощью шрифта #catextlayer

Вопрос:

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

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

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

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

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

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

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

Итог:

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

P.S:

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

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

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

Комментарии:

1. Я только что столкнулся с этой проблемой … поспешите с ответом.

2. @All, в случае, если это неясно, вопрос относится строго к изменению размера CATextLayer по вертикали фиксированной ширины для строк разной длины / атрибутов.

Ответ №1:

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

 - (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), amp;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.

Комментарии:

1. Извините, прошло некоторое время, и в настоящее время у меня нет кода или времени для тестирования этого. Я обязательно проверю, когда смогу. Тем временем, если кто-то еще, столкнувшийся с этим ответом, сможет проверить, что он работает, я пометлю его как «отвеченный».

2. Через 6 месяцев я, наконец, снова столкнулся с этой проблемой. 🙂 И решение сработало. Большое спасибо, Мустафа!!

Ответ №2:

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

Если вы не делаете ничего особенного с 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
  

Комментарии:

1. Ах, видишь. Как отмечалось в нижней части моего вопроса, мне специально приходится использовать CATextLayers из-за их необычных возможностей форматирования. В противном случае UILabel прекрасно справился бы с этой задачей. В любом случае, прошло так много времени с тех пор, как был опубликован этот вопрос, о единственной зацепке, которая пока что давала какие-то надежды, — это @Mustafa. Трагично, что я так долго не мог это сделать. Кто-нибудь собирается это сделать?

Ответ №3:

С LabelKit вам больше не нужно CATextLayer . Больше никаких неправильных межстрочных интервалов и более широких символов, все отображается так же, как и UILabel, с сохранением анимации.

Ответ №4:

Этой страницы мне хватило для создания простого 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);
}
  

Комментарии:

1. Извините, но я не думаю, что это отвечает на вопрос. Вопрос заключается в изменении размера CATextLayer по вертикали, чтобы обернуть / подогнать любую строку, в то время как ответ заключается в выравнивании по вертикали — это тоже одна строка текста.