Access class name using @objc attribute
Scenario
- Create a Simple Table View app
- Load customs cells each row
- Cell identifier is the class name if the object is TableCell cell identifier is "TableCell"
- Use common cell loading techniques
- Use helper method to load cell using @objc attribute
Create UITableViewCell subclass
class HeaderCell: UITableViewCell {
...
}
class ContentsCell: UITableViewCell {
...
}
class AboutCell: UITableViewCell {
...
}
Load cell in Tableview in traditional way
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as? HeaderCell
return cell
}
if indexPath.row == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContentsCell", for: indexPath) as? ContentsCell
return cell
}
if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "AboutCell", for: indexPath) as? AboutCell
return cell
}
return UITableViewCell()
}
So whats @objc attribute?
When you apply it to a class or method it instructs Swift to make those things available to Objective-C as well as Swift code.
Most of the time you see @objc
when you call a method from a UIBarButtonItem or UIButton, you’ll need to mark that method using @objc
so it’s exposed – both of those, and many others, are Objective-C code.
In this case, we will use @objc
to name our subclasses HeaderCell
, ContentsCell
, and AboutCell
in Swift
Update our UITableViewCell subclass
@objc(HeaderCell)
class HeaderCell: UITableViewCell {
...
}
@objc(ContentsCell)
class ContentsCell: UITableViewCell {
...
}
@objc(AboutCell)
class AboutCell: UITableViewCell {
...
}
Create a Helper method to take the cell identifier using the AnyClass type from the UITableViewCell subclass.
Note: Cell identifier should be the class name
func getCell(_ cls: AnyClass, _ index: IndexPath) -> AnyObject {
let identifier = NSStringFromClass(cls)
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: index)
return cell
}
Load cell in Tableview using new way
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = getCell(HeaderCell.self, indexPath) as! HeaderCell
return cell
}
if indexPath.row == 2 {
let cell = getCell(ContentsCell.self, indexPath) as! ContentsCell
return cell
}
if indexPath.row == 1 {
let cell = getCell(AboutCell.self, indexPath) as! AboutCell
return cell
}
return UITableViewCell()
}
Looks clean right?, so @objc
attribute is not just for methods, we use it to name our class as well.
Addition Tricks
Let's make it shorter, create an enum of rows called Row
enum Row: Int {
case HeaderCell = 0
case ContentsCell = 1
case AboutCell = 2
var className: AnyClass {
switch self {
case .Header:
return HeaderCell.self
case .Contents:
return ContentsCell.self
case .About:
return AboutCell.self
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = Row(rawValue: indexPath.row)
let cell = getCell(row.className, indexPath) // dynamic binding
return cell
}
That's it!
Thanks for your time