¿Cómo hago para que ImageView de UITableViewCell tenga un tamaño fijo incluso cuando la imagen es más pequeña?


104

Tengo un montón de imágenes que estoy usando para las vistas de imágenes de la celda, todas no son más grandes que 50x50. por ejemplo, 40x50, 50x32, 20x37 .....

Cuando cargo la vista de tabla, el texto no se alinea porque el ancho de las imágenes varía. También me gustaría que aparecieran imágenes pequeñas en el centro en lugar de en la izquierda.

Aquí está el código que estoy probando dentro de mi método 'cellForRowAtIndexPath'

cell.imageView.autoresizingMask = ( UIViewAutoresizingNone );
cell.imageView.autoresizesSubviews = NO;
cell.imageView.contentMode = UIViewContentModeCenter;
cell.imageView.bounds = CGRectMake(0, 0, 50, 50);
cell.imageView.frame = CGRectMake(0, 0, 50, 50);
cell.imageView.image = [UIImage imageWithData: imageData];

Como puede ver, he probado algunas cosas, pero ninguna funciona.

Respuestas:


152

No es necesario reescribir todo. Recomiendo hacer esto en su lugar:

Publique esto dentro de su archivo .m de su celda personalizada.

- (void)layoutSubviews {
    [super layoutSubviews];
    self.imageView.frame = CGRectMake(0,0,32,32);
}

Esto debería funcionar bien. :]


28
si configura, self.imageView.boundsla imagen estará centrada.
BLeB

45
¿y si no agregamos una subclase de UITableViewCell?
nopolaridad

3
@ 動靜 能量: Subclasificar UITableViewCell es el truco principal para que esto funcione.
auco

5
Esto no funciona para mi. La imagen todavía envuelve todo el imageView.
Joslinm

14
Tampoco me funciona porque las etiquetas están desalineadas.
nverinaud

139

Para aquellos de ustedes que no tienen una subclase de UITableViewCell:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 [...]

      CGSize itemSize = CGSizeMake(40, 40);
      UIGraphicsBeginImageContextWithOptions(itemSize, NO, UIScreen.mainScreen.scale);
      CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
      [cell.imageView.image drawInRect:imageRect];
      cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();

 [...]
     return cell;
}

El código anterior establece el tamaño en 40x40.

Swift 2

    let itemSize = CGSizeMake(25, 25);
    UIGraphicsBeginImageContextWithOptions(itemSize, false, UIScreen.mainScreen().scale);
    let imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
    cell.imageView?.image!.drawInRect(imageRect)
    cell.imageView?.image! = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

O puede usar otro enfoque (no probado) sugerido por @Tommy:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 [...]

      CGSize itemSize = CGSizeMake(40, 40);
      UIGraphicsBeginImageContextWithOptions(itemSize, NO, 0.0)          
 [...]
     return cell;
}

Swift 3+

let itemSize = CGSize.init(width: 25, height: 25)
UIGraphicsBeginImageContextWithOptions(itemSize, false, UIScreen.main.scale);
let imageRect = CGRect.init(origin: CGPoint.zero, size: itemSize)
cell?.imageView?.image!.draw(in: imageRect)
cell?.imageView?.image! = UIGraphicsGetImageFromCurrentImageContext()!;
UIGraphicsEndImageContext();

El código anterior es la versión Swift 3+ del anterior.


3
La distorsión de la imagen se puede corregir mediante UIGraphicsBeginImageContextWithOptions (itemSize, NO, UIScreen.mainScreen.scale); en lugar de UIGraphicsBeginImageContext (itemSize);
Kiran Ruth R

1
Buena respuesta. Por cierto, no tuve la opción, UIScreen.mainScreen.scaleasí que simplemente me fui UIGraphicsBeginImageContext. También cambió el tamaño de imageView en la celda básica.
Denikov

3
@GermanAttanasioRuiz al seleccionar la celda, vuelve a cambiar el tamaño al original, se supone que es así, cómo resolver eso.
Bonnie

6
para todos aquellos que se confundieron como yo, deben configurar la imagen antes del inicio del contexto. es decir, cell.imageView.image = [UIImage imageNamed: @ "my_image.png"];
Guy Lowe

5
Tal costosas operaciones no deben ser una parte de cellForRowAtIndexPath
Krizai

33

Así es como lo hice. Esta técnica se encarga de mover el texto y las etiquetas de texto de detalle de forma adecuada a la izquierda:

@interface SizableImageCell : UITableViewCell {}
@end
@implementation SizableImageCell
- (void)layoutSubviews {
    [super layoutSubviews];

    float desiredWidth = 80;
    float w=self.imageView.frame.size.width;
    if (w>desiredWidth) {
        float widthSub = w - desiredWidth;
        self.imageView.frame = CGRectMake(self.imageView.frame.origin.x,self.imageView.frame.origin.y,desiredWidth,self.imageView.frame.size.height);
        self.textLabel.frame = CGRectMake(self.textLabel.frame.origin.x-widthSub,self.textLabel.frame.origin.y,self.textLabel.frame.size.width+widthSub,self.textLabel.frame.size.height);
        self.detailTextLabel.frame = CGRectMake(self.detailTextLabel.frame.origin.x-widthSub,self.detailTextLabel.frame.origin.y,self.detailTextLabel.frame.size.width+widthSub,self.detailTextLabel.frame.size.height);
        self.imageView.contentMode = UIViewContentModeScaleAspectFit;
    }
}
@end

...

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[SizableImageCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }

    cell.textLabel.text = ...
    cell.detailTextLabel.text = ...
    cell.imageView.image = ...
    return cell;
}

Gracias, Chris. Esto funcionó perfectamente. Es posible que desee actualizarlo eliminando el lanzamiento automático porque ARC lo prohíbe ahora. ¡Sin embargo, gran respuesta!
CSawy

1
Esta sigue siendo la mejor solución en la actualidad. Gracias.
Rémi Belzanti

En estos días, probablemente recomendaría hacer una celda personalizada con un xib o una celda prototipo en un guión gráfico, y crear una vista de imagen completamente diferente que no esté relacionada con la vista de imagen de una celda estándar. ¡Pero esto todavía es bastante simple, supongo!
Chris

1
Quiero hacer todo con código en lugar de usar un xib o un guión gráfico y esto funcionó perfectamente.
John81

Esta respuesta no hace nada si w <deseadoCon, que me parece que es el caso de uso de interés (al menos, en la pregunta).
Nate

21

vista de imagen agregar como una vista secundaria a la celda de vista de tabla

UIImageView *imgView=[[UIImageView alloc] initWithFrame:CGRectMake(20, 5, 90, 70)];
imgView.backgroundColor=[UIColor clearColor];
[imgView.layer setCornerRadius:8.0f];
[imgView.layer setMasksToBounds:YES];
[imgView setImage:[UIImage imageWithData: imageData]];
[cell.contentView addSubview:imgView];

1
No olvide liberar imgView si no está utilizando ARC.
Charlie Monroe

14

No es necesario rehacer la celda completa. Puede usar las propiedades indentationLevel y indentationWidth de tableViewCells para cambiar el contenido de su celda. Luego agrega su imageView personalizado al lado izquierdo de la celda.


6

Mejor cree una vista de imagen y agréguela como una vista secundaria a la celda. Entonces puede obtener el tamaño de marco deseado.


Acabo de intentarlo, parece un buen comienzo, pero el texto en las celdas ahora se superpone a las imágenes, ¿cómo cambio la vista de contenido 50 píxeles a la derecha? cell.contentView.bounds = CGRectMake (50, 0, 270, 50); no tiene ningún efecto
Robert

1
En lugar de usar la vista predeterminada de celda, cree una etiqueta y agréguela como una subvista a la celda, luego asigne el texto a la propiedad de texto de la etiqueta. Con esto, puede diseñar la celda según sus necesidades.
Warrior

Esto será más útil si desea mostrar el título, la fecha, la descripción, etc., más valores en una celda.
Warrior

Bien, básicamente tendré que rehacer la celda mediante programación. No debería ser demasiado difícil. Gracias por la ayuda.
Robert

6

Simplemente rápido ,

Paso 1: cree una subclase del UITableViewCell
paso 2: agregue este método a la subclase de UITableViewCell

override func layoutSubviews() {
    super.layoutSubviews()
    self.imageView?.frame = CGRectMake(0, 0, 10, 10)
}

Paso 3: crea un objeto de celda usando esa subclase en cellForRowAtIndexPath,

Ex: let customCell:CustomCell = CustomCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")

Paso 4: disfruta


2
UIImage *image = cell.imageView.image;

UIGraphicsBeginImageContext(CGSizeMake(35,35));
// draw scaled image into thumbnail context

[image drawInRect:CGRectMake(5, 5, 35, 35)]; //
UIImage *newThumbnail = UIGraphicsGetImageFromCurrentImageContext();
// pop the context
UIGraphicsEndImageContext();
if(newThumbnail == nil)
{
    NSLog(@"could not scale image");
    cell.imageView.image = image;
}
else
{
    cell.imageView.image = newThumbnail;
}

2

Esto funcionó para mí en rápido:

Cree una subclase de UITableViewCell (asegúrese de vincular su celda en el guión gráfico)

class MyTableCell:UITableViewCell{
    override func layoutSubviews() {
        super.layoutSubviews()

        if(self.imageView?.image != nil){

            let cellFrame = self.frame
            let textLabelFrame = self.textLabel?.frame
            let detailTextLabelFrame = self.detailTextLabel?.frame
            let imageViewFrame = self.imageView?.frame

            self.imageView?.contentMode = .ScaleAspectFill
            self.imageView?.clipsToBounds = true
            self.imageView?.frame = CGRectMake((imageViewFrame?.origin.x)!,(imageViewFrame?.origin.y)! + 1,40,40)
            self.textLabel!.frame = CGRectMake(50 + (imageViewFrame?.origin.x)! , (textLabelFrame?.origin.y)!, cellFrame.width-(70 + (imageViewFrame?.origin.x)!), textLabelFrame!.height)
            self.detailTextLabel!.frame = CGRectMake(50 + (imageViewFrame?.origin.x)!, (detailTextLabelFrame?.origin.y)!, cellFrame.width-(70 + (imageViewFrame?.origin.x)!), detailTextLabelFrame!.height)
        }
    }
}

En cellForRowAtIndexPath, retire la celda como su nuevo tipo de celda:

    let cell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as! MyTableCell

Obviamente, cambie los valores numéricos para adaptarlos a su diseño


1

Creé una extensión usando la respuesta de @GermanAttanasio. Proporciona un método para cambiar el tamaño de una imagen a un tamaño deseado y otro método para hacer lo mismo mientras se agrega un margen transparente a la imagen (esto puede ser útil para las vistas de tabla en las que desea que la imagen también tenga un margen).

import UIKit

extension UIImage {

    /// Resizes an image to the specified size.
    ///
    /// - Parameters:
    ///     - size: the size we desire to resize the image to.
    ///
    /// - Returns: the resized image.
    ///
    func imageWithSize(size: CGSize) -> UIImage {

        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.mainScreen().scale);
        let rect = CGRectMake(0.0, 0.0, size.width, size.height);
        drawInRect(rect)

        let resultingImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        return resultingImage
    }

    /// Resizes an image to the specified size and adds an extra transparent margin at all sides of
    /// the image.
    ///
    /// - Parameters:
    ///     - size: the size we desire to resize the image to.
    ///     - extraMargin: the extra transparent margin to add to all sides of the image.
    ///
    /// - Returns: the resized image.  The extra margin is added to the input image size.  So that
    ///         the final image's size will be equal to:
    ///         `CGSize(width: size.width + extraMargin * 2, height: size.height + extraMargin * 2)`
    ///
    func imageWithSize(size: CGSize, extraMargin: CGFloat) -> UIImage {

        let imageSize = CGSize(width: size.width + extraMargin * 2, height: size.height + extraMargin * 2)

        UIGraphicsBeginImageContextWithOptions(imageSize, false, UIScreen.mainScreen().scale);
        let drawingRect = CGRect(x: extraMargin, y: extraMargin, width: size.width, height: size.height)
        drawInRect(drawingRect)

        let resultingImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        return resultingImage
    }
}

1

Aquí está el método de trabajo de @germanattanasio, escrito para Swift 3

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    ...
    cell.imageView?.image = myImage
    let itemSize = CGSize(width:42.0, height:42.0)
    UIGraphicsBeginImageContextWithOptions(itemSize, false, 0.0)
    let imageRect = CGRect(x:0.0, y:0.0, width:itemSize.width, height:itemSize.height)
    cell.imageView?.image!.draw(in:imageRect)
    cell.imageView?.image! = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
}

1

Si lo usa cell.imageView?.translatesAutoresizingMaskIntoConstraints = false, puede establecer restricciones en imageView. Aquí hay un ejemplo de trabajo que usé en un proyecto. Evité las subclases y no necesité crear un guión gráfico con celdas prototipo, pero me tomó bastante tiempo ponerme en funcionamiento, por lo que probablemente sea mejor usarlo solo si no hay una forma más simple o concisa disponible para usted.

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 80
}



    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: String(describing: ChangesRequiringApprovalTableViewController.self))

    let record = records[indexPath.row]

    cell.textLabel?.text = "Title text"

    if let thumb = record["thumbnail"] as? CKAsset, let image = UIImage(contentsOfFile: thumb.fileURL.path) {
        cell.imageView?.contentMode = .scaleAspectFill
        cell.imageView?.image = image
        cell.imageView?.translatesAutoresizingMaskIntoConstraints = false
        cell.imageView?.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor).isActive = true
        cell.imageView?.widthAnchor.constraint(equalToConstant: 80).rowHeight).isActive = true
        cell.imageView?.heightAnchor.constraint(equalToConstant: 80).isActive = true
        if let textLabel = cell.textLabel {
            let margins = cell.contentView.layoutMarginsGuide
            textLabel.translatesAutoresizingMaskIntoConstraints = false
            cell.imageView?.trailingAnchor.constraint(equalTo: textLabel.leadingAnchor, constant: -8).isActive = true
            textLabel.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
            textLabel.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
            let bottomConstraint = textLabel.bottomAnchor.constraint(equalTo: margins.bottomAnchor)
            bottomConstraint.priority = UILayoutPriorityDefaultHigh
            bottomConstraint.isActive = true
            if let description = cell.detailTextLabel {
                description.translatesAutoresizingMaskIntoConstraints = false
                description.bottomAnchor.constraint(equalTo: margins.bottomAnchor).isActive = true
                description.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
                cell.imageView?.trailingAnchor.constraint(equalTo: description.leadingAnchor, constant: -8).isActive = true
                textLabel.bottomAnchor.constraint(equalTo: description.topAnchor).isActive = true
            }
        }
        cell.imageView?.clipsToBounds = true
    }

    cell.detailTextLabel?.text = "Detail Text"

    return cell
}

0

El UITableViewCell normal funciona bien para colocar las cosas, pero el cell.imageView no parece comportarse como usted quiere. Descubrí que es lo suficientemente simple como para que UITableViewCell se distribuya correctamente dando primero a cell.imageView una imagen de tamaño adecuado como

// Putting in a blank image to make sure text always pushed to the side.
UIGraphicsBeginImageContextWithOptions(CGSizeMake(kGroupImageDimension, kGroupImageDimension), NO, 0.0);
UIImage *blank = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
cell.imageView.image = blank;

Entonces puede conectar su propio UIImageView que funcione correctamente con

// The cell.imageView increases in size to accomodate the image given it.
// We don't want this behaviour so we just attached a view on top of cell.imageView.
// This gives us the positioning of the cell.imageView without the sizing
// behaviour.
UIImageView *anImageView = nil;
NSArray *subviews = [cell.imageView subviews];
if ([subviews count] == 0)
{
    anImageView = [[UIImageView alloc] init];
    anImageView.translatesAutoresizingMaskIntoConstraints = NO;
    [cell.imageView addSubview:anImageView];

    NSLayoutConstraint *aConstraint = [NSLayoutConstraint constraintWithItem:anImageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:cell.imageView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
    [cell.imageView addConstraint:aConstraint];

    aConstraint = [NSLayoutConstraint constraintWithItem:anImageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:cell.imageView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
    [cell.imageView addConstraint:aConstraint];

    aConstraint = [NSLayoutConstraint constraintWithItem:anImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:kGroupImageDimension];
    [cell.imageView addConstraint:aConstraint];

    aConstraint = [NSLayoutConstraint constraintWithItem:anImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:kGroupImageDimension];
    [cell.imageView addConstraint:aConstraint];
}
else
{
    anImageView = [subviews firstObject];
}

Configure la imagen en unImageView y hará lo que espera que haga un UIImageView. Sea del tamaño que quieras sin importar la imagen que le des. Esto debería ir en tableView: cellForRowAtIndexPath:


0

Básicamente, esta solución dibuja la imagen como "ajuste de aspecto" dentro del rect dado.

CGSize itemSize = CGSizeMake(80, 80);
UIGraphicsBeginImageContextWithOptions(itemSize, NO, UIScreen.mainScreen.scale);
UIImage *image = cell.imageView.image;

CGRect imageRect;
if(image.size.height > image.size.width) {
    CGFloat width = itemSize.height * image.size.width / image.size.height;
    imageRect = CGRectMake((itemSize.width - width) / 2, 0, width, itemSize.height);
} else {
    CGFloat height = itemSize.width * image.size.height / image.size.width;
    imageRect = CGRectMake(0, (itemSize.height - height) / 2, itemSize.width, height);
}

[cell.imageView.image drawInRect:imageRect];
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

0

Yo tuve el mismo problema. Gracias a todos los que respondieron: pude encontrar una solución usando partes de varias de estas respuestas.

Mi solución es usar swift 5

El problema que estamos tratando de resolver es que podemos tener imágenes con diferentes relaciones de aspecto en nuestro TableViewCell s, pero queremos que se rendericen con anchos consistentes. Las imágenes deben, por supuesto, renderizarse sin distorsión y llenar todo el espacio. En mi caso, estaba bien con algunos "recortes" de imágenes altas y delgadas, así que usé el modo de contenido.scaleAspectFill

Para hacer esto, creé una subclase personalizada de UITableViewCell. En mi caso, lo nombré StoryTableViewCell. Toda la clase se pega a continuación, con comentarios en línea.

Este enfoque funcionó para mí cuando también utilicé una Vista de accesorios personalizada y etiquetas de texto largas. Aquí hay una imagen del resultado final:

Vista de tabla renderizada con ancho de imagen consistente

class StoryTableViewCell: UITableViewCell {

    override func layoutSubviews() {
        super.layoutSubviews()

        // ==== Step 1 ====
        // ensure we have an image
        guard let imageView = self.imageView else {return}

        // create a variable for the desired image width
        let desiredWidth:CGFloat = 70;

        // get the width of the image currently rendered in the cell
        let currentImageWidth = imageView.frame.size.width;

        // grab the width of the entire cell's contents, to be used later
        let contentWidth = self.contentView.bounds.width

        // ==== Step 2 ====
        // only update the image's width if the current image width isn't what we want it to be
        if (currentImageWidth != desiredWidth) {
            //calculate the difference in width
            let widthDifference = currentImageWidth - desiredWidth;

            // ==== Step 3 ====
            // Update the image's frame,
            // maintaining it's original x and y values, but with a new width
            self.imageView?.frame = CGRect(imageView.frame.origin.x,
                                           imageView.frame.origin.y,
                                           desiredWidth,
                                           imageView.frame.size.height);

            // ==== Step 4 ====
            // If there is a texst label, we want to move it's x position to
            // ensure it isn't overlapping with the image, and that it has proper spacing with the image
            if let textLabel = self.textLabel
            {
                let originalFrame = self.textLabel?.frame

                // the new X position for the label is just the original position,
                // minus the difference in the image's width
                let newX = textLabel.frame.origin.x - widthDifference
                self.textLabel?.frame = CGRect(newX,
                                               textLabel.frame.origin.y,
                                               contentWidth - newX,
                                               textLabel.frame.size.height);
                print("textLabel info: Original =\(originalFrame!)", "updated=\(self.textLabel!.frame)")
            }

            // ==== Step 4 ====
            // If there is a detail text label, do the same as step 3
            if let detailTextLabel = self.detailTextLabel {
                let originalFrame = self.detailTextLabel?.frame
                let newX = detailTextLabel.frame.origin.x-widthDifference
                self.detailTextLabel?.frame = CGRect(x: newX,
                                                     y: detailTextLabel.frame.origin.y,
                                                     width: contentWidth - newX,
                                                     height: detailTextLabel.frame.size.height);
                print("detailLabel info: Original =\(originalFrame!)", "updated=\(self.detailTextLabel!.frame)")
            }

            // ==== Step 5 ====
            // Set the image's content modoe to scaleAspectFill so it takes up the entire view, but doesn't get distorted
            self.imageView?.contentMode = .scaleAspectFill;
        }
    }
}

0

La solución que obtuvimos es similar a muchas de las otras. Pero para obtener la posición correcta del separador, tuvimos que configurarlo antes de llamar super.layoutSubviews(). Ejemplo simplificado:

class ImageTableViewCell: UITableViewCell {

    override func layoutSubviews() {
        separatorInset.left = 70
        super.layoutSubviews()

        imageView?.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
        textLabel?.frame = CGRect(x: 70, y: 0, width: 200, height: 50)
    }

}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.