IOS Stretchy Headers TableView with MVC
How to Make it Clear and Enjoy Your Code
Hôm nay mình sẽ hướng dẫn các bạn xây dựng ứng dụng đơn giản trong IOS sử dụng Xcode 8 và Swift 3. Mục đích của bài viết này sẽ giúp các bạn
- Cài đặt và custom UITableViewCell sử dụng Xib file
- Áp dụng mô hình MVC
- Mask views sử dụng Core Animation
- Autolayout cơ bản
Sản phẩm cuối cùng trông sẽ giống như vậy.
Cấu trúc project của chúng ta sẽ gồm có:
- SHTableViewController: subclass của UIViewController và chứa UITableView
- SHTableViewDataModelItem: lưu trữ dữ liệu và hiển thị lên SHTableViewCell
- SHTableViewDataModel: khởi tạo dữ liệu(nhận dữ liệu từ API) và trả dữ liệu về cho SHTableViewController sử dụng Delegation
- SHTableViewCell: subclass của UITableViewCell
Đầu tiên chúng ta hãy bắt đầu với SHTableViewCell
Phần 1: TableViewCell
Chúng ta hãy bắt đầu bằng cách tạo project mới trong Xcode chọn “Single View Application” và xoá file ViewController.swift mặc định Xcode đã tạo ra sẵn đi, chúng ta sẽ tạo những file cần thiết sau.
Đầu tiên tạo một UITableViewCell subclass và check vào “Also create XIB file”
Với ví dụ này, Mỗi Cell chỉ hiển thị 2 thông tin là Category và Summary. Vậy đơn giản chỉ cần 2 UILabel, chúng ta sẽ làm như sau.
Add constrain cho view
Tạo outlet cho 2 label trong SHTableViewCell
Tiếp theo, viết cấu trúc dữ liệu cho SHTableViewDataModelItem để hiển thị dữ liệu lên Cell
Từ đó ta viết function configureWithItem hiển thị dữ liệu trong SHTableViewCell
Vậy là đã xong việc cấu hình cho Cell trên Tableview, Tiếp đến chúng ta sẽ tạo TableView
Phần 2: TableView
Đầu tiên, chúng ta tạo một UIViewController subclass:
Tiếp theo, chọn Main.storyboard, ở tab Identity inspector ta gán SHTableViewControler vào custom class
Add UITableView vào storyboard
Cuối cùng, add outlet của tableview vào SHTableViewControler
Chúng ta sẽ tạo ra một mảng SHTableViewDataModelItem để lưu trữ dữ liệu và hiển thị lên tableview
Bây giờ trong viewDidLoad của SHTableViewControler ta cần thực hiện việc load UITableViewCell from Xib files. Thông thường chúng ta sẽ dùng như sau
tableview?.register(nib: UINib?, forCellReuseIdentifier: String)
Thay vì phải “hard-typing the cell identifier” ta sẽ tạo Nib và ReuseIdentifier trong SHTableViewCell
Vậy là xong, ta quay trở lại SHTableViewController. Phương thức registerNib sẽ trở lên đơn giản hơn rất nhiều
tableview?.register(SHTableViewCell.nib, forCellReuseIdentifier: SHTableViewCell.identifier)
Và đừng quên gán delegate và dataSource cho tableview
tableview?.delegate = self
tableview?.dataSource = self
Nhưng chúng ta vẫn chưa xong, Xcode sẽ báo lỗi như sau “Cannot assign value of type SHTableViewController to type UITableViewDelegate”. Để giải quyết vấn đề này, ta thêm 2 extensions vào SHTableViewController
extension SHTableViewController: UITableViewDelegate {}extension SHTableViewController: UITableViewDataSource {}
Sau đó bạn sẽ lại thấy báo thêm một lỗi khác là: “Type SHTableViewController does not conform to protocol UITableViewDataSource”. Lỗi này là do có một vài phương thức yêu cầu bạn phải implement bên trong extension:
func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {}func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {}
Mình sẽ đi nhanh qua phần này, đây sẽ là code đầy đủ của 2 phương thức trên
Vậy là xong, chúng ta đi đến phần cuối: tạo và kết nối DataSource tới Tableview
Phần 3: DataModel
Tạo SHDataModel class.
Trong class này chúng ta sẽ request data từ JSON file hoặc sử dụng HTTP request hay đơn giản hơn là từ “local datasource file”. Trong bài viết này mình sẽ sử dụng “local datasource file” để tạo ra dữ liệu test.
Vậy làm sao để chuyển mảng dữ liệu này lên TableView?
Phần 4: Delegate
Đầu tiên, ta tạo ra một delegate protocol SHTableViewDataModelDelegate bên trong SHTableViewDataModel.swift
protocol SHTableViewDataModelDelegate: class {}
Bên trong protocol ta tạo ra một phương thức
protocol SHTableViewDataModelDelegate: class{func didRecieveDataUpdate(data: [SHTableViewDataModelItem])}
Tiếp theo, ta thêm một optional weak property bên trong SHTableViewDataModel class
weak var delegate: SHTableViewDataModelDelegate?
Sau đây là full code cho class này
Bây giờ chúng ta đã có thể pass data đến tableview
Phần 5: Display Data
Đầu tiên chúng ta phải tạo reference cho DataModel bên trong SHTableViewController:
private let datasource = SHTableViewDataModel()
Tiếp theo, để request data từ Model trong ViewWillAppear chúng ta sẽ viết như sau
override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(true)
dataSource.requestData()
}
Sau đó gán delegate cho dataSource trong ViewDidLoad
dataSource.delegate = self
Một lần nữa, bạn sẽ thấy Xcode báo lỗi bởi vì SHTableViewController không hề kế thừa từ SHTableViewDataModelDelegate, vì vậy chúng ta cần thêm vào đoạn code sau
Vậy là xong, sau đây là full code của SHTableViewController để giúp các bạn dễ hình dung
Kết quả sau khi chúng ta Build và run
Phần 6: Building the table header
Bây giờ chúng ta sẽ tạo header view cho tableview, các bạn làm như sau
Build và run các bạn sẽ thấy được như hình
Để cố định chiều cao cho headerview chúng ta sẽ define ra 1
private let kTableHeaderHeight: CGFloat = 300.0
Chúng ta không thể sử dụng thuộc tính tableHeaderView của TableView để quản lý table header bởi vì Tableview quản lý frame của table header vì vậy chúng ta sẽ custom lại header của tableview
var headerView: UIView!
Tiếp theo trong viewDidLoad chúng ta sẽ viết như sau
override func viewDidLoad() {
......headerView = tableview.tableHeaderViewtableview.tableHeaderView = niltableview.addSubview(headerView).......}
Bây giờ chúng ta sẽ define một hằng số cho chiều cao chúng ta sẽ cắt bỏ đi của header view
private let kTableHeaderCutAway: CGFloat = 80.0
Tiếp theo chúng ta sẽ thêm CAShapeLayer cho việc masking layer
var headerMaskLayer: CAShapeLayer!
Trong viewDidLoad chúng ta sẽ viết
override func viewDidLoad() {........
headerMaskLayer = CAShapeLayer() headerMaskLayer.fillColor = UIColor.black.cgColor headerView.layer.mask = headerMaskLayer
}
Tiếp theo chúng ta sẽ tạo một UIBezierPath để xác định vùng trên header view sẽ bị cắt
Trong hàm updateHeaderView()
Kết quả cuối cùng
Sau đây là full source của SHTableViewController cho các bạn dễ hình dung
Bài viết mình có sử dụng resource của
http://blog.matthewcheok.com/design-teardown-stretchy-headers/