Reputation: 10744
Keeping ViewControllers thin and using MVVMC has helped me maintain so much easier. Obj.io has a very good tutorial on their site. Unfortunately, the tutorial is only in Objective-C.
I'm trying to switch over to Swift and was moving very swiftly, until I reached the block that's used to configure the cell.
In the tutorial they created a block typedef
:
typedef void (^TableViewCellConfigureBlock)(id cell, id item);
that returns the cell in cellForRowAtIndexPath
upon creating the cell.
Below is some of the code and here is the entire project: Project
void (^configureCell)(PhotoCell*, Photo*) = ^(PhotoCell* cell, Photo* photo) {
cell.label.text = photo.name;
};
photosArrayDataSource = [[ArrayDataSource alloc] initWithItems:photos
cellIdentifier:PhotoCellIdentifier
configureCellBlock:configureCell];
self.tableView.dataSource = photosArrayDataSource;
cellForRowAtIndexPath
:
- (UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
id cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier
forIndexPath:indexPath];
id item = [self itemAtIndexPath:indexPath];
configureCellBlock(cell,item);
return cell;
}
Question: How can I accomplish the same in Swift 1.2+? Should I use a closure
, protocol delegate
, etc.? And more importantly, how do I call it in cellForRowAtIndexPath
?
I'm new to Swift so solution code would be greatly appreciated. Thanks.
Upvotes: 2
Views: 787
Reputation: 1554
In Swift 2.0, you can use generics (as seen here - sample github repo), that's easy.
But in Swift 1.2 you need to drop the generics (can't make generic class inherit from objc type). Use protocols instead. This way you don't need to pass the cell configuration closure and use the power of protocols.
You'd go something like this:
protocol CellDescription {
var title: String { get }
var image: UIImage { get }
}
Your models which you wanna show in the tableview then need to implement the protocol
class Question: CellDescription {
var image: Image {
return UIImage(named: "whatever")!
}
var title: String {
return "cell title"
}
}
You can then have UITableViewController
with property items: [CellDescription]
and create it like this:
class CustomTableViewController: UITableViewController {
var items: [CellDescription] = []
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(pickerCell, forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.item].title
cell.imageView?.image = items[indexPath.item].image
return cell
}
}
Upvotes: 1
Reputation: 3905
You can use the swift closures
here. Define closureType as,
typealias TableViewCellConfigureBlock = (cell : AnyObject , item : AnyObject) -> Void
Then define the closure
like,
let configureCellBlock : TableViewCellConfigureBlock = { (cell : PhotoCell, photo : Photo) in
cell.label.text = photo.name
} as TableViewCellConfigureBlock
Then in cellForRowAtIndexPath:
you can use it like as before,
configureCellBlock(cell: yourCell, item: yourPhotoItem)
Upvotes: 4
Reputation: 4337
Closures are what you use instead of blocks. You implement the protocol in Swift just as you would in Objective-C. It's necessary to inherit from NSObject to conform to this protocol. Generics would be nice here, but as of Swift 1.2 they can't be used for interop with Objective-C.
class ArrayDataSource : NSObject, UITableViewDataSource {
let array: [AnyObject]
let identifier: String
let configureCell: (UITableViewCell, AnyObject) -> ()
init(array: [AnyObject], identifier: String, configureCell: (UITableViewCell, AnyObject) -> ()) {
self.array = array
self.identifier = identifier
self.configureCell = configureCell
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let item: AnyObject = array[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier(identifier) as! UITableViewCell
configureCell(cell, item)
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
}
let tableView = UITableView()
tableView.dataSource = ArrayDataSource(array: ["foo", "bar", "baz"], identifier: "foo", configureCell: { (cell, obj) -> () in
cell.textLabel?.text = "bar"
})
Upvotes: 1