I'm trying to create my own Book app, and using UICollectionView
for listing all the books. Data for each cell is from .plist
file, and I'm using custom flowLayout
(to make some changes later).
So now I'm stucked with delays and lags when scrolling. I suppose I've made mistakes with passing data to cell or with cell initializing.
Cell created by .xib
and custom class, just some layout and UI:
class BookCoverCell: UICollectionViewCell {
@IBOutlet weak var view1: UIView!
@IBOutlet weak var view2: UIView!
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var darkRedView: UIView!
@IBOutlet weak var lightRedView: UIView!
@IBOutlet weak var readButton: UIButton!
@IBOutlet weak var pageLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
clipsToBounds = true
self.backgroundColor = UIColor(white: 1, alpha: 0.0)
view1.layer.cornerRadius = 10
view2.layer.cornerRadius = 10
darkRedView.layer.cornerRadius = 10
lightRedView.layer.cornerRadius = 10
}
}
At ViewController's class:
class MainVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UIScrollViewDelegate {
@IBOutlet weak var collectionContainerView: UIView!
@IBOutlet weak var collectionView: UICollectionView!
var books: Array<Book>? {
didSet {
collectionView.reloadData()
}
}
var flowLayout = UICollectionViewFlowLayout()
override func viewDidLayoutSubviews() {
flowLayout = ZoomAndSnapFlowLayout()
collectionView.collectionViewLayout = flowLayout
}
override func viewDidLoad() {
super.viewDidLoad()
collectionContainerView.backgroundColor = UIColor(white: 1, alpha: 0.0)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UINib(nibName: "BookCoverCell", bundle: nil), forCellWithReuseIdentifier: "BookCoverCell");
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
books = BookStore.sharedInstance.loadBooks(plist: "Books")
}
//MARK: UICollectionViewDelegate
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let books = books {
return books.count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BookCoverCell", for: indexPath) as! BookCoverCell
let book = books![indexPath.row]
let cover = book.coverImage()!
let color = book.getDominantColor()
cell.view1.backgroundColor = color
cell.imageView.image = cover
cell.pageLabel.text = "Pages: 29"
cell.readButton.setTitle("Read", for: .normal)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let book = books?[indexPath.row]
print ("open")
}
}
And my layout now looks like:
class ZoomAndSnapFlowLayout: UICollectionViewFlowLayout {
var cellWidth = CGFloat()
var cellHeight = CGFloat()
var minLineSpacing = CGFloat()
override init() {
super.init()
self.scrollDirection = .horizontal
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepare() {
guard let collectionView = collectionView else { fatalError() }
cellHeight = collectionView.frame.height * 0.8
minLineSpacing = 200
cellWidth = cellHeight
itemSize = CGSize(width: cellWidth, height: cellHeight)
let verticalInsets = (collectionView.frame.height - collectionView.adjustedContentInset.top - collectionView.adjustedContentInset.bottom - itemSize.height) / 2
let horizontalInsets = (collectionView.frame.width - collectionView.adjustedContentInset.right - collectionView.adjustedContentInset.left - itemSize.width) / 2
sectionInset = UIEdgeInsets(top: verticalInsets, left: horizontalInsets, bottom: verticalInsets, right: horizontalInsets)
super.prepare()
}
}
So I'm pretty sure that have some mistakes in my code, but can't find them(( Any suggestion will be helpful for me!
UPDATE: So first of all thanks! I've changed my code and replace a really big image with smallest one, and function to get dominant color simply to white color for now, and things already gets better!
BUT there is small lag or delay when first scroll begins, and only with 1st and 2nd cells, while they are scrolling to the left edge of the screen. And after that all cells scrolling without lags, even first two, in both directions (left / right).
Now my code looks like:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BookCoverCell", for: indexPath) as! BookCoverCell
let book = books![indexPath.row]
let cover = UIImage(named: "flag.png")
let color = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
cell.view1.backgroundColor = color
cell.imageView.image = cover
cell.pageLabel.text = "Pages: 29"
cell.readButton.setTitle("Read", for: .normal)
return cell
}
and UPDATE #2 removing
flowLayout = ZoomAndSnapFlowLayout()
collectionView.collectionViewLayout = flowLayout
to viewWillAppear()
fix these small lags too!! Hooray!)
Read this article about Time Profiling and collection view: https://voxels.github.io/eliminating-collection-view-tearing-with-xcode-time-profiler-instrument
Since your implementation is very simple, there aren't many things that could be wrong, but you probably have the same issue that the author of the article had -- the images themselves are very large, and need to be read and resized.
They solved the issue by making appropriately sized versions of the images.
In your case getDominantColor
will also be slower on large images (I am assuming that it reads the pixels to get the dominant color. You should also consider caching this color and not recalculating it every time (if you are not already doing that).