CollectionView с ячейками, состоящими из LPLinkView и UIImageView, работает медленно и перезагружает данные при прокрутке

#ios #swift #uicollectionview #uicollectionviewcell

Вопрос:

У меня есть 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 буквами » s » очень медленно отображают данные, а также то, что при прокрутке происходит много обновлений UICollectionView .

Любые идеи или рекомендации о том, как я мог бы повысить производительность, здесь приветствуются; на самом деле я просто пытаюсь не перезагружать изображения/URL-адреса ячеек при прокрутке.

Ответ №1:

У меня нет времени разбираться в вашем коде, поэтому мой ответ будет общим:

Когда в представлении таблицы/коллекции отображаются данные, которые необходимо извлечь по сети, загрузите эти данные в модель данных, а не в ячейки. Когда загрузка сети завершится и эта запись в модели будет обновлена, попросите соответствующую ячейку перезагрузиться.

Если вы загружаете данные в свои ячейки при прокрутке, данные, которые вы загрузили ранее, будут потеряны, а если вы прокрутите назад, вам придется загрузить их снова. В идеале сохраните загруженные данные в файлы в каталоге кэша, чтобы они по-прежнему были доступны, если пользователь закроет контроллер представления, а затем снова откроет его.

Если вы установите загруженные данные в свою модель, то при прокрутке и прокрутке назад ячейка будет отображаться правильно, как только она появится.