UICollectionView DataSourcePrefetching Example and Explanation
Share:
Free JavaScript Book!
Write powerful, clean and maintainable JavaScript.RRP $11.95
UICollectionView DataSourcePrefetching
Recently, Apple announced a new UICollectionViewDataSource
protocol extension called UICollectionViewDataSourcePrefetching
. This extension makes it possible to implement two new functions that deliver a completely smooth scrolling performance. This tutorial will show you how to use it.
You can find the source code on GitHub.
Setting up the Xcode project
Open Xcode and create a new project based on the Single View Application template. Before you do anything else, in order to improve the appearance of our application ,select the view and choose Editor -> Embed In -> Navigation Controller. Now drag a Collection View
from the Object Library onto the view canvas and adjust its size .
Notice that into the Collection View
is one reusable cell
, so click under the Attributes Inspector and give this cell an Identifier, let’s call it foodCell
. Drag an UIImageView
onto it and adjust its size to fit the content.
Create a new file, of type Cocoa Touch Class , subclass of UICollectionViewCell
, name it collectionViewCell
and assign it to the cell
.
Connect the UIImageView
to the collectionViewCell.swift code and name it ‘foodImage‘. Also , connect the collectionView
to ViewController.swift.
Conforming to protocols and writing some lines
Now , it’s time to write some code in viewController.swift.
The first thing to do is to conform the class to UICollectionViewDelegate
,UICollectionViewDataSource
, and UICollectionViewDataSourcePrefetching
protocols.
You should probably see some errors showing up in your sidebar, due to required methods not being implemented yet. We’ll cover that soon, but let’s finish a little setup first.
We’ll need a data source for this UICollectionView
, so let’s just create an array called imageArray
, which for illustrative purposes will store 30 images. Outside of any method, create the array like so:
var imageArray = [UIImage?](repeating: nil, count: 30)
and a variable to store the base url of a picture like so :
var baseUrl = URL(string: "https://placehold.it")!
Same as above create another array of type URLSessionDataTask
:
var tasks = [URLSessionDataTask?](repeating: nil, count: 30)
The next step is to create two separated functions in order to generate images from dynamically generated urls. The first function requires a parameter which in our case will be the index of each cell
and will return an url.
func urlComponents(index: Int) -> URL {
var baseUrlComponents = URLComponents(url: baseUrl, resolvingAgainstBaseURL: true)
baseUrlComponents?.path = "/\(screenSize.width)x\(screenSize.height * 0.3)"
baseUrlComponents?.query = "text=food \(index)"
return (baseUrlComponents?.url)!
}
The second one is where the downloading process will be executed and it requires the indexPath
of each cell
as a parameter. The return type of this function will be URLSessionDataTask
.
func getTask(forIndex: IndexPath) -> URLSessionDataTask {
let imgURL = urlComponents(index: forIndex.row)
return URLSession.shared.dataTask(with: imgURL) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
let image = UIImage(data: data)!
self.imageArray[forIndex.row] = image
self.collectionView.reloadItems(at: [forIndex])
}
}
}
Make sure you have added these lines in viewDidLoad()
method:
collectionView.dataSource = self
collectionView.delegate = self
collectionView.prefetchDataSource = self
Implementing required methods
Now , we must implement 3 required methods :
collectionView(_:numberOfItemsInSection:)
collectionView(_:cellForItemAt:)
collectionView(_:prefetchItemsAt:)
UICollectionView
must know how many rows are going to be inside the section, so we’ll return the count of the elements in our array here:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
Before coming to the most important methods, there is only one more thing left to do. Let’s create a method where we can supervise the downloading process.
Once again the parameter required is the indexPath
of the cell
.
func requestImage(forIndex: IndexPath) {
var task: URLSessionDataTask
if imageArray[forIndex.row] != nil {
// Image is already loaded
return
}
if tasks[forIndex.row] != nil
&& tasks[forIndex.row]!.state == URLSessionTask.State.running {
// Wait for task to finish
return
}
task = getTask(forIndex: forIndex)
tasks[forIndex.row] = task
task.resume()
}
}
Finally, is time to build and return each cell
. So in this method :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let foodCell = collectionView.dequeueReusableCell(withReuseIdentifier: "foodCell", for: indexPath) as! collectionViewCell
if let img = imageArray[indexPath.row] {
foodCell.foodImage.image = img
}
else {
requestImage(forIndex: indexPath)
}
return foodCell
}
As we see the requestImage(forIndex: IndexPath)
method helps us to observe if an image is already downloaded,is in process or is not downloaded at all.
Prefetching
In order for your app to have “buttery smooth” performance, you must strive for app animation that performs at 60 frames per second. This means that a given frame of the user interface must be displayed in less than 16.67ms in order for the animation to appear “smooth.” Otherwise, when the frame rate drops lower than that, it’s apparent to the user in the form of a choppy animation.
Prefetching is a mechanism by which you are notified early before the collection view will need the cells to be displayed, thus providing an opportunity to prepare the cell’s data source in advance of actually creating the cell. Prefetching is provided by the UICollectionViewDataSourcePrefetching
protocol and contains two instance functions:
public protocol UICollectionViewDataSourcePrefetching : NSObjectProtocol {
// indexPaths are ordered ascending by geometric distance from the collection view
@available(iOS 10.0, *)
public func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath])
// indexPaths that previously were considered as candidates for pre-fetching, but were not actually used; may be a subset of the previous call to -collectionView:prefetchItemsAtIndexPaths:
@available(iOS 10.0, *)
optional public func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath])
}
Implementing DataSource Prefetching
The first and required method is called when the Collection View
is ready to start building out cells even before they are on the screen. An array of NSIndexPath
objects is passed to the function and is used to prepare the data source. In our code we would write something like this:
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths{
requestImage(forIndex: indexPath)
}
}
Implementing Prefetch Cancellation
As mentioned above, the second method, collectionView(_:cancelPrefetchingForItemsAt:)
, is an optional feature that allows you to revert or clean up your data source when prefetching for an array of cells has been cancelled by the Collection View
. This can happen when scrolling changes direction or becomes too rapid for prefetching to be implemented effectively. So let’s implement the last function in our code:
func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths{
if let task = tasks[indexPath.row] {
if task.state != URLSessionTask.State.canceling {
task.cancel()
}
}
}
}
There may be times when you want to disable collection view prefetching. This can be done by setting the UICollectionView isPrefetchingEnabled
property to false
.
These new functions can also be used in UITableView
as well just by implementing UITableViewDataSourcePrefetching
protocol.
Conclusion
Using this protocol and its functions is a good choice . There’s no more need to worry about the performance of cells as they are ready to come to the screen before being seen and with a smooth scrolling performance, our application feels better.
New books out now!
Get practical advice to start your career in programming!
Master complex transitions, transformations and animations in CSS!
Latest Remote Jobs




