UILabel иногда неправильно переносит текст (автоматическая разметка)

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

Метка имеет 0 строк и перенос слов. Насколько я понимаю, это должно привести к росту структуры uilabel, и иногда это происходит. (До автоматического макета я бы вычислял и обновлял кадр метки в коде).

Таким образом, в некоторых случаях он работает правильно, а в других - нет. Посмотрите, как работает большинство ячеек, но последняя ячейка кажется слишком большой. На самом деле это правильный размер. Заголовок "Справедливый дубовый тест на проверку смога" на самом деле заканчивается "Только". Так что мой расчет размера ячейки правильный, он должен быть таким. Однако ярлык не переносит текст по какой-либо причине. Его ширина кадра не распространяется вправо, так что это не проблема.

и так, что здесь происходит? Это на 100% согласованно, всегда в этой ячейке, а не в той, что над ней, что заставляет меня думать, что это связано с размером текста, и UILabel не перестраивает текст, как только это представление добавляется в ячейку (что делает его на самом деле меньше по ширине).

Какие-нибудь мысли?

Некоторая дополнительная информация

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

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (self.items.count == 0) {
        return 60;
    }
    static TCAnswerBuilderCell *cell = nil;
    static dispatch_once_t pred;

    dispatch_once(&pred,
                  ^{
                      // get a sample cellonce
                      cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL];
                  });
    [cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
    return [cell heightForCellWithTableWidth:self.tableView.frame.size.width];
}

Я настраиваю ячейку с моим объектом данных на лету, а затем вызываю метод, который у меня есть, который вычисляет высоту ячейки с заданной шириной таблицы (не всегда можно полагаться на то, что кадр ячейки изначально корректен).

Это, в свою очередь, вызывает метод высоты на мой взгляд, так как это действительно где метка живет:

- (CGFloat)heightForCellWithTableWidth:(CGFloat)tableWidth {
    // subtract 38 from the constraint above
    return [self.thirdPartyAnswerView heightForViewWithViewWidth:tableWidth - 38];
}

Этот метод определяет высоту, вычисляя правильную ширину метки, а затем выполняя расчет:

- (CGFloat)heightForViewWithViewWidth:(CGFloat)viewWidth {
    CGFloat widthForCalc = viewWidth - self.imageFrameLeadingSpaceConstraint.constant - self.thumbnailFrameWidthConstraint.constant - self.titleLabelLeadingSpaceConstraint.constant;
    CGSize size = [self.titleLabel.text sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(widthForCalc, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
    CGFloat returnHeight = self.frame.size.height - self.titleLabel.frame.size.height + size.height;
    CGFloat height = returnHeight < self.frame.size.height ? self.frame.size.height : returnHeight;
    return height;
}

Это работает на 100% правильно.

Ячейки создаются явно в cellForRowAtIndexPath и сразу настраиваются:

if (self.items.count > 0) {
    TCAnswerBuilderCell *cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL forIndexPath:indexPath];
    [cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
    return cell;
}

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

- (void) configureCellWithThirdPartyObject:(TCThirdPartyObject *)object {
    self.detailDisclosureImageView.hidden = NO;
    if (!self.thirdPartyAnswerView) {
        self.thirdPartyAnswerView = [TCThirdPartyAPIHelper thirdPartyAnswerViewForThirdPartyAPIServiceType:object.thirdPartyAPIType];
        self.thirdPartyAnswerView.translatesAutoresizingMaskIntoConstraints = NO;
        [self.contentView addSubview:self.thirdPartyAnswerView];
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_thirdPartyAnswerView]-38-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(_thirdPartyAnswerView)]];
    }
    [self.thirdPartyAnswerView configureViewForThirdPartyObject:object forViewStyle:TCThirdPartyAnswerViewStyleSearchCell];
}

Наконец, моя конфигурация вида выглядит так:

- (void) configureViewForThirdPartyObject:(TCTPOPlace *)object forViewStyle:(TCThirdPartyAnswerViewStyle) style {
    self.titleLabel.text = object.name;
    self.addressLabel.text = [NSString stringWithFormat:@"%@, %@, %@", object.address, object.city, object.state];
    self.ratingsLabel.text = [NSString stringWithFormat:@"%d Ratings", object.reviewCount];
    NSString *ratingImageName = [NSString stringWithFormat:@"yelp_star_rating_%.1f.png", object.rating];
    UIImage *ratingsImage = [UIImage imageNamed:ratingImageName];
    if (ratingsImage) {
        self.ratingImageView.image = ratingsImage;
    }
    if (object.imageUrl) {
        [self.thumbnailImageView setImageWithURL:[NSURL URLWithString:object.imageUrl] completed:nil];
    }
}

Своего рода решение, но я не понимаю, почему

  1. Мое подпредставление было спроектировано с шириной 320, но не имеет собственных ограничений по ширине
  2. Подвид был добавлен в ячейку, но с горизонтальными ограничениями, которые выглядят так:
    • @"|[_thirdPartyAnswerView]-38-|"
  3. Представление было настроено сразу после добавления в ячейку, что означает, что текст для titleLabel был установлен прямо тогда.
  4. По какой-то причине текст был выложен так, как будто в представлении были полные 320 вместо 282.
  5. Метка никогда не обновлялась, даже если фрейм подпредставления был обновлен до 282, и на метке были ограничения, которые позволили бы сохранить его правильный размер.
  6. Изменение размера представления в xib на 282 решило проблему, потому что метка имеет правильный размер для начала.

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

РЕШИТЬ

Смотрите ответ Мэтта ниже: /questions/898991/uilabel-inogda-nepravilno-perenosit-tekst-avtomaticheskaya-razmetka/899002#899002

Если вы не читаете комментарий, основной проблемой было то, что я неосознанно настраивал preferredMaxLayoutWidth через IB при проектировании вида на большую ширину, чем это было бы показано (в некоторых случаях). preferredMaxLayoutWidth это то, что используется для определения места переноса текста. Так что, хотя мой взгляд и titleLabel правильно изменили размер, preferredMaxLayoutWidth был все еще в старом значении, и вызывая обертку в неожиданных точках. Вместо этого установите titleLabel в автоматический размер (⌘= в IB) и обновите preferredMaxLayoutWidth динамически в layoutSubviews до звонка супер был ключ. Спасибо, Мэтт!

3 ответа

Решение

Я тот, кто написал приложение, которое использует автоматическое расположение пяти меток в ячейке таблицы, ячейки которой имеют разную высоту, где метки изменяют размеры в соответствии с тем, что в них находится, и это работает. Поэтому я собираюсь предположить, что причина, по которой у вас возникли проблемы, может заключаться в том, что ваши ограничения недостаточно определяют компоновку, то есть, что вы получили неоднозначную компоновку для элементов ячейки. Я не могу проверить эту гипотезу, потому что не вижу твоих ограничений. Но вы можете легко проверить (я думаю) с помощью po [[UIWindow keyWindow] _autolayoutTrace] когда пауза в отладчике.

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

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

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = self.bounds.size.width;
}

Я не понимаю, почему это не поведение по умолчанию UILabel, или, по крайней мере, почему вы не можете просто включить это поведение с помощью флага.

Я немного обеспокоен тем, что предпочитаемое значение MaxLayoutWidth устанавливается в середине процесса разметки, но я не вижу простого способа обойти это.

Также убедитесь, что вы передаете целые числа в ограничения макета в коде.

Для меня случилось так, что после некоторых вычислений (например, convertPoint:toView:) я передал что-то вроде 23.99999997, и в итоге это привело к появлению двухстрочной метки, отображаемой в виде одной строки (хотя ее кадр, казалось, рассчитывался правильно), В моем случае CGRectIntegral добился цели!

Ошибки округления могут убить тебя:)

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