使用 UIStackView 时,UIImageView 在 UITableViewCell 内被挤压
Posted
技术标签:
【中文标题】使用 UIStackView 时,UIImageView 在 UITableViewCell 内被挤压【英文标题】:UIImageView squished inside UITableViewCell when using UIStackView 【发布时间】:2018-04-17 14:50:59 【问题描述】:在 UIStackView
中使用 UIImageView
时,我看到了一些奇怪的行为,我怀疑我忽略了一些明显的东西 - 尽管此时我已经尝试了很多东西。
我要做的很简单:从网上下载一张图片,在UIStackView
中设置UIImageView
。 UIStackView
还包含一个UILabel
(而我的实际项目包含不少UILabels
,因此是UIStackView
的原因)。下图有彩色背景来说明哪里出了问题,我还包含了完整的 Playground 代码,以便轻松复制/粘贴。
正在下载的图像大于屏幕的宽度/高度,我设置了contentMode = .scaleAspectFit
。 UITableView
使用tableView.rowHeight = UITableViewAutomaticDimension
和 .estimatedRowHeight
。我已经设置了约束,并且还尝试在 UIImageView 上设置拥抱优先级,但无济于事。我不提前知道确切的单元格高度,单元格高度都会有所不同。
实际发生的情况是图像被调整得非常小,尽管UIImageView
上的前导/尾随约束、UIStackView
上的约束、单元格上的.rowHeight
和@987654339 上的.scaleAspectFit
@。你甚至可以看到一小块(红色)标签,所以我知道UIStackView
被固定在UITableViewCell
的contentView
上。
颜色键
橙色:UITableViewCell 背景 红色:UILabel 背景 绿色:UIImageView 背景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
【问题讨论】:
【参考方案1】:您似乎认为堆栈视图中的图像视图会以某种方式从内向外调整单元格的高度。它不会。如果您想使用自动单元格高度,请不要使用堆栈视图(或添加约束以显式调整堆栈视图的大小,但我怀疑这会给您带来想要的结果)。
您的代码中堆栈视图的目的是什么?堆栈视图所做的只是在困难的情况下为您设置约束。但这不是一个困难的情况。标签和图像视图一起可以很容易地手动配置。
【讨论】:
感谢您的回复。以上是实际项目的简化版本——实际项目有大约六个标签,所以理论上stackview会让事情更容易管理。因此,根据您的回复,听起来 stackviews 不会从其内容中获取内在内容大小,因此不会从内向外调整大小。 但实际上堆栈视图是问题。用你的简化版试试,你会看到的。 你是对的。正如here 所证明的那样,Apple hard 推动 StackViews。 “一般来说,使用堆栈视图来管理尽可能多的布局。仅当您无法单独使用堆栈视图实现目标时才使用创建约束。”我不明白为什么要使用 StackView似乎无法从其内容中传递内在内容大小,因此使用范围有限。 在非常少数情况下,您会尝试使用其子视图的约束“从内到外”调整视图的大小。在这些情况下,堆栈视图通常只会妨碍您(除非您可以从外部调整堆栈视图本身的大小,而您在此处无法做到这一点)。因此,Apple 说您通常可以使用堆栈视图并没有错——尽管事实上我不同意他们过度推销它们的方式。对我来说,堆栈视图仅适用于一个用例,即解决“平均分配”问题(即使那样你也可以在没有它们的情况下做到这一点)。【参考方案2】:你只需要在这个方法中给出你估计的行高
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
return 300; //Choose your custom row height
【讨论】:
感谢您的回复。tableView.rowHeight = UITableViewAutomaticDimension
和 tableView.estimatedRowHeight = 300
使这种方法变得不必要我很确定。我也不希望牢牢设置单元格高度+我不知道提前。以上是关于使用 UIStackView 时,UIImageView 在 UITableViewCell 内被挤压的主要内容,如果未能解决你的问题,请参考以下文章
Swift UIStackView 利用 UIStackView 的全宽
UIStackView 内的视图未使用 AutoLayout 调整大小