Auto-Layout: Obtener UIImageView height para calcular correctamente la altura de la celda


Hasta ahora solo he estado usando etiquetas de texto dentro de las celdas que deben 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, al tratar de incluir con imágenes, me enfrento a varios problemas. Durante algunas investigaciones encontré varios enfoques escritos en Objective-C, pero mientras que no estoy seguro de cuál de ellos realmente abordó mi problema central, todavía estoy luchando con el complejo código Objective-C. Tan para reanudar: Mi objetivo final es obtener una tabla con celdas de 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 relaciones de aspecto y diferentes tamaños. Para simplificar algunos de los desafíos, preparé un nuevo proyecto más simple:

  1. Primero, creé una clase UITableViewController y una CustomCell con dos IBOutlets title: String y postedImage: UIImage (UIImage de tamaño aleatorio en el guion gráfico) para configurar las celdas. I defined constraints to the bordes y uno a otro.

  1. Cuando se construyen, se configuran dos celdas que contienen la misma imagen, pero se obtienen de dos archivos individuales con diferentes tamaños y resoluciones (900 x 171 vs.450 x 86 de tamaño medio). El modo de vista UIImage está configurado en" Ajuste de aspecto", pero como no pude ejecutarlo de la manera que quiero hasta ahora, no estoy seguro de que este sea el ajuste correcto. Mientras que yo había esperado que la altura de la celda se calculara en función de la imagen UIImage restricciones que había establecido, parece estar basado en la altura inicial de los archivos de imagen (por lo 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 inicial del archivo de imagen y no en la altura real de 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 menor que el ancho de la pantalla, deben ampliarse. Como en el proyecto de arriba la altura de la celda debe ajustarse a su contenido individual según lo definido por las restricciones en el guion gráfico. De todos modos, comienza con el problema discutido anteriormente: ¿Cómo puedo permitir que esas dos celdas tengan la misma altura correcta, abrazando su contenido como se define en el guion gráfico?

Un millón de gracias por cualquier ayuda!

Author: Alexei S., 2014-09-25

5 answers

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

Para 'ajustar el aspecto' de la imagen al UIImageView el sistema necesita un tamaño para la vista, y sin embargo nos gustaría que la altura de la vista se determinara después de ajustar el aspecto de la imagen dada una anchura conocida para la vista.

Una solución es calcular la relación de aspecto de la imagen nosotros mismos y establecerla como una restricción en el UIImageView cuando establecemos la imagen. De esa manera el sistema de diseño automático tiene suficiente información para dimensionar 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 hacer 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:

introduzca la descripción de la imagen aquí

Espero que esto ayude!

 67
Author: Mike Pollard,
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
2014-09-26 10:00:03

Traduje la solución de Pollard a la versión objc algo así.
También recibí advertencias como "Incapaz de satisfacer simultáneamente las restricciones". Mi solución es establecer la prioridad de 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
 6
Author: 허광호,
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
2015-07-16 20:28:19

Trabajando en Swift 3 + iOS 8

Usando objetos de diseño automático y tamaño variable UITableViewCell -

Para imageview, establezca las restricciones de espacio superior, inferior, inicial y final en 0.

Código a añadir a viewDidLoad ()

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 200

Define la altura de UITableViewDelegate pararowatindexpath ()

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) + <<Height of image title label>>
        return cellHeight
    }
}
 1
Author: c_sharma,
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
2016-11-29 20:15:29

En lugar de tener un método personalizado setPostedImage, es más conveniente actualizar la restricción de aspecto en updateConstraints. De esta manera, puede cambiar la imagen de UIImageView directamente sin el método auxiliar 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 <NSLayoutConstraint *> *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];
}
 0
Author: wonder.mice,
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
2016-10-06 05:33:43

Swift 4

Calcular la altura de la imagen usando UIImage extension.

Puede usar el siguiente extension de UIImage para obtener la Altura de acuerdo con la 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 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
}
 0
Author: Rashesh Bosamiya,
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
2018-08-01 13:42:45