한 테이블 뷰에서 이미지를 포함한 셀, 텍스트만 포함하는 셀, 여러 버튼이 있는 셀 등 여러 셀을 다루다 보면
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.row]
let cell: UITableViewCell
if item.isPhotoItem {
cell = tableView.dequeueReusableCell(withIdentifier: photoCellIdentifier, for: indexPath)
// Configure photo cell...
} else if item.isVideoItem {
cell = tableView.dequeueReusableCell(withIdentifier: videoCellIdentifier, for: indexPath)
// Configure video cell...
} else {
cell = tableView.dequeueReusableCell(withIdentifier: textCellIdentifier, for: indexPath)
// Configure text cell...
}
return cell
}
이런식으로 tableView(_cellForRowAt) 안에 if-else 문이 매우 많아지고, ViewController 가 점점 비대해지고 가독성이 떨어집니다.
protocol TableCellController {
static func registerCell(on tableView: UITableView)
func cellFromTableView(_ tableView: UITableView, forIndexPath indexPath: IndexPath) -> UITableViewCell
func didSelectCell()
}
// cell 이 어떤 셀인지
protocol ListItem {
var isPhotoItem: Bool {get}
var isVideoItem: Bool {get}
var isTextItem: Bool {get}
}
ViewController를 가볍게 하고, 가독성을 좋게 하기 위해서는 cell이 스스로 일하게 해야합니다.
cell의 xib를 register, deque, UI 구성 등을 위의 protocol을 통해 cell이 직접 수행하도록 합니다.
위 프로토콜을 따르는 cell controller의 에는
class PhotoTableCellController: TableCellController {
fileprivate let item: ListItem
init(item: ListItem) {
self.item = item
}
fileprivate static var cellIdentifier: String {
return String(describing: type(of: PhotoTableViewCell.self))
}
static func registerCell(on tableView: UITableView) {
tableView.register(UINib(nibName: cellIdentifier, bundle: Bundle(for: PhotoTableViewCell.self)), forCellReuseIdentifier: cellIdentifier)
}
func cellFromTableView(_ tableView: UITableView, forIndexPath indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: type(of: self).cellIdentifier, for: indexPath) as! PhotoTableViewCell
// Configure photo cell...
return cell
}
func didSelectCell() {
// Do something for photo...
}
}
이런 cell controller를 사용할 셀 마다 정의 함으로써, view controller에서 하던 일을 가져올 수 있습니다.
tableView에 여러 종류의 셀을 사용하면, 그 셀의 종류의 갯수만큼, cell controller의 갯수도 늘어나는데
cellController1 = someCellController()
cellController2 = otherCellController()
cellController3 = differentCellController()
...
이런식으로 View Controller 내에서 하나씩 생성을 해주면 또 View Controller가 커지는 문제가 있고, 가독성이 떨어지기 때문에 Factory pattern을 통해 내가 사용하고 싶은 셀들의 Cell Controller를 한번에 생성하여 가져올 수 있습니다.
class MyCellControllerFactory {
// xib 등록
func registerCells(on tableView: UITableView) {
PhotoTableCellController.registerCell(on: tableView)
VideoTableCellController.registerCell(on: tableView)
TextTableCellController.registerCell(on: tableView)
}
func cellControllers(with items: [ListItem]) -> [TableCellController] {
// items에 맞는 Cell Controller를 모두 생성해서 리턴
return items.map { item in
if item.isPhotoItem {
return PhotoTableCellController(item: item)
} else if item.isVideoItem {
return VideoTableCellController(item: item)
} else {
return TextTableCellController(item: item)
}
}
}
}
class MyViewController: UITableViewController {
fileprivate var cellControllers = [TableCellController]()
fileprivate let cellControllerFactory = MyCellControllerFactory()
override func viewDidLoad() {
super.viewDidLoad()
cellControllerFactory.registerCells(on: tableView)
}
//...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return cellControllers[indexPath.row].cellFromTableView(tableView, forIndexPath: indexPath)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
cellControllers[indexPath.row].didSelectCell()
}
}
Factory pattern으로 cell controller를 모두 받아오고,
cell controller에 여러 역할을 맡김으로써 위처럼 View Controller는 매우 간결해진 것을 볼 수 있습니다.
참조: https://tech.busuu.com/dealing-with-different-kinds-of-cells-in-swift-part-1-of-3-18c6cd10a0b3