Reputation: 2398
I'm seeing some odd behavior when using UIImageView
inside a UIStackView
and I suspect there's something obvious that I'm overlooking - though I've tried quite a few things at this point.
What I'm trying to do is very straightforward: download an image from the web, set the UIImageView
inside the UIStackView
. The UIStackView
also contains a UILabel
(and my actual project contains quite a few UILabels
, thus the reason for the UIStackView
). The below image has colored backgrounds to demonstrate where things are going wrong, and I've also included the full Playground code so as to be easily copied/pasted.
The image being downloaded is bigger than the width/height of the screen, and I've set contentMode = .scaleAspectFit
. The UITableView
uses
tableView.rowHeight = UITableViewAutomaticDimension
and .estimatedRowHeight
. I've set constraints, and have also attempted to set the hugging priority on the UIImageView, to no avail. I don't know the exact cell height ahead of time, and the cell heights will all differ.
What's actually happening is that the image is being resized very small, despite the leading/trailing constraints on the UIImageView
, constraints on the UIStackView
, .rowHeight
on the cell, and .scaleAspectFit
on the UIImageView
. You can even see a sliver of the (red) label, so I know the UIStackView
is being pinned to the contentView
of the UITableViewCell
.
Color Key
import UIKit
import PlaygroundSupport
class CustomView: UIView, UITableViewDataSource {
var tableView = UITableView()
var tableData = [String]()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.gray
tableView.dataSource = self
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
tableView.tableFooterView = UIView()
tableView.translatesAutoresizingMaskIntoConstraints = false
addSubview(tableView)
tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
tableData = [String](repeating: "https://img.webmd.com/dtmcms/live/webmd/consumer_assets/site_images/article_thumbnails/video/wibbitz/wbz-breakfast-most-important-meal.jpg", count: 2)
tableView.register(CustomCell.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomCell
let imageURL = tableData[indexPath.row]
if let url = URL.init(string: imageURL) {
cell.cellImageView?.downloadImage(with: url,
completion: {
// ...
})
}
return cell
}
}
class CustomCell: UITableViewCell {
var cellImageView: UIImageView?
required init?(coder aDecoder: NSCoder) {
fatalError() // since we're not using
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupCell()
}
func setupCell() {
backgroundColor = UIColor.orange
clipsToBounds = true
let tempLabel = UILabel()
tempLabel.translatesAutoresizingMaskIntoConstraints = false
tempLabel.backgroundColor = UIColor.red
tempLabel.text = "this is some text"
tempLabel.numberOfLines = 0
tempLabel.font = UIFont.systemFont(ofSize: 20, weight: .bold)
cellImageView = UIImageView()
cellImageView?.clipsToBounds = true
cellImageView?.contentMode = .scaleAspectFit
cellImageView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
cellImageView?.backgroundColor = UIColor.green
cellImageView?.translatesAutoresizingMaskIntoConstraints = false
let stackView = UIStackView(arrangedSubviews: [tempLabel, cellImageView!])
contentView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .fillProportionally
stackView.alignment = .leading
stackView.spacing = 10
stackView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
cellImageView?.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
cellImageView?.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
}
}
extension UIImageView {
func downloadImage(with url: URL, completion: @escaping () -> Void) {
let task = URLSession.shared.dataTask(with: url) { [weak weakSelf = self] data, response, error in
guard error == nil else {
print("UIImageView: error downloading image: \(String(describing: error))")
return
}
guard let data = data, let downloadedImage = UIImage.init(data: data) else {
print("UIImageView: issue with data from downloaded image.")
return
}
DispatchQueue.main.async {
weakSelf?.image = downloadedImage
completion()
}
}
task.resume()
}
}
let containerView = CustomView(frame: CGRect(x: 0, y: 0, width: 400, height: 600))
containerView.backgroundColor = UIColor.blue
PlaygroundPage.current.liveView = containerView
PlaygroundPage.current.needsIndefiniteExecution = true
Upvotes: 3
Views: 1048
Reputation: 535556
You seem to think that the image view inside the stack view will somehow size the cell's height from the inside out. It won't. If you want to use automatic cell height, don't use a stack view (or add constraints to size the stack view explicitly, but I doubt that will give you the desired results).
What's the purpose of the stack view in your code anyway? All a stack view does is make constraints for you in difficult situations. But this is not a difficult situation. The label and the image view together can easily be configured by hand.
Upvotes: 3
Reputation: 914
You simply have to give your estimated row height in this method
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 300; //Choose your custom row height
}
Upvotes: 0