Cải thiện UI/UX loading data trên tableview
1. Tạo information view Information view(iv) nằm dưới tableview, chức năng của iv Khi vừa vào viewcontroller iv sẽ hiện lên đồn thời indicator view sẽ thực hiện animation. Nếu internet bị mất kết nối iv sẽ hiển thị thông tin cho người dùng biết kèm theo button cho phép reload data. Nếu ...
1. Tạo information view
Information view(iv) nằm dưới tableview, chức năng của iv
- Khi vừa vào viewcontroller iv sẽ hiện lên đồn thời indicator view sẽ thực hiện animation.
- Nếu internet bị mất kết nối iv sẽ hiển thị thông tin cho người dùng biết kèm theo button cho phép reload data.
- Nếu data trả về từ server không có thì cũng sẽ hiện thị tại đây cho người dùng biết
- Nếu data trả về từ server có dữ liệu thì iv sẽ bị ẩn đi
class InformationView: UIView { open var titleLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.backgroundColor = UIColor.clear label.font = UIFont.systemFont(ofSize: 16.0) label.textColor = UIColor.gray label.textAlignment = .center label.numberOfLines = 0 return label }() required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override init(frame: CGRect) { super.init(frame: frame) self.addSubview(titleLabel) self.setConstraints() } override func didMoveToSuperview() { } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let uiview = super.hitTest(point, with: event) if uiview == self { return nil } return uiview } func setConstraints() { let centerX = NSLayoutConstraint(item: titleLabel, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0) let centerY = NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0) NSLayoutConstraint.activate([centerX, centerY]) } }
2. Loading data trên tableview
loading data ở đây sẽ có 2 phần
- loading khi mới vào viewcontroller (loading ở viewDidLoad )
- loading thêm data khi scroll xuống dưới
var isNodata = false var isLoadingData = false var isNoMoreData = false
Nếu ngay trong lần đầu load data dữ liệu trả về là 0 thì trường hợp này isNodata = true, nếu dữ liệu trả về < limit (số lượng dữ liệu trên mỗi request) isNoMoreData = true ( không còn data để load thêm nữa), ngoài ra trong khi đang load dữ liệu isLoadingData để người dùng không thể tiến hành load dữ liệu được nữa. Vì ở đây mình demo bằng dữ liệu fake nên mình sẽ sử dụng hàm delay để trả về data thay vì gọi api lên server (fake mà)
func delay(time: TimeInterval, completionHandler: @escaping () -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + time) { completionHandler() } }
hàm load data
func loadData() { if isLoadingData { print("loading data") return } delay(time: 3.0) {[weak self] in guard let weakSelf = self else { return } if weakSelf.activityIndicator.isAnimating { weakSelf.activityIndicator.stopAnimating() } guard !weakSelf.isNodata else { self?.noDataView.isHidden = false self?.tableView.reloadData() return } for i in 0..<20 { weakSelf.array.append("(i)") } if weakSelf.array.count > 0 { weakSelf.noDataView.isHidden = true } weakSelf.tableView.reloadData() if weakSelf.refreshControl.isRefreshing { weakSelf.refreshControl.endRefreshing() } } }
3. Loading more data
Loading more data được thực hiện khi chúng ta kéo tableview xuống cuối cùng.
3.1 Loading more cell
Nằm ở cuối cùng có chức năng để người dùng biết đang load more data
hay là no more data.
3.2 Loading more data trên tableview
sử dụng delegate của UIScrollView, khi người dùng cuộn đến cuối tableview sẽ cho activity indicator chạy animation và thực hiện load more data
extension NoDataViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { if (scrollView.contentOffset.y == scrollView.contentSize.height - scrollView.frame.size.height) { print("load more data.") loadMoreData() } } }
trong đó hàm load more data
func loadMoreData() { if self.isLoadingData { print("loading data break.") return } if isNoMoreData { print("have no more data") return } isLoadingData = true delay(time: 3.0) {[weak self] in guard let weakSelf = self else { return } guard weakSelf.loadingMoreDataTime != 2 else { weakSelf.isNoMoreData = true weakSelf.tableView.reloadRows(at: [IndexPath(row: weakSelf.array.count, section: 0)], with: .none) weakSelf.isLoadingData = false return } for i in 0..<20 { weakSelf.array.append("(i)") weakSelf.tableView.beginUpdates() weakSelf.tableView.insertRows(at: [IndexPath(row: weakSelf.array.count - 1, section: 0)], with: .automatic) weakSelf.tableView.endUpdates() } weakSelf.isLoadingData = false weakSelf.loadingMoreDataTime += 1 } }
3.3 cell trên tableview
khi thêm cell loading more chúng ta sẽ thêm nếu data có dữ liệu
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return (array.count > 0 ? (array.count + 1) : array.count) } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let lastIndex = array.count if indexPath.row == lastIndex { let cell = tableView.dequeueReusableCell(withIdentifier: "LoadMoreCell", for: indexPath) as! LoadMoreCell if isNoMoreData { if cell.activityIndicator.isAnimating { cell.activityIndicator.stopAnimating() } cell.titleLabel.text = "No More Data." } else { cell.activityIndicator.startAnimating() cell.titleLabel.text = "" } return cell } let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = array[indexPath.row] return cell }