由 LPLinkView 和 UIImageView 組成的單元格的 CollectionView 速度很慢,並且在滾動時會重新加載數據 (CollectionView with Cells made up of LPLinkView's and UIImageView's is Slow and Reloads Data While Scrolling)


問題描述

由 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.

任何關於我如何在這裡提高性能的想法或指導表示讚賞;真的只是想在我滾動時不重新加載單元格的圖像/URL。


參考解法

方法 1:

I don't have time to work through your code, so my answer is going to be general:

When your table view/collection view displays data that has to be fetched over the network, load that data into your data model, not into cells. When the network load completes and that entry in the model is updated, tell the corresponding cell to reload.

If you load data into your cells, as you scroll, the data you loaded before will be lost, and if you scroll back, you'll have to load it again. Ideally, save you loaded data to files in the caches directory so it will still be available if the user closes the view controller and then reopens it.

If you install downloaded data into your model then when you scroll away and scroll back the cell will render correctly as soon as it's displayed.

(by narnerDuncan C)

參考文件

  1. CollectionView with Cells made up of LPLinkView's and UIImageView's is Slow and Reloads Data While Scrolling (CC BY‑SA 2.5/3.0/4.0)

#uicollectionviewcell #iOS #swift #uicollectionview






相關問題

Sel UICollectionView tidak terlihat (UICollectionView cells are invisible)

UICollectionViewCell裡面的UIWebView不調用Delegate方法 (UIWebView inside UICollectionViewCell does not call Delegate Method)

Bộ sưu tập xem vấn đề thay đổi kích thước iPhone 4 và iPhone 5 (Collection view resizing issue iPhone 4 vs iPhone 5)

集合視圖單元格未出現 (Collection View Cells not appearing)

UICollectionView 單元格大小 ContentView 問題 (UICollectionView cell size ContentView Issue)

在 Swift 中使用 CollectionView 進行網格佈局 (Grid layout with CollectionView in Swift)

如何為collectionviewcell的刪除自定義動畫? (How to custom animation for collectionviewcell's deletion?)

檢測位於另一個 uicollectionview 內的 uicollectionView 的哪個 UICollectionViewCell 在中心 (Detect which UICollectionViewCell of a uicollectionView that is inside another uicollectionview is in the center)

navigationController?.pushViewController 不工作 (navigationController?.pushViewController is not working)

XCode UICollectionViewCell 在模擬器上看起來比在故事板中小得多 (XCode UICollectionViewCell appearing much smaller on simulator than in storyboard)

CollectionViewCell 未出現在 collectionView 中 (CollectionViewCell not appearing in collectionView)

由 LPLinkView 和 UIImageView 組成的單元格的 CollectionView 速度很慢,並且在滾動時會重新加載數據 (CollectionView with Cells made up of LPLinkView's and UIImageView's is Slow and Reloads Data While Scrolling)







留言討論