Is there a simple way to separate some common tasks, such as the UITableViewDataSource protocol? Enter "Lighter View Controllers,” a concept by Chris Edihof.
In the article, Chris describes a nice way to separate your UITableViewDataSource. You can apply this to UICollectionViewDataSource or any other common protocol. This can significantly clean up your view controller’s code base and simplify your development process.
The concept is simple: create a reusable NSObject subclass to handle any protocol that you’d like to separate out. From there, it’s just as simple as setting the datasource to point to the new class. In the case of UITableViewDatasource and UICollectionViewDatasource, you’ll also use a block (or closure in Swift) to handle the configuring of the cell. This can be reused in all of your view controllers that need a data source. The article is written in Objective-C, so what might this look like in Swift? Let’s find out.
Lighter View Controller (UICollectionViewDatasource) in Swift
CollectionViewDataSource
import Foundation
typealias CollectionViewCellConfigureBlock = (cell:UICollectionViewCell, item:AnyObject?) -> ()
class CollectionViewDataSource: NSObject, UICollectionViewDataSource {
var items:NSArray = []
var itemIdentifier:String?
var configureCellBlock:CollectionViewCellConfigureBlock?
init(items: NSArray, cellIdentifier: String, configureBlock: CollectionViewCellConfigureBlock) {
self.items = items
self.itemIdentifier = cellIdentifier
self.configureCellBlock = configureBlock
super.init()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(self.itemIdentifier!, forIndexPath: indexPath) as UICollectionViewCell
let item: AnyObject = self.itemAtIndexPath(indexPath)
if (self.configureCellBlock != nil) {
self.configureCellBlock!(cell: cell, item: item)
}
return cell
}
func itemAtIndexPath(indexPath: NSIndexPath) -> AnyObject {
return self.items[indexPath.row]
}
}
CustomUICollectionViewCell
import UIKit
class CustomUICollectionViewCell: UICollectionViewCell {
//Example cell configuration
func configureForItem(item:AnyObject) {
//do something with the cell...
}
}
ViewController
import UIKit
class ViewController: UIViewController {
//variable to hold reference to the datasource
var dataSource:CollectionViewDataSource?
override func viewDidLoad() {
super.viewDidLoad()
//Init our datasource and setup the closure to handle our cell
//modify 'AnyObject' to match your model
self.dataSource = CollectionViewDataSource(items: self.items, cellIdentifier: "Cell", configureBlock: { (cell, item) -> () in
if let actualCell = cell as? CustomUICollectionViewCell {
if let actualItem = item as? AnyObject {
actualCell.configureForItem(actualItem)
}
}
})
//finally, set the collectionview datasource
self.collectionView.dataSource = self.dataSource
}
}
As you can see above, this can really reduce the amount of code stuffed inside of your view controllers.
Questions or comments? Follow me on Twitter.