Подтвердить что ты не робот

Линии отсутствуют в высоком UILabel при встраивании NSTextAttachment

Я могу создать многострочный NSAttributedString с помощью экранированных символов новой строки (@"\n"). С iOS 7 я могу теперь внедрить UIImage внутренние атрибутные строки (через NSTextAttachment).

Я заметил, что всякий раз, когда я устанавливаю attributedText of UILabel в строку с несколькими строками со встроенным изображением, количество отображаемых строк обратно пропорционально высоте метки. Например, когда высота метки равна 80, появляются две строки; когда высота около 100, появляется только вторая строка; когда высота около 130, ничего не появляется.

Эта проблема возникла при попытке расположить несколько UILabels рядом друг с другом внутри UITableViewCell и с ростом ярлыков (по вертикали) с высотой ячейки.

Может ли кто-нибудь объяснить, почему это происходит? Кто-нибудь знает обходные пути, которые не связаны с уменьшением размера UILabel?


Пример кода:

@implementation SOViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableAttributedString *text1 = [[NSMutableAttributedString alloc] init];
    [text1 appendAttributedString:[[NSAttributedString alloc] initWithString:@"Line 1\n"]];
    [text1 appendAttributedString:[[NSAttributedString alloc] initWithString:@"Line 2"]];

    UIImage *image = [UIImage imageNamed:@"17x10"]; //some PNG image (17px by 10px)

    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    attachment.image = image;
    attachment.bounds = CGRectMake(0, 0, image.size.width, image.size.height);

    NSMutableAttributedString *text2 = [[NSMutableAttributedString alloc] init];
    [text2 appendAttributedString:[[NSAttributedString alloc] initWithString:@"Line 1\n"]];
    [text2 appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
    [text2 appendAttributedString:[[NSAttributedString alloc] initWithString:@"Line 2"]];

    CGFloat margin = 20;

    //shows both lines when height == 80
    //shows line 2 when 90 <= height <= 120
    //shows nothing when height == 130
    CGFloat height = ???;
    CGFloat width = 200;

    UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(margin, margin, width, height)];
    UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectMake(margin, margin + height, width, height)];
    [self.view addSubview:label1];
    [self.view addSubview:label2];
    label1.backgroundColor = [UIColor orangeColor];
    label2.backgroundColor = [UIColor blueColor];
    label2.textColor = [UIColor whiteColor];
    label1.numberOfLines = 0;
    label2.numberOfLines = 0;
    label1.attributedText = text1;
    label2.attributedText = text2;

    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    imageView.frame = CGRectMake(margin + width, margin + height, image.size.width, image.size.height);
    [self.view addSubview:imageView];
}

@end

... Поместите это в контроллер представления по умолчанию для приложения "Single View". (Вы можете выбрать свое собственное изображение.)

4b9b3361

Ответ 1

Это не имеет ничего общего с NSTextAttachment. Это то, что в iOS 7, выпущенном до сих пор, UILabel не очень хорошо рисует атрибутивные строки. Простая атрибутная строка с некоторым подчеркиванием и центрированным стилем абзаца будет отображаться пустым или частично пустым в UILabel; эта же атрибутная строка отлично рисует в UITextView.

Итак, на данный момент существует одно решение: вместо этого используйте UITextView. Это на самом деле довольно хорошее решение, потому что в iOS 7 UITextView - это всего лишь обертка вокруг стека Text Kit. Таким образом, он нарисовал присланную строку простым способом. Это не мешает отношениям под капотом с Web Kit, которые были в предыдущих версиях iOS.

С другой стороны, я также нашел обходное решение для этой ошибки UILabel; вам нужно возиться с количеством строк метки и строки таким образом, чтобы замять текст до верхней части метки: см. мой ответ здесь - fooobar.com/questions/194697/...

Или вы можете просто ждать, пока Apple исправит ошибку и не скрежет пальцев. EDIT: в iOS 7.1, кажется, что ошибка будет исправлена ​​и не потребуется обходной путь.

Ответ 2

Кажется, это ошибка в UILabel. Тот же код отлично работает, когда UITextView используется вместо UILabel (UITextView размер шрифта по умолчанию отличается, поэтому я тестировал его на разных высотах).

Ответ 3

Вы пытались получить фактическую высоту текста, используя метод boundingRectWithSize:

NSAttributedString *text;
CGFloat width = 200;
CGRect rect = [text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil];
CGFloat height = rect.size.height;

Ответ 4

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

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

[self.lab removeConstraint:self.labelHeight];
[self.lab addConstraint:
 [NSLayoutConstraint constraintWithItem:self.lab 
 attribute:NSLayoutAttributeHeight 
 relatedBy:NSLayoutRelationGreaterThanOrEqual 
 toItem:nil attribute:0 multiplier:1 constant:20]];

Эта метка правильно отображает каждую присланную строку, которую я бросаю на нее! Конечно, вы теряете автоматическое вертикальное центрирование строки, но весь источник ошибки, поэтому потерять ее не так страшно.