Jusqu'à présent, je n'utilisais que des étiquettes de texte dans des cellules qui devraient se redimensionner elles-mêmes. Je mets généralement des contraintes sur toutes les arêtes (vue du contenu ou voisins) et ajoute les lignes suivantes à ma 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
Maintenant, lorsque j'essaie d'inclure des images, je suis confronté à plusieurs problèmes. Au cours de certaines recherches, j'ai trouvé plusieurs approches écrites dans Objective-C, mais alors que je ne savais pas laquelle de celles-ci résolvait réellement mon problème central, je suis toujours aux prises avec un code Objective-C complexe. Donc, pour résumer: Mon objectif final est d’obtenir un tableau avec des cellules correctement dimensionnées (hauteurs correctes), chacune contenant un titre et une image qui correspond à la largeur de l’écran. Ces images peuvent être de différents formats et de différentes tailles. Pour simplifier certains des défis, j'ai préparé un nouveau projet, plus simple:
UITableViewController
et une CustomCell
avec deux IBOutlets title: String
et postedImage: UIImage
(UIImage de taille aléatoire dans le storyboard) pour configurer les cellules. J'ai défini des contraintes aux bords et les unes aux autres.Dans un projet un peu plus complexe, je souhaite que différentes images s’adaptent automatiquement à la largeur de l’écran, qu’il s’agisse de portrait ou de paysage. Si leur largeur est inférieure à celle de l’écran, elles doivent être redimensionnées. Comme dans le projet ci-dessus, chaque hauteur de cellule doit correspondre à son contenu individuel, tel que défini par les contraintes du storyboard. Quoi qu’il en soit, cela commence par le problème évoqué ci-dessus: comment puis-je laisser ces deux cellules avoir la même hauteur, avec une hauteur correcte, épousant ainsi son contenu comme défini dans le storyboard?
Merci mille fois pour toute aide!
Je pense donc que le problème sous-jacent est un problème de poulet et d’œufs.
Pour "ajuster" l'image à la UIImageView
, le système a besoin d'une taille pour la vue. Cependant, nous aimerions que la hauteur de la vue soit déterminée après l'aspect correspondant à l'image avec une largeur connue pour la vue. .
Une solution de contournement consiste à calculer nous-mêmes le rapport de format de l'image et à le définir comme une contrainte sur la variable UIImageView
lorsque nous définissons l'image. Ainsi, le système de mise en page automatique dispose de suffisamment d’informations pour redimensionner la vue (une largeur et un rapport hauteur/largeur).
J'ai donc changé votre classe de cellules personnalisée en:
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
}
}
Et ensuite, dans le délégué, faites ceci au lieu de définir l'image directement:
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
}
Et vous aurez:
J'espère que cela t'aides!
J'ai traduit la solution de Pollard en version objc quelque chose comme ceci.
J'ai également reçu des avertissements du type "Impossible de satisfaire simultanément les contraintes". Ma solution est de définir la priorité de contrainte d’espace vertical sur 999 plutôt que sur 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
Travailler dans Swift 3 + iOS 8
Utilisation d’AutoLayout et d’objets UITableViewCell de taille variable -
Pour une image, définissez les contraintes d’espace haut, bas, avant et arrière sur 0.
Code à ajouter à viewDidLoad ()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 200
Définir le paramètre 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) + <<Height of image title label>>
return cellHeight
}
}
Au lieu d'avoir la méthode setPostedImage
personnalisée, il est plus pratique de mettre à jour la contrainte d'aspect dans updateConstraints
. De cette façon, vous pouvez modifier l'image de UIImageView directement sans méthode d'assistance supplémentaire:
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];
}
Swift 4
Calculez la hauteur de l'image à l'aide de UIImage
extension
.
Vous pouvez utiliser extension
sur UIImage
pour obtenir la hauteur en fonction de AspectRatio de l'image.
extension UIImage {
var cropRatio: CGFloat {
let widthRatio = CGFloat(self.size.width / self.size.height)
return widthRatio
}
}
Donc, dans votre méthode 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
}