UILabel no envuelve el texto correctamente a veces (diseño automático)


Tengo una configuración de uilabel en una vista. No tiene una restricción de ancho, pero su ancho está determinado por una restricción inicial a la imagen en miniatura y una restricción final al borde de la vista.

introduzca la descripción de la imagen aquí

La etiqueta está configurada para tener 0 líneas, y para ajustar la línea. Mi entendimiento es que esto debería hacer que el marco del uilabel crezca, y de hecho lo hace a veces. (Anterior al diseño automático, calculaba y actualizaba el marco de la etiqueta en codificar).

Así que el resultado es que funciona correctamente en algunos casos y no en otros. Vea que la mayoría de las celdas funcionan correctamente allí, pero la última celda parece ser demasiado grande. De hecho es del tamaño correcto. El título "Fair Oaks Smog Check Test" en realidad termina con "Only". Así que mi cálculo para el tamaño de la celda es correcto, debería ser ese tamaño. Sin embargo, la etiqueta no envuelve el texto por cualquier razón. Su ancho de marco no se extiende a la derecha, por lo que no es el cuestión.

introduzca la descripción de la imagen aquí

Entonces, ¿qué está pasando aquí? Es 100% consistente, siempre en esa celda y no en las que están arriba, lo que me hace pensar que está relacionado con el tamaño del texto, y UILabel no está rediseñando el texto una vez que esta vista se agrega a la celda (lo que lo hace en realidad más pequeño en cuanto a ancho).

¿Algún pensamiento?

Alguna información adicional

La altura de las celdas se calcula a partir de una celda de muestra que creo y almaceno en una estática variable:

- (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];
}

Configuro la celda con mi objeto de datos sobre la marcha, y luego llamo a un método que tengo en él que calcula la altura de la celda con un ancho de tabla dado (no siempre puedo confiar en que el marco de celda sea correcto inicialmente).

Esto a su vez llama a un método de altura en mi opinión, ya que es realmente donde vive la etiqueta:

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

Este método determina la altura calculando el ancho correcto de la etiqueta, y luego haciendo un cálculo:

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

Esto funciona 100% correctamente.

Las celdas se crean obviamente en cellForRowAtIndexPath y se configuran inmediatamente:

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

En la configuración de la celda, mi vista se carga desde un plumín (se reutiliza en otro lugar, por lo que no está directamente en la celda). La celda lo agrega de la siguiente manera:

- (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];
}

Finalmente mi configuración de vista se ve así:

- (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];
    }
}

Una especie de solución, pero no entiendo por qué

  1. Mi subview fue diseñado a 320 de ancho, pero no tiene restricciones propias para el ancho
  2. El subview se agregó a la celda, pero con restricciones horizontales que se ven así:
    • @"|[_thirdPartyAnswerView]-38-|"
  3. La vista se configuró inmediatamente después de ser agregada a la celda, lo que significa que el texto para la etiqueta de título se estableció en ese momento.
  4. Por cualquier razón, el texto fue presentado como si la vista tuviera el 320 completo en lugar de 282.
  5. El la etiqueta nunca se actualizó, a pesar de que el marco del subview se actualizó a 282, y había restricciones en la etiqueta que la mantendrían dimensionada correctamente.
  6. Cambiar el tamaño de la vista en el xib a 282 solucionó el problema, porque la etiqueta tiene el tamaño correcto para empezar.

Todavía no entiendo por qué la etiqueta no se vuelve a diseñar después de que se actualice el tamaño de la vista principal cuando tiene tanto el inicial como el final limitación.

RESUELTO

Ver la respuesta de Matt abajo: https://stackoverflow.com/a/15514707/287403

En caso de que no lea el comentario, el problema principal fue que, sin saberlo, estaba configurando preferredMaxLayoutWidth a través de IB al diseñar una vista con un ancho mayor del que se mostraría (en algunos casos). preferredMaxLayoutWidth es lo que se usa para determinar dónde se ajusta el texto. Así que a pesar de que mi vista y titleLabel redimensionados correctamente, el preferredMaxLayoutWidth todavía estaba en el valor antiguo, y causando envolver en puntos inesperados. Establecer el titleLabel en su lugar a su tamaño automático (IB = en IB), y actualizar el preferredMaxLayoutWidth dinámicamente en layoutSubviews antes de llamar a super fue la clave. Gracias Matt!

Author: Community, 2013-03-20

3 answers

Soy alguien que ha escrito una aplicación que utiliza el diseño automático de cinco etiquetas en una celda en una tabla cuyas celdas tienen diferentes alturas, donde las etiquetas se redimensionan de acuerdo con lo que hay en ellas, y funciona. Voy a sugerir, por lo tanto, que la razón por la que está teniendo problemas podría ser que sus restricciones están infradeterminando el diseño, es decir, que tiene un diseño ambiguo para los elementos de la celda. No puedo probar esa hipótesis porque no puedo ver tus restricciones. Pero puede comprobarlo fácilmente (creo) usando po [[UIWindow keyWindow] _autolayoutTrace] cuando está en pausa en el depurador.

También tengo otra sugerencia (lamento solo tirarte cosas): asegúrate de configurar la etiqueta preferredMaxLayoutWidth. Esto es crucial porque es el ancho en el que la etiqueta dejará de crecer horizontalmente y comenzará a crecer verticalmente.

 42
Author: matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-20 03:22:08

Tuve el mismo problema y lo resolví usando una sugerencia de esta respuesta. En una subclase de UILabel puse este código:

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

No entiendo por qué este no es el comportamiento predeterminado de UILabel, o al menos por qué no se puede habilitar este comportamiento a través de una bandera.

Estoy un poco preocupado de que preferredMaxLayoutWidth se establezca en medio del proceso de diseño, pero no veo una manera fácil de evitarlo.

 13
Author: phatmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:35

Además, compruebe que está pasando números integrales a sus restricciones de diseño en código.

Para mí sucedió que después de algunos cálculos (por ejemplo, convertPoint:toView:), estaba pasando algo como 23.99999997, y finalmente esto llevó a una etiqueta de 2 líneas que se muestra como una línea única (aunque su marco parecía calcularse correctamente). En mi caso CGRectIntegral hizo el truco!

Los errores de redondeo podrían matarte:)

 0
Author: Sergiu Todirascu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-20 13:58:58