Thường thì khi lập trình TableView trong iOS, chúng ta hay lấy ViewController làm data source cho TableView luôn. Đây không phải là một cách hay bởi vì:
1.Nó sẽ "đính" TableView vào với Controller.
Về mặt lý thuyết thì việc này là hoàn toàn bình thường, nhưng thử đặt vào trường hợp bạn đang code một màn hình động và bạn có một TableView hiển thị nhiều thứ khác nhau. Nhưng nếu bạn muốn chuyển đến một view khác, một ViewController khác nhưng bạn cũng muốn TableView hoạt động như cũ?. Câu trả lời chắc chắn ko phải là copy paste code từ ViewController này sang cái khác rồi.
2. Cùng một logic cho nhiều ViewController khác nhau.
Nếu bạn có một tá những TableView với cùng một kiểu dữ liệu, nhưng phân bố ở nhiều ViewController khác nhau và bạn sẽ mất rất nhiều thời gian để tùy chỉnh dữ liệu. Hãy nghĩ đến việc cục bộ hóa chúng.
3. Rất nhiều code thừa
Nếu có một thứ khiến tôi phải chán ghét, thì đó là việc viết những dòng code mà tôi đã từng phải viết hàng trăm lần trước đó. Nhưng bạn hoàn toàn có thể tạo một mẫu TableViewDataSource chung cho toàn bộ TableView trong project của bạn.
Vâỵ khi tôi nhắc đến việc tạo một class UITableViewDataSource mà chỉ cần cung cấp dự liệu vào, nó sẽ tự động làm mọi thứ còn lại cho chúng ta thì sao?. Nghe rất thú vị phải không, vậy hãy cùng bắt tay vào làm nhé !

Chúng ta sẽ tạo ra một view Setting động cho ứng dụng. View Setting thường có nhiều segue trỏ tới nhiều View Controller giống nhau, và có chung mẫu Table View. Để cho DataSource của chúng ta dễ dàng tái sử dụng và tùy chỉnh, chúng ta sẽ tạo ra một enum đại diện dữ liệu của từng cell một.
enum SettingType {
case Switch(text: String) //on-off
case Segue(text: String) //navigate to a sub-section
case Info(text: String, detail: String) //e.g. usage stats
var identifier: String {
switch self {
case .Info: return "infoCell"
case .Segue: return "segueCell"
case .Switch: return "switchCell"
}
}
}
Ở đây có mọi thứ mà DataSource của chúng ta cần, bao gồm cell-reuse identifiers, ở đây tôi declare một computed variable theo từng case của enum. Chúng ta cũng tạo một struct để chứa những section trong TableView, và những đối tượng SettingType cần thiết.
struct SettingsSection {
var title: String?
var cellData: [SettingType]
init(title: String?, cellData: [SettingType]) {
self.title = title
self.cellData = cellData
}
}
SettingsTableViewDataSource
Lúc này chúng ta đã có thể tạo ra được data source, nó chỉ cần có một property là array chứa những Setting section.
class SettingsTableViewDataSource {
var sections: [SettingsSection]
init(sections: [SettingsSection]) {
self.sections = sections
}
}
Nhưng đây chưa phải là UITableViewDataSource của chúng ta, để tạo được nó, chúng ta cần phải subclass NSObject và thiết lập protocol UITableViewDataSource, cũng như những phương thức protocol cần phải có.
class SettingsTableViewDataSource: NSObject, UITableViewDataSource {
//... (above)
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].cellData.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].title
}
Cuối cùng chúng ta tạo phần khung chính cho data source:
class SettingsTableViewDataSource: NSObject, UITableViewDataSource {
//... (above)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let setting: SettingType = sections[indexPath.section].cellData[indexPath.row]
if let cell = tableView.dequeueReusableCellWithIdentifier(setting.identifier) {
switch setting {
case .Switch(text: let text):
cell.textLabel?.text = text
cell.accessoryType = .DetailButton
cell.accessoryView = UISwitch()
case .Segue(text: let text):
cell.textLabel?.text = text
cell.accessoryType = .DisclosureIndicator
case .Info(text: let text, detail: let detailText):
cell.textLabel?.text = text
cell.detailTextLabel?.text = detailText
}
return cell
} else {
fatalError("Unknown identifier")
}
}
}
Ở đây đơn giản chỉ là chúng ta lấy những kiểu SettingType và ném vào trong những thuộc tính của cell, sau đó trả về một cell hoàn chỉnh khớp với identifier.
Đây là tất cả những gì chúng ta cần cho UITableViewDataSource. Nếu bạn muốn tùy biến thêm tính năng, bạn có thể extend class này để thêm những method như thêm xóa dữ liệu, hoặc là hàm callback để gọi qua property observer của property sections do đó bạn có thể gọi tableView.reloadData() khi chúng thay đổi...vv.
Sử dụng DataSource
Bây giờ chúng ta đã có data source, hãy cùng tạo view Setting, và chúng ta chỉ cần làm với 6 dòng code bên trong ViewController thôi.
Đây là dữ liệu chúng ta cung cấp cho phía Model:
struct Model {
static let data = [
SettingsSection(title: "General",
cellData: [SettingType.Switch(text: "Dark mode"),
SettingType.Switch(text: "Auto save"),
SettingType.Segue(text: "Language")]),
SettingsSection(title: "Stats",
cellData: [SettingType.Info(text: "Usage", detail: "2 days")])
]
}
Ở đây tôi sử dụng Interface Builder để thêm vào một TableView với 3 kiểu prootype cell tương ứng với identifier chúng ta đã tạo ra trước đó. Style của info cell là Right Detail, còn lại là kiểu Basic

Và đây là ViewController:
class MainSettingsViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
let dataSource = SettingsTableViewDataSource(sections: Model.data)
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = dataSource
}
}
Và chúng ta có kết quả:

Bây giờ bạn đã có một ViewController gọn gàng hơn rất nhiều và một DataSource riêng rẽ để bạn tha hồ tùy chỉnh, thay đổi theo ý của mình.
Một điều hay nữa của data source này là bạn có thể yêu cầu nó cung cấp một đối tượng SettingType và thêm logic chi tiết cho từng cell một mà không cần biết nó là cell gì và thêm kiểu cell khác dễ dàng.
Nguồn bài viết : Tham khảo tại đây
Khóa học lập trình di động tại Techmaster:
Để cài đặt MacOSX lên phần cứng không phải Apple liên hệ chuyên gia cài Hackintosh:
- Nguyễn Minh Sơn: 01287065634
- Huỳnh Minh Sơn: 0936225565
- Website: caidatmacos.com
Bình luận