我有一个
UITableViewController
来托管无限滚动列表,因为我想试验 UITableView
性能与任何类型的 SwiftUI 实现的比较,尤其是在浏览较长的列表时。
它的效果非常好,除了
AsyncImage
。一旦我开始重复使用具有图像的单元格,我就会非常简短地看到之前加载的图像。我认为这是因为 AsyncImage 由于重复使用而被混淆为与以前相同的AsyncImage
,直到它得到更新Image
(或者缓存未命中并切换到ProgressView
)。
这是
CommunityMessageListViewController
的精简代码:
@MainActor
public class CommunityMessageListViewController: UITableViewController {
private let cellIdentifier = "cellIdentifier"
public override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
Task {
await observe(buffer: messageBuffer)
}
}
public override func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: cellIdentifier,
for: indexPath
)
cell.contentConfiguration = UIHostingConfiguration {
buildView(for: indexPath)
}
.margins(.horizontal, 8)
.margins(.vertical, 4)
.background { Color.clear }
return cell
}
private func setupTableView() {
tableView.register(
UITableViewCell.self,
forCellReuseIdentifier: cellIdentifier
)
tableView.allowsSelection = true
tableView.separatorStyle = .none
tableView.backgroundColor = .gray
tableView.contentInsetAdjustmentBehavior = .always
}
}
我删除了所有似乎与该问题无关的代码,因为它在所有其他方面都工作正常
SwiftUI 单元看起来(精简)如下:
@MainActor
public struct CommunityMessageCell: View {
private let screenInset: CGFloat
public var body: some View {
Group {
VStack(alignment: .leading) {
if let attachment = message.imageAttachment {
factory.buildMessageCellImage(
for: attachment.url,
screenInset: screenInset
)
.aspectRatio(Constants.imageRatio, contentMode: .fill)
}
factory.buildMessageCellContent()
.padding()
.frame(maxWidth: .infinity)
}.clipped()
.background(.white)
.frame(maxWidth: .infinity)
.cornerRadius(Constants.cornerRadius, antialiased: true)
.overlay(
RoundedRectangle(cornerRadius: Constants.cornerRadius)
.inset(by: Constants.borderWidth)
.strokeBorder(
.gray,
lineWidth: Constants.borderWidth,
antialiased: true
)
)
}
.padding(.horizontal)
.padding(.top)
.background(.clear)
}
}
最后
MessageCellImage
:
@MainActor
public struct MessageCellImage: View {
private let imageURL: URL
private let screenInset: CGFloat
public init(
imageURL: URL,
screenInset: CGFloat // The amount of pts of padding horizontally of the container
) {
self.imageURL = imageURL
self.screenInset = screenInset
}
public var body: some View {
AsyncImage(url: imageURL) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
ProgressView()
}
.frame(
width: UIScreen.main.bounds.size.width - screenInset * 2,
height: (UIScreen.main.bounds.size.width - screenInset * 2) / Constants.imageRatio
)
.clipped()
}
}
奖励积分给那些让我摆脱
UIScreen.main.bounds
黑客的人👍
我尝试了 GeometryReader,但它占用了我的布局...
如何确保我的 SwiftUI 视图树了解它没有显示具有相同图像的相同单元格,而是显示可能不同图像的新单元格?
我只是在输入问题时自己想出了这个问题,但我将把它留在这里供后代使用:
我的第一个预感“我认为这是因为 AsyncImage 由于重复使用而被混淆为与以前相同的
AsyncImage
”被证明是正确的,SwiftUI 需要的是一种方法来识别 AsyncImage
中存在的事实事实改变了:
AsyncImage(url: imageURL) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
ProgressView()
}
.id(imageURL)
闪烁现在消失了,它要么显示正确的缓存图像,要么在加载时显示
ProgressView
。
总结一下:封装在
UITableViewCell
中的 SwiftUI 视图有时需要自定义 id 来了解它们在重用单元格时实际发生的变化/