Je veux implémenter une TableView avec une TableViewCell personnalisée montrant une image.
Pour simplifier les choses, j’ai simplement placé UIImageView dans une tableviewcell en utilisant autolayout (illustré ci-dessous) .
Ce que je veux, c’est d’afficher l’image à l’intérieur d’un UIImageView, mais ces dimensions peuvent être quelconques et sont incohérentes (portrait, paysage, carré) ...
Par conséquent, je cherche à afficher une image avec une largeur fixe (la largeur du périphérique) et une hauteur dynamique qui respecte le rapport des images. Je veux quelque chose comme ça:
Je n'ai malheureusement pas réussi à atteindre ce résultat.
Voici comment je l'ai implémentée - (J'utilise Haneke pour charger l'image à partir d'une URL - image stockée dans Amazon S3):
class TestCellVC: UITableViewCell {
@IBOutlet weak var selfieImageView: UIImageView!
@IBOutlet weak var heightConstraint: NSLayoutConstraint!
func loadItem(#selfie: Selfie) {
let selfieImageURL:NSURL = NSURL(string: selfie.show_selfie_pic())!
self.selfieImageView.hnk_setImageFromURL(selfieImageURL, placeholder: nil, format: HNKFormat<UIImage>(name: "original"), failure: {error -> Void in println(error)
}, success: { (image) -> Void in
// Screen Width
var screen_width = UIScreen.mainScreen().bounds.width
// Ratio Width / Height
var ratio = image.size.height / image.size.width
// Calculated Height for the picture
let newHeight = screen_width * ratio
// METHOD 1
self.heightConstraint.constant = newHeight
// METHOD 2
//self.selfieImageView.bounds = CGRectMake(0,0,screen_width,newHeight)
self.selfieImageView.image = image
}
)
}
override func viewDidLoad() {
super.viewDidLoad()
// Register the xib for the Custom TableViewCell
var nib = UINib(nibName: "TestCell", bundle: nil)
self.tableView.registerNib(nib, forCellReuseIdentifier: "TestCell")
// Set the height of a cell dynamically
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 500.0
// Remove separator line from UITableView
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
loadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("TestCell") as TestCellVC
cell.loadItem(selfie: self.selfies_array[indexPath.row])
// Remove the inset for cell separator
cell.layoutMargins = UIEdgeInsetsZero
cell.separatorInset = UIEdgeInsetsZero
// Update Cell Constraints
cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()
cell.sizeToFit()
return cell
}
}
Mon calcul de la hauteur dynamique est correct (je l'ai imprimé). J'ai essayé les deux méthodes (décrire dans le code) mais aucune d'entre elles n'a fonctionné:
Voir les résultats des méthodes 1 et 2 ici:
Dans le storyboard, ajoutez des contraintes de fin, de début, de haut et de bas à votre UIImageView
. Après avoir chargé votre image, il vous suffit d'ajouter une contrainte d'aspect à votre UIImageView
. Votre cellule d'images ressemblerait à quelque chose comme ceci:
class ImageTableViewCell: UITableViewCell {
@IBOutlet weak var customImageView: UIImageView!
internal var aspectConstraint : NSLayoutConstraint? {
didSet {
if oldValue != nil {
customImageView.removeConstraint(oldValue!)
}
if aspectConstraint != nil {
customImageView.addConstraint(aspectConstraint!)
}
}
}
override func prepareForReuse() {
super.prepareForReuse()
aspectConstraint = nil
}
func setCustomImage(image : UIImage) {
let aspect = image.size.width / image.size.height
let constraint = NSLayoutConstraint(item: customImageView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: customImageView, attribute: NSLayoutAttribute.height, multiplier: aspect, constant: 0.0)
constraint.priority = 999
aspectConstraint = constraint
customImageView.image = image
}
}
Vous pouvez consulter l'exemple de travail ici DynamicTableWithImages
Lors du chargement des images dans la table, chaque image peut avoir un rapport de format différent . Avec l’utilisation de SDWebImage nous pouvons obtenir l’image à partir de l’URL sous la forme -
testImageView.sd_setImage(with: url, placeholderImage: nil, options: [], completed: { (downloadedImage, error, cache, url) in
print(downloadedImage?.size.width)//prints width of image
print(downloadedImage?.size.height)//prints height of image
})
Dans UITableViewCell
, définissez la contrainte pour imageView sur top
, bottom
, leading
, trailing
, fixed height constraint
avec une priorité de 999 Et changez height-constraint-constant
en fonction de l'image.
var cellFrame = cell.frame.size
cellFrame.height = cellFrame.height - 15
cellFrame.width = cellFrame.width - 15
cell.feedImageView.sd_setImage(with: url, placeholderImage: nil, options: [], completed: { (theImage, error, cache, url) in
cell.feedImageHeightConstraint.constant = self.getAspectRatioAccordingToiPhones(cellImageFrame: cellFrame,downloadedImage: theImage!)
})
Calculez le cadre de la cellule et recherchez la hauteur respective en fonction de cela.
func getAspectRatioAccordingToiPhones(cellImageFrame:CGSize,downloadedImage: UIImage)->CGFloat {
let widthOffset = downloadedImage.size.width - cellImageFrame.width
let widthOffsetPercentage = (widthOffset*100)/downloadedImage.size.width
let heightOffset = (widthOffsetPercentage * downloadedImage.size.height)/100
let effectiveHeight = downloadedImage.size.height - heightOffset
return(effectiveHeight)
}
Si vous voulez que UIImageView indique à UITableView quelle doit être sa hauteur, vous devez configurer la vue tabulaire pour permettre le calcul automatique des lignes. Pour ce faire, vous devez définir ratedRowHeight sur une valeur positive fixe et définir rowHeight sur UIViewAutomaticDimension. C'est la première partie.
Vous devez maintenant configurer UIImageView pour demander une hauteur basée sur la largeur requise par la vue tableau et sur le format de l'image. Ce sera la deuxième partie.
Tout d’abord, définissez le contentMode sur .AspectFit. Ainsi, la vue redimensionnera l'apparence de l'image en fonction de ses dimensions. Cependant, cela ne l'oblige toujours pas à demander la hauteur nécessaire avec la mise en page automatique. Au lieu de cela, il demandera la hauteur intrinsèqueContentSize, qui peut être assez différente de l'image redimensionnée. Ensuite, ajoutez une contrainte à UIImageView de la forme width = height * multiplier
, où le multiplicateur est basé sur le rapport de format de l'image elle-même.
Désormais, la vue de tableau nécessitera la largeur correcte, le mécanisme contentMode garantira le redimensionnement correct de l'image sans distorsion et la contrainte de rapport d'aspect garantira que la vue de l'image requiert la hauteur correcte via la mise en page automatique.
Cela marche. L'inconvénient est que vous devrez mettre à jour la contrainte de rapport d'aspect chaque fois que vous affectez une nouvelle image à la vue d'image, ce qui peut se produire assez souvent si la vue d'image se trouve dans une cellule en cours de réutilisation.
Une approche alternative consiste à ajouter uniquement une contrainte de hauteur et à la mettre à jour dans les sous-vues layoutSublies d'une vue contenant la vue d'image. Cela a de meilleures performances mais une moins bonne modularité du code.
Swift 4.0
Vous pouvez utiliser un code simple pour gérer la hauteur de ligne en fonction de la hauteur de imageView dans la cellule tableView.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if let height = self.rowHeights[indexPath.row]{
return height
}else{
return defaultHeight
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return imageArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "editImageCell"
let customCell : EdiPostImageCell = (self.imagesTableView.dequeueReusableCell(withIdentifier: identifier)) as! EdiPostImageCell
let aspectRatio = (imageArray[indexPath.row]).size.height/(imageArray[indexPath.row]).size.width
customCell.editlImageView.image = imageArray[indexPath.row]
let imageHeight = self.view.frame.width*aspectRatio
self.imagesTableView.beginUpdates()
self.rowHeights[indexPath.row] = imageHeight
tableView.endUpdates()
}
Supprimez la contrainte de hauteur de votre imageView. Choisissez le contentMode de la vue d'image qui commence par "aspect", par exemple: aspect ou remplissage (comme il convient en fonction de vos besoins).
NOTE: Vous devez également définir la hauteur de la ligne de sorte que l'imageView puisse s'y adapter. Utilisation
heightForRowAtIndexPath
définir la hauteur de ligne personnalisée.