Como faço para tornar ImageView de UITableViewCell em um tamanho fixo, mesmo quando a imagem é menor


104

Eu tenho um monte de imagens que estou usando para visualizações de imagens de células, todas elas não são maiores que 50x50. por exemplo, 40x50, 50x32, 20x37 .....

Quando eu carrego a visualização da tabela, o texto não se alinha porque a largura das imagens varia. Também gostaria que pequenas imagens aparecessem no centro em vez de à esquerda.

Aqui está o código que estou tentando dentro do meu 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 você pode ver, tentei algumas coisas, mas nenhuma delas funcionou.

Respostas:


152

Não é necessário reescrever tudo. Eu recomendo fazer isso em vez disso:

Poste isso dentro do arquivo .m da sua célula personalizada.

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

Isso deve funcionar bem. :]


28
se você definir self.imageView.boundsa imagem será centralizada.
BLeB

45
e se não adicionarmos uma subclasse de UITableViewCell?
polaridade

3
@ 動靜 能量: Subclassificar UITableViewCell é o truque principal para que isso funcione.
auco

5
Isso não funciona para mim. A imagem ainda envolve todo o imageView.
joslinm

14
Também não funciona para mim, uma vez que os rótulos estão desalinhados.
nverinaud

139

Para aqueles de vocês que não têm uma subclasse 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;
}

O código acima define o tamanho como 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();

Ou você pode usar outra abordagem (não testada) sugerida 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();

O código acima é a versão Swift 3+ do anterior.


3
A distorção da imagem pode ser corrigida por UIGraphicsBeginImageContextWithOptions (itemSize, NO, UIScreen.mainScreen.scale); em vez de UIGraphicsBeginImageContext (itemSize);
Kiran Ruth R

1
Boa resposta. BTW, eu não tive a opção de UIScreen.mainScreen.scaleentão eu apenas fui com UIGraphicsBeginImageContext. Também redimensionou o imageView na célula básica.
denikov

3
@GermanAttanasioRuiz ao selecionar a célula ela volta a se redimensionar para o original, é suposto ser assim, como resolver isso.
Bonnie

6
para todos aqueles que ficaram confusos como eu, você precisa definir a imagem antes do início do contexto. isto é, cell.imageView.image = [UIImage imageNamed: @ "my_image.png"];
Guy Lowe,

5
Essas operações caras não deveriam fazer parte de cellForRowAtIndexPath
Krizai

33

Veja como eu fiz. Esta técnica se encarrega de mover o texto e os rótulos de texto detalhados de forma adequada para a esquerda:

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

Obrigado, Chris. Funcionou perfeitamente. Você pode querer atualizá-lo removendo o autorelease porque ARC o proíbe agora. Ótima resposta!
CSawy

1
Esta ainda é a melhor solução hoje. Obrigado.
Rémi Belzanti de

Atualmente, eu provavelmente recomendaria fazer uma célula customizada com um xib ou uma célula protótipo em um storyboard e criar uma visualização de imagem totalmente diferente, sem relação com a visualização de imagem de uma célula padrão. Mas isso ainda é bastante simples, eu acho!
Chris

1
Quero fazer tudo com código em vez de usar um xib ou storyboard e funcionou perfeitamente.
John81,

Essa resposta não faz nada se w <desejado Com, o que me parece ser o caso de uso de interesse (pelo menos, na pergunta).
Nate

21

visualização da imagem adicionada como subvisualização à célula tableview

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
Não se esqueça de liberar o imgView se não estiver usando o ARC.
Charlie Monroe

14

A célula inteira não precisa ser refeita. Você pode usar as propriedades indentationLevel e indentationWidth de tableViewCells para deslocar o conteúdo de sua célula. Em seguida, você adiciona sua imageView personalizada ao lado esquerdo da célula.


6

É melhor criar uma visualização de imagem e adicioná-la como uma subvisualização à célula. Então, você pode obter o tamanho de quadro desejado.


Acabei de tentar fazer isso, parece bom começar, mas o texto nas células agora se sobrepõe às imagens, como mudo a visualização do conteúdo 50 pixels para a direita? cell.contentView.bounds = CGRectMake (50, 0, 270, 50); não tem nenhum efeito
Robert

1
Em vez de usar a visualização padrão da célula, crie um rótulo e adicione-o como uma subvisão à célula e, a seguir, atribua o texto à propriedade do texto do rótulo. Por isso, você pode projetar a célula de acordo com sua necessidade.
Warrior

Isso será mais útil se você quiser exibir título, data, descrição, etc., mais valores em uma célula.
Guerreiro

Ok, então basicamente terei que refazer a célula programaticamente. Não deve ser muito difícil. Obrigado pela ajuda.
Robert

6

Simplesmente Swift ,

Etapa 1: criar uma subclasse da UITableViewCell
etapa 2: adicionar este método à subclasse de UITableViewCell

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

Etapa 3: Crie um objeto de célula usando essa SubClasse em cellForRowAtIndexPath,

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

Etapa 4: aproveite


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

Isso funcionou para mim rapidamente:

Crie uma subclasse de UITableViewCell (certifique-se de vincular sua célula no storyboard)

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)
        }
    }
}

Em cellForRowAtIndexPath, retire a célula da fila como seu novo tipo de célula:

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

Obviamente, altere os valores numéricos para se adequar ao seu layout


1

Criei uma extensão usando a resposta de @GermanAttanasio. Ele fornece um método para redimensionar uma imagem para um tamanho desejado e outro método para fazer o mesmo ao adicionar uma margem transparente à imagem (isso pode ser útil para visualizações de tabela onde você deseja que a imagem também tenha uma margem).

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

Aqui está o método de trabalho 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

Se você usar, cell.imageView?.translatesAutoresizingMaskIntoConstraints = falsevocê pode definir restrições no imageView. Aqui está um exemplo prático que usei em um projeto. Evitei subclasses e não precisei criar storyboard com células de protótipo, mas demorei um pouco para começar a funcionar, então provavelmente melhor usar apenas se não houver uma forma mais simples ou concisa disponível para você.

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

O UITableViewCell regular funciona bem para posicionar coisas, mas o cell.imageView não parece se comportar como você deseja. Descobri que é simples o suficiente fazer com que o UITableViewCell tenha um layout adequado, primeiro dando a cell.imageView uma imagem de tamanho adequado, 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;

Então você pode apenas conectar seu próprio UIImageView funcionando corretamente com

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

Defina a imagem em anImageView e ela fará o que você espera que um UIImageView faça. Seja do tamanho que você deseja, independentemente da imagem que você fornecer. Isso deve ir em tableView: cellForRowAtIndexPath:


0

Esta solução desenha essencialmente a imagem como 'ajuste de aspecto' dentro do retângulo fornecido.

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

Eu tive o mesmo problema. Obrigado a todos os que responderam - Consegui encontrar uma solução juntos usando partes de várias dessas respostas.

Minha solução é usar o swift 5

O problema que estamos tentando resolver é que podemos ter imagens com diferentes proporções de aspecto em nosso TableViewCell mas queremos que elas sejam renderizadas com larguras consistentes. As imagens devem, é claro, renderizar sem distorção e preencher todo o espaço. No meu caso, não havia problema em alguns "cortes" de imagens altas e estreitas, então usei o modo de conteúdo.scaleAspectFill

Para fazer isso, criei uma subclasse personalizada de UITableViewCell. No meu caso, eu nomeei isso StoryTableViewCell. A classe inteira é colada abaixo, com comentários embutidos.

Essa abordagem funcionou para mim ao usar também uma Visualização de acessório personalizada e rótulos de texto longo. Aqui está uma imagem do resultado final:

Visualização de tabela renderizada com largura de imagem 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

A solução que encontramos é semelhante a muitas das outras. Mas para obter a posição correta do separador, tivemos que configurá-lo antes de chamar super.layoutSubviews(). Exemplo 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)
    }

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.