web-dev-qa-db-fra.com

Comment rendre UITableViewCell ImageView une taille fixe, même lorsque l'image est plus petite

J'ai un tas d'images que j'utilise pour les vues d'image de cellule, elles ne sont pas toutes plus grandes que 50x50. par exemple. 40x50, 50x32, 20x37 .....

Lorsque je charge la vue tabulaire, le texte ne s'aligne pas car la largeur des images varie. Aussi, j'aimerais que les petites images apparaissent au centre et non à gauche.

Voici le code que j'essaie dans ma méthode '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];

Comme vous pouvez le constater, j'ai essayé plusieurs solutions, mais aucune d'entre elles ne fonctionne.

95
Robert

Il n'est pas nécessaire de tout réécrire. Je recommande de faire ceci à la place:

Postez ceci dans votre fichier .m de votre cellule personnalisée.

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

Cela devrait faire l'affaire bien. :]

141
Nils Munch

Pour ceux d'entre vous qui n'ont pas de sous-classe 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;
}

Le code ci-dessus définit la taille à 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 vous pouvez utiliser une autre approche (non testée) suggérée par @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();

Le code ci-dessus est la version Swift 3+ de ce qui précède.

123
German Attanasio

Voici comment je l'ai fait. Cette technique permet de déplacer le texte et les étiquettes de texte de détail de manière appropriée vers la gauche:

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

ajouter une vue en tant que vue secondaire à la cellule 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];
21
Rinju Jain

La cellule entière n'a pas besoin d'être refaite. Vous pouvez utiliser les propriétés indentationLevel et indentationWidth de tableViewCells pour déplacer le contenu de votre cellule. Ensuite, vous ajoutez votre imageView personnalisée sur le côté gauche de la cellule.

12
Peter Robert

Un Simply Swift ,

Étape 1: Créer une sous-classe de UITableViewCell
Étape 2: Ajouter cette méthode à la sous-classe de UITableViewCell

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

Étape 3: Créer un objet de cellule à l'aide de cette sous-classe dans cellForRowAtIndexPath,

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

Étape 4: Profitez

6
Zaid Pathan

Créez mieux une vue d’image et ajoutez-la en tant que sous-vue à la cellule. Vous pouvez ensuite obtenir la taille de cadre souhaitée.

5
Warrior
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
Mohit Gaur

J'ai créé une extension en utilisant la réponse de @GermanAttanasio. Il fournit une méthode pour redimensionner une image à la taille souhaitée et une autre pour l’ajouter tout en ajoutant une marge transparente à l’image (cela peut être utile pour les vues sous forme de tableau où l’image doit également avoir une marge).

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
diegoreymendez

Cela a fonctionné pour moi à Swift:

Créez une sous-classe de UITableViewCell (assurez-vous de relier votre cellule au 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)
        }
    }
}

Dans cellForRowAtIndexPath, retirez la cellule de votre nouveau type de cellule:

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

Évidemment, changez les valeurs numériques en fonction de votre mise en page

1
Derek

Cette solution dessine essentiellement l’image en "ajustement d’aspect" dans le rect donné.

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
Nick

Si vous utilisez cell.imageView?.translatesAutoresizingMaskIntoConstraints = false, vous pouvez définir des contraintes sur l'imageView. Voici un exemple de travail que j'ai utilisé dans un projet. J'évitais les sous-classes et je n'avais pas besoin de créer de story-board avec des cellules prototypes, mais cela me prenait un bon bout de temps avant de fonctionner. Il est donc préférable de ne l'utiliser que s'il n'existe pas de moyen plus simple ou plus concis. 

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
robwithhair

Voici la méthode de travail de @germanattanasio, écrite pour 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()
}
0
FredericP

UITableViewCell standard fonctionne bien pour positionner des objets, mais cell.imageView ne semble pas se comporter comme vous le souhaitez. J'ai trouvé qu'il était assez simple de placer correctement UITableViewCell en donnant d'abord à cell.imageView une image correctement dimensionnée comme

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

Ensuite, vous pouvez simplement connecter votre propre UIImageView qui fonctionne correctement avec

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

Définissez l'image sur une imageImageView et il fera ce que vous attendez d'un UIImageView. Soyez la taille que vous voulez, peu importe l'image que vous lui donnez. Cela devrait aller dans tableView: cellForRowAtIndexPath:

0
Gerard

J'ai eu le même problème. Merci à tous ceux qui ont répondu - j'ai pu trouver une solution en utilisant des parties de plusieurs de ces réponses.

Ma solution utilise Swift 5

Le problème que nous essayons de résoudre est qu’il est possible que nous ayons des images avec différents formats d’image dans notre TableViewCells, mais nous voulons qu’elles rendent le rendu avec des largeurs cohérentes. Bien entendu, les images doivent être restituées sans distorsion et occuper tout l’espace. Dans mon cas, je me débrouillais bien avec quelques "recadrages" d'images hautes et maigres, alors j'ai utilisé le mode contenu .scaleAspectFill

Pour ce faire, j'ai créé une sous-classe personnalisée de UITableViewCell. Dans mon cas, je l'ai nommé StoryTableViewCell. La classe entière est collée ci-dessous, avec des commentaires en ligne.

Cette approche a fonctionné pour moi lorsque j'utilisais également une vue d'accessoire personnalisée et de longues étiquettes de texte. Voici une image du résultat final:

Affichage du tableau avec une largeur d'image constante

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
Neil Poulin