SwiftUI 中的 AsyncImage 查看 UITableViewCell 的内容会闪烁上一张图片

问题描述 投票:0回答:1

我有一个

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 视图树了解它没有显示具有相同图像的相同单元格,而是显示可能不同图像的新单元格?

uitableview swiftui swiftui-asyncimage
1个回答
0
投票

我只是在输入问题时自己想出了这个问题,但我将把它留在这里供后代使用:

我的第一个预感“我认为这是因为 AsyncImage 由于重复使用而被混淆为与以前相同的

AsyncImage
”被证明是正确的,SwiftUI 需要的是一种方法来识别
AsyncImage
中存在的事实事实改变了:

        AsyncImage(url: imageURL) { image in
            image
                .resizable()
                .aspectRatio(contentMode: .fill)
        } placeholder: {
            ProgressView()
        }
        .id(imageURL)

闪烁现在消失了,它要么显示正确的缓存图像,要么在加载时显示

ProgressView

总结一下:封装在

UITableViewCell
中的 SwiftUI 视图有时需要自定义 id 来了解它们在重用单元格时实际发生的变化/

© www.soinside.com 2019 - 2024. All rights reserved.