由 LPLinkView 和 UIImageView 组成的单元格的 CollectionView 速度很慢,并且在滚动时重新加载数据

CollectionView with Cells made up of LPLinkView's and UIImageView's is Slow and Reloads Data While Scrolling

我有一个 UICollectionView 有两种类型的细胞;一些从 URL 加载图像,另一些从 URL 加载元数据并用 LPLinkView.

显示它

下面是我通过 LPLinkView 显示数据的单元格代码:

import UIKit
import LinkPresentation

class LinkItemLinkPreviewCell: UICollectionViewCell {
    
    static let identifier = "kLinkPreviewCollectionViewCell"
    var linkView: LPLinkView?
    var urlMetadata: LPLinkMetadata?
    
    @IBOutlet var containerView: UIView? = UIView()
    @IBOutlet var titleLabel: UILabel? = UILabel()
    @IBOutlet var dateLabel: UILabel? = UILabel()
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.clipsToBounds = true
        self.autoresizesSubviews = true
        self.layer.cornerRadius = 20
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
    }
    
    var linkItem: LinkPostDataObject? {
        didSet {
            titleLabel?.text = linkItem?.postTitle ?? ""
            
            let df = DateFormatter()
            df.dateFormat = "yyyy-MM-dd hh:mm"
            dateLabel?.text = df.string(from: toDate((linkItem?.timeSent)!/1000)!)
            
            let provider = LPMetadataProvider()
            let url = URL(string: linkItem!.url)
            
            if url != nil {
                provider.startFetchingMetadata(for: URL(string: linkItem!.url)!) { (metadata, error) in
                    if let md = metadata {
                        DispatchQueue.main.async { [self] in
                            linkView = LPLinkView()
                            linkView?.metadata = md
                            linkView!.frame = containerView!.frame
                            containerView?.addSubview(linkView!)
                            linkView!.isUserInteractionEnabled = false
                            urlMetadata = metadata
                        }
                    }
                }
            }
        }
    }
 }

下面是 UICollectionViewCell 的代码,显示来自 URL 的图像:

import UIKit

class LinkItemImageCell: UICollectionViewCell {
    
    static let identifier = "kImageCollectionViewCell"
    
    @IBOutlet var imageView: UIImageView? = UIImageView()
    @IBOutlet var titleLabel: UILabel? = UILabel()
    @IBOutlet var dateLabel: UILabel? = UILabel()
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        self.clipsToBounds = true
        self.autoresizesSubviews = true
        self.layer.cornerRadius = 20
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
    }
    
    var linkItem: LinkPostDataObject? {
        didSet {
            titleLabel?.text = linkItem?.postTitle ?? ""
            
            let df = DateFormatter()
            df.dateFormat = "yyyy-MM-dd hh:mm"
            dateLabel?.text = df.string(from: toDate((linkItem?.timeSent)!/1000)!)
            
            if linkItem?.url.isImage() == true {
                let url = URL(string: linkItem!.url)
                url!.getImageData(from: url!) { (data, response, error) in
                    guard let data = data, error == nil else { return }
                    DispatchQueue.main.async() { [self] in
                        imageView?.image = UIImage(data: data)
                    }
                }
            }
        }
    }
}

下面是我的 ViewController:

的代码
import UIKit

class LinkCollectionViewController : UIViewController, UICollectionViewDelegate,UICollectionViewDataSource {
    
    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var collectionNameLabel: UILabel!
    @IBOutlet weak var collectionSubTitleLabel: UILabel!
    @IBOutlet weak var collectionCreatorLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let backButton = UIBarButtonItem()
        backButton.title = "Inbox"
        self.navigationController?.navigationBar.topItem?.backBarButtonItem = backButton

        collectionNameLabel.text = airlockStore.selectedChannel.channelName
        collectionSubTitleLabel.text = airlockStore.selectedChannel.channelDescription
        collectionCreatorLabel.text = airlockStore.selectedChannel.creator
        
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        collectionView.alwaysBounceVertical = true
        collectionView.register(UINib(nibName: "LinkImageItemCell", bundle: nil), forCellWithReuseIdentifier: "kImageCollectionViewCell")
        collectionView.register(UINib(nibName: "LinkItemLinkPreviewCell", bundle: nil), forCellWithReuseIdentifier: "kLinkPreviewCollectionViewCell")
        collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
        collectionView.reloadData()

        NotificationCenter.default.addObserver(self, selector: #selector(self.updateLinkCollection), name: Notification.Name("Link_Collection_Updated"), object: nil)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        self.navigationController?.setNavigationBarHidden(false, animated: animated)
    }
    
    @objc func updateLinkCollection() {
        collectionView.reloadData()
        collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems)
    }
    
    // MARK: - UICollectionViewDataSource Delegate

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return airlockStore.selectedChannel.linkPosts.count
    }
    
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }
    
    // MARK: - UICollectionViewDelegate
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        //Image
        if airlockStore.selectedChannel.linkPosts.reversed()[indexPath.item].url.isImage() == true {
            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LinkItemImageCell.identifier, for: indexPath) as? LinkItemImageCell
                else { preconditionFailure("Failed to load collection view cell") }
            cell.linkItem = airlockStore.selectedChannel.linkPosts.reversed()[indexPath.item]
            return cell
        }
        
        //URL
        else {
            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LinkItemLinkPreviewCell.identifier, for: indexPath) as? LinkItemLinkPreviewCell
                else { preconditionFailure("Failed to load collection view cell") }
            cell.linkItem = airlockStore.selectedChannel.linkPosts.reversed()[indexPath.item]
            return cell
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        self.performSegue(withIdentifier: "GoToLinkBlockViewController", sender: self)
        airlockStore.selectedLinkBlock = airlockStore.selectedChannel.linkPosts.reversed()[indexPath.item]
        
        guard let cell: LinkItemLinkPreviewCell = collectionView.cellForItem(at: indexPath)! as? LinkItemLinkPreviewCell else {
            return
        }
        
        airlockStore.selectedLinkBlock.urlMetadata = cell.urlMetadata

    }
    
}

我注意到带有 LPLinkView 的单元格显示数据的速度非常慢,而且滚动 UICollectionView 时会发生很多刷新。

感谢任何关于我如何提高性能的想法或指导;真的只是试图在我滚动时不重新加载单元格的 images/URL。

我没有时间研究你的代码,所以我的回答很笼统:

当您的 table view/collection 视图显示必须通过网络获取的数据时,将该数据加载到您的数据模型中,而不是单元格中。当网络加载完成并且模型中的条目更新时,告诉相应的单元格重新加载。

如果您将数据加载到单元格中,滚动时,之前加载的数据将会丢失,如果向后滚动,则必须重新加载。理想情况下,将加载的数据保存到缓存目录中的文件中,这样如果用户关闭视图控制器然后重新打开它,它仍然可用。

如果您将下载的数据安装到您的模型中,那么当您滚动并向后滚动时,单元格将在显示后立即正确呈现。