gpt4 book ai didi

ios - Swift - 在 TableViewCell 中使用不同大小的图像有困难

转载 作者:搜寻专家 更新时间:2023-10-30 22:07:14 25 4
gpt4 key购买 nike

我正在使用 Kingfisher 加载大量远程图像,但很难将它们正确加载到具有动态高度单元格的 Tableview 中。 我的目标是让图像始终是屏幕的全宽和动态高度,如何实现?

我之前问过一个相关问题,它让我理解了使用堆栈 View 的基本布局:SnapKit: How to set layout constraints for items in a TableViewCell programatically

所以我构建了如下内容:

Hierarchy overview

使用以下代码(为简洁起见删除了一些部分):

// CREATE VIEWS
let containerStack = UIStackView()
let header = UIView()
let headerStack = UIStackView()
let title = UILabel()
let author = UILabel()
var previewImage = UIImageView()

...

// KINGFISHER
let url = URL(string: article.imageUrl)
previewImage.kf.indicatorType = .activity
previewImage.kf.setImage(
with: url,
options: [
.transition(.fade(0.2)),
.scaleFactor(UIScreen.main.scale),
.cacheOriginalImage
]) { result in
switch result {
case .success(_):
self.setNeedsLayout()
UIView.performWithoutAnimation {
self.tableView()?.beginUpdates()
self.tableView()?.endUpdates()
}
case .failure(let error):
print(error)
}
}

...

// LAYOUT
containerStack.axis = .vertical

headerStack.axis = .vertical
headerStack.spacing = 6
headerStack.addArrangedSubview(title)
headerStack.addArrangedSubview(author)
header.addSubview(headerStack)

containerStack.addArrangedSubview(header)
containerStack.addSubview(previewImage)

addSubview(containerStack)

headerStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(20)
}

containerStack.snp.makeConstraints { make in
make.edges.equalToSuperview()
}

没有 imageView 的约束,图像不会出现。

有如下约束,图像也不会出现:

previewImage.snp.makeConstraints { make in
make.leading.trailing.bottom.equalToSuperview()
make.top.equalTo(headerView.snp.bottom).offset(20)
}

在其他尝试中,图像完全倾斜或与标签/其他单元格和图像重叠。

最后,关注这条评论:With Auto Layout, how do I make a UIImageView's size dynamic depending on the image?这个要点:https://gist.github.com/marcc-orange/e309d86275e301466d1eecc8e400ad00并且在这些约束下 make.edges.equalToSuperview() 我能够让图像以正确的比例显示,但它们完全覆盖了标签。

理想情况下它看起来像这样:

mockup

最佳答案

带有示例代码的 100% 工作解决方案

我刚刚设法实现了具有动态标签内容和动态图像尺寸的相同布局。我通过约束和 Autolayout 做到了这一点。看看这个演示项目 GitHub Repository


正如 matt 指出的那样,我们必须在下载图像后计算每个单元格的高度(当我们知道它的宽度和高度时)。注意每个cell的高度是通过tableView的委托(delegate)方法heightForRowAt IndexPath

计算的

因此,在下载完每张图片后,将图片保存在该 indexPath 的数组中,然后重新加载该 indexPath,以便根据图片尺寸再次计算高度。

需要注意的一些关键点如下

  • Use 3 types of cells. One for label, one for subtitle and one for Image. Inside cellForRowAt initialize and return the appropriatecell. Each cell has a unique cellIdentifier but class is same
  • number of sections in tableView == count of data source
  • number of rows in section == 3
  • First row corresponds to title, second row corresponds to subtitle and the 3rd corresponds to the image.
  • number of lines for labels should be 0 so that height should be calculated based on content
  • Inside cellForRowAt download the image asynchrounously, store it in array and reload that row.
  • By reloading the row, heightForRowAt gets called, calculates the required cell height based on image dimensions and returns the height.
  • So each cell's height is calculated dynamically based on image dimensions

看看一些代码

override func numberOfSections(in tableView: UITableView) -> Int {
return arrayListItems.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//Title, SubTitle, and Image
return 3
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

switch indexPath.row {
case 0:
//configure and return Title Cell. See code in Github Repo

case 1:

//configure and return SubTitle Cell. See code in Github Repo

case 2:

let cellImage = tableView.dequeueReusableCell(withIdentifier: cellIdentifierImage) as! TableViewCell
let item = arrayListItems[indexPath.section]
//if we already have the image, just show
if let image = arrayListItems[indexPath.section].image {
cellImage.imageViewPicture.image = image
}else {

if let url = URL.init(string: item.imageUrlStr) {

cellImage.imageViewPicture.kf.setImage(with: url) { [weak self] result in
guard let strongSelf = self else { return } //arc
switch result {
case .success(let value):

print("=====Image Size \(value.image.size)" )
//store image in array so that `heightForRowAt` can use image width and height to calculate cell height
strongSelf.arrayListItems[indexPath.section].image = value.image
DispatchQueue.main.async {
//reload this row so that `heightForRowAt` runs again and calculates height of cell based on image height
self?.tableView.reloadRows(at: [indexPath], with: .automatic)
}

case .failure(let error):
print(error) // The error happens
}
}

}

}


return cellImage

default:
print("this should not be called")
}

//this should not be executed
return .init()
}


override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//calculate the height of label cells automatically in each section
if indexPath.row == 0 || indexPath.row == 1 { return UITableView.automaticDimension }

// calculating the height of image for indexPath
else if indexPath.row == 2, let image = arrayListItems[indexPath.section].image {

print("heightForRowAt indexPath : \(indexPath)")
//image

let imageWidth = image.size.width
let imageHeight = image.size.height

guard imageWidth > 0 && imageHeight > 0 else { return UITableView.automaticDimension }

//images always be the full width of the screen
let requiredWidth = tableView.frame.width

let widthRatio = requiredWidth / imageWidth

let requiredHeight = imageHeight * widthRatio

print("returned height \(requiredHeight) at indexPath: \(indexPath)")
return requiredHeight


}
else { return UITableView.automaticDimension }
}

相关。

我们可以遵循的另一种方法是从 API 请求返回图像尺寸。如果能做到这一点,事情就会简单很多。看看这个类似的问题(对于 collectionView)。

Self sizing Collection view cells with async image downloading.

Placholder.com用于异步获取图片

Self Sizing Cells: (A Good read)

示例

Sample

关于ios - Swift - 在 TableViewCell 中使用不同大小的图像有困难,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55128485/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com