Diseño automático: obtenga altura UIImageView para calcular la altura de la celda correctamente

Hasta ahora solo he usado tags de texto dentro de las celdas que deberían auto-dimensionarse. Normalmente pongo restricciones a todos los bordes (vista de contenido o vecinos) y agrego las siguientes líneas a mi viewDidLoad() :

 // 190.0 is the actual height of my custom cell (see next image) in the storyboard containing a label and a randomly sized UIImageView tableView.estimatedRowHeight = 190.0 tableView.rowHeight = UITableViewAutomaticDimension 

Ahora, cuando trato de incluir imágenes, tengo varios problemas. Durante algunas investigaciones, encontré varios enfoques escritos en Objective-C pero, aunque no estoy seguro de cuáles de ellos realmente abordaron mi problema central, todavía estoy luchando con el complejo código Objective-C. Entonces, para resumir: mi objective final es obtener una tabla con celdas del tamaño correcto (alturas correctas), cada una con un título y una imagen que se ajuste al ancho de la pantalla. Esas imágenes pueden tener diferentes proporciones de aspecto y diferentes tamaños. Para simplificar algunos de los desafíos, preparé un nuevo proyecto más simple:

  1. Primero, creé un UITableViewController y una clase CustomCell con dos title: String IBOutlets title: String y postedImage: UIImage (UIImage tamaño aleatorio en el guión gráfico) para configurar las celdas. Definí restricciones a los bordes y entre sí.

  1. Cuando se construye, se configuran dos celdas que contienen la misma imagen pero se extraen de dos archivos individuales con diferentes tamaños y resoluciones (900 x 171 frente a 450 x 86 de tamaño medio). El modo de vista de UIImage se establece en “Ajuste de aspecto”, pero como no pude hacerlo funcionar como quiero hasta ahora, no estoy seguro de que este sea el ajuste correcto. Mientras que yo esperaba que la altura de la celda se calculara en base al UIImage reescalado incluidas las restricciones que había establecido, parece basarse en la altura inicial de los archivos de imagen (por lo tanto, 171 y 86). En realidad, no se adapta a la altura real de la vista UIImage (alrededor de 70 supongo).

Ambas alturas de celda se basan en la altura del archivo de imagen inicial y no en la altura real del UIImage

En un proyecto mío algo más complejo, quiero que diferentes imágenes se ajusten automáticamente al ancho de la pantalla, sin importar si son verticales o horizontales, y en caso de que su ancho sea inferior al ancho de la pantalla, deberían ampliarse. Al igual que en el proyecto anterior, cada altura de celda debe ajustarse a su contenido individual según lo definido por las restricciones en el guión gráfico. De todos modos, comienza con el problema discutido anteriormente: ¿cómo puedo dejar que esas dos celdas tengan la misma altura, correcta, abrazando su contenido como se define en el guión gráfico?

¡Gracias un millón por cualquier ayuda!

Así que creo que el problema subyacente es una especie de problema de huevo y pollo.

Para “adaptar” la imagen a UIImageView el sistema necesita un tamaño para la vista y, sin embargo, nos gustaría que la altura de la vista se determine después de que el aspecto se ajuste a la imagen con un ancho conocido para la vista.

Una solución consiste en calcular la relación de aspecto de la imagen y establecerla como una restricción en el UIImageView cuando configuramos la imagen. De esta forma, el sistema de diseño automático tiene suficiente información para ajustar el tamaño de la vista (un ancho y una relación de aspecto).

Así que cambié tu clase de celda personalizada a:

 class CustomCell: UITableViewCell { @IBOutlet weak var imageTitle: UILabel! @IBOutlet weak var postedImageView: UIImageView! internal var aspectConstraint : NSLayoutConstraint? { didSet { if oldValue != nil { postedImageView.removeConstraint(oldValue!) } if aspectConstraint != nil { postedImageView.addConstraint(aspectConstraint!) } } } override func prepareForReuse() { super.prepareForReuse() aspectConstraint = nil } func setPostedImage(image : UIImage) { let aspect = image.size.width / image.size.height aspectConstraint = NSLayoutConstraint(item: postedImageView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: postedImageView, attribute: NSLayoutAttribute.Height, multiplier: aspect, constant: 0.0) postedImageView.image = image } } 

Y luego en el delegado haz esto en lugar de configurar la imagen directamente:

 override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomCell cell.imageTitle.text = titles[indexPath.row] let image = images[titles[indexPath.row]]! cell.setPostedImage(image) return cell } 

Y obtendrás:

enter image description here

¡Espero que esto ayude!

Traduje la solución de Pollard a la versión objc algo así.
También recibí advertencias como “Incapaz de satisfacer restricciones simultáneamente”. Mi solución establece la prioridad de la restricción de espacio vertical como 999 en lugar de 1000.

 @interface PostImageViewCell() @property (nonatomic, strong) NSLayoutConstraint *aspectContraint; @end @implementation PostImageViewCell - (void) setAspectContraint:(NSLayoutConstraint *)aspectContraint { if (_aspectContraint != nil) { [self.postImage removeConstraint:_aspectContraint]; } if (aspectContraint != nil) { [self.postImage addConstraint:aspectContraint]; } } - (void) prepareForReuse { [super prepareForReuse]; self.aspectContraint = nil; } - (void)setPicture:(UIImage *)image { CGFloat aspect = image.size.width / image.size.height; self.aspectContraint = [NSLayoutConstraint constraintWithItem:self.postImage attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.postImage attribute:NSLayoutAttributeHeight multiplier:aspect constant:0.0]; [self.postImage setImage:image]; } @end 

Trabajando en Swift 3 + iOS 8

Usando AutoLayout y objetos UITableViewCell de tamaño variable –

Para la vista de imagen, establezca las restricciones de espacio superior, inferior, inicial y final en 0.

Código que se agregará a viewDidLoad ()

 tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = 200 

Defina heightForRowAtIndexPath de UITableViewDelegate ()

 extension ViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let image = UIImage(named: list_images[indexPath.row]) let aspect = (image?.size.width)! / (image?.size.height)! let cellHeight = (tableView.frame.width / aspect) + <> return cellHeight } } 

En lugar de tener el método setPostedImage personalizado, es más conveniente actualizar la restricción de aspecto en updateConstraints . De esta forma, puedes cambiar la imagen de UIImageView directamente sin método de ayuda adicional:

 static NSLayoutConstraint *constraintWithMultiplier(NSLayoutConstraint *constrain, CGFloat multiplier) { return [NSLayoutConstraint constraintWithItem:constrain.firstItem attribute:constrain.firstAttribute relatedBy:constrain.relation toItem:constrain.secondItem attribute:constrain.secondAttribute multiplier:multiplier constant:constrain.constant]; } -(void)viewDidLoad { UIView *contentView = self.contentView; _imageView = [[UIImageView alloc] init]; _imageView.translatesAutoresizingMaskIntoConstraints = NO; _imageView.contentMode = UIViewContentModeScaleAspectFit; [contentView addSubview:_imageView]; _imageAspectConstraint = [_imageView.heightAnchor constraintEqualToAnchor:_imageView.widthAnchor multiplier:1.0f]; NSArray  *constraints = @[ [_imageView.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:0], [_imageView.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:0], [_imageView.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:0], [_imageView.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor constant:0], ]; [NSLayoutConstraint activateConstraints:constraints]; } -(void)updateConstraints { const CGSize size = _imageView.image.size; const CGFloat multiplier = size.width / size.height; _imageAspectConstraint.active = NO; _imageAspectConstraint = constraintWithMultiplier(_imageAspectConstraint, multiplier); _imageAspectConstraint.active = YES; [super updateConstraints]; } 

Swift 4

Calcule la altura de la imagen usando la extension UIImage .

Puede usar la siguiente extension de UIImage para obtener la altura de acuerdo con AspectRatio de la imagen.

 extension UIImage { var cropRatio: CGFloat { let widthRatio = CGFloat(self.size.width / self.size.height) return widthRatio } } 

Por lo tanto, dentro de su override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat método override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat .

 override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let currentImage = dataSource[indexPath.row].image let imageCrop = currentImage.cropRatio var _height = tableView.frame.width / imageCrop return _height }