I would like to resize any downloaded images so they maintain their aspect ratio, however are all as wide as the UITableViewCell
they are rendered in.
My UIImageView is configured with the contentMode
as AspectFit
and I have the following anchors on my cell:
I am almost there with the current attempt:
class AnimatedImageTableViewCell: UITableViewCell {
@IBOutlet weak var gifView: AnimatedImageView!
@IBOutlet weak var imageHeightAnchor: NSLayoutConstraint!
var refreshCell: (() -> Void)?
func render(imageUrl: String) {
guard let url = URL(string: imageUrl) else { return }
gifView.kf.setImage(with: url) { result in
switch result {
case .success(let value):
let ratio = value.image.size.width / value.image.size.height
let newHeight = self.gifView.frame.width / ratio
self.imageHeightAnchor.constant = newHeight
self.refreshCell?()
case .failure(let error):
print(error) // The error happens
}
}
}
}
Also:
class HomeViewController: UITableViewController {
let images = [
"https://cdn-images-1.medium.com/max/1600/1*OJxJTJLSyqJ0nMeuswuCSQ.gif",
"https://static1.squarespace.com/static/552a5cc4e4b059a56a050501/565f6b57e4b0d9b44ab87107/566024f5e4b0354e5b79dd24/1449141991793/NYCGifathon12.gif",
"https://media2.giphy.com/avatars/100soft/WahNEDdlGjRZ.gif"
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableView.automaticDimension
tableView.allowsSelection = false
let nib = UINib(nibName: "AnimatedImageTableViewCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cellId")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return images.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let imagePath = images[indexPath.item]
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! AnimatedImageTableViewCell
cell.refreshCell = {
tableView.reloadRows(at: [indexPath], with: .automatic)
}
cell.render(imageUrl: imagePath)
return cell
}
}
However in some case the cell is too tall. The images renders full width, with the correct aspect ratio, there is just way too much space above and below the image.
I also get errors in the console as follows:
(
"<NSLayoutConstraint:0x600000329770 Kingfisher.AnimatedImageView:0x7fd339e19330.height == 268.5 (active)>",
"<NSLayoutConstraint:0x60000032a8f0 Kingfisher.AnimatedImageView:0x7fd339e19330.top == UITableViewCellContentView:0x7fd339e1a280.topMargin + 8 (active)>",
"<NSLayoutConstraint:0x60000032a850 UITableViewCellContentView:0x7fd339e1a280.bottomMargin == Kingfisher.AnimatedImageView:0x7fd339e19330.bottom + 8 (active)>",
"<NSLayoutConstraint:0x60000032a7b0 'UIView-bottomMargin-guide-constraint' V:[UILayoutGuide:0x600001908ee0'UIViewLayoutMarginsGuide']-(8)-| (active, names: '|':UITableViewCellContentView:0x7fd339e1a280 )>",
"<NSLayoutConstraint:0x600000323980 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fd339e1a280.height == 450.5 (active)>",
"<NSLayoutConstraint:0x60000032a6c0 'UIView-topMargin-guide-constraint' V:|-(8)-[UILayoutGuide:0x600001908ee0'UIViewLayoutMarginsGuide'] (active, names: '|':UITableViewCellContentView:0x7fd339e1a280 )>"
)
You can resize images by passing a size parameter in the query string portion of the URL. The size parameter replaces the larger of the height or width parameters.
If you are resizing UIImageView manually, set the content mode to UIViewContentModeScaleAspectFill . If you want to keep content mode UIViewContentModeScaleAspectFit do not resize imageview to 313. Image will adjust maximum possible width and height , keeping it's aspect ratio.
You could simply try to resize the image view after setting its image
property. For example:
class ResizableImageView: UIImageView {
override var image: UIImage? {
didSet {
guard let image = image else { return }
let resizeConstraints = [
self.heightAnchor.constraint(equalToConstant: image.size.height),
self.widthAnchor.constraint(equalToConstant: image.size.width)
]
if superview != nil {
addConstraints(resizeConstraints)
}
}
}
}
Then, add its normal constraints omitting heightAnchor
and widthAnchor
ones, because those will be added as soon as the image gets added (I'd recommend not using interface builder at this point):
let imageView = ResizableImageView(frame: .zero)
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imageView)
// These are only illustrative constraints
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
imageView.image = image
Here's a complete and fully functional example:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
downloadImage(
fromURL: URL(string: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F33105753%2F184176199496%2F1%2Foriginal.jpg?h=200&w=450&rect=0%2C0%2C400%2C200&s=a92a5a03c153d312d682b2dfedd6a6ad")!,
completionHandler: { [weak self] image in
guard let self = self, let image = image else { return }
let imageView = ResizableImageView(frame: .zero)
imageView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(imageView)
imageView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
imageView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
imageView.image = image
})
}
func downloadImage(fromURL url: URL, completionHandler: @escaping (UIImage?) -> Void) {
let operationQueue = OperationQueue()
operationQueue.addOperation {
guard let data = try? Data(contentsOf: url) else {
OperationQueue.main.addOperation {
completionHandler(nil)
}
return
}
OperationQueue.main.addOperation {
let image = UIImage(data: data)
completionHandler(image)
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With