問題描述
由 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.