+1

TableView Trong TableView

Trong quá trình dựng giao diện cho ứng dụng của mình, đôi khi muốn hiển thị một tableview con nằm trong một màn hình lớn được thiết kế là một tableview hoặc là UITabliewController. Tuy nhiên nếu dùng giao diện kéo cả 2 tableview vào cùng một giao diện như thế sẽ gây hiểm lầm cho controller không phân biệt được đâu là delegate, datasource của tableview nào. Vì vậy phần này sẽ hướng dẫn cách lồng nhiều tableview vào trong một tableview và nhiều hơn nữa Kiến thức cơ bạn: sử dụng tableview, custom tableview, passing data sử dụng closure và tất nhiên là autolayout Step 1: Tổng quan về demo Trước tiên dựng giao diện trong main.storyboard. Với cách làm này thực chất giao diện chỉ có một UITableViewController. Chúng ta cần hình dung giao diện sau khi hoàn thành sẽ trông thế nào Bản chất của việc tạo giao diện này là tạo hàng loạt các custom Cell với mỗi cell là một phần giao diện cụ thể. Có thể thoạt đầu sẽ nghĩ tại sao với một giao diện như vậy lại không kéo thả mà phải code qua lại mất công rồi phải tạo custom cell. Tuy nhiên với bài toán trong màn hình của bạn có nhiều view phức tạp cần phải custom liên tục hoặc khi bạn có nhiều màn hình có giao diện tương tự nhau. Việc tạo nhiều custom cell sẽ có thể dùng lại được. Song không phải không có hạn chế, với bài toán đơn giản, ít cell phải custom thì việc này đúng là rất mất công. Vậy nên tùy bài toán cụ thể để áp dụng cho hợp lý Step 2: Xác định bài toán Xác định cần tạo những Cell giao diện nào. Trong bài toán này. Xác định sẽ có tổng cộng 4 loại cell khác nhau.

  1. cell textfield
  2. cell button
  3. cell tableview con
  4. cell của tableview con Tương ứng với mỗi loại Cell lại cần phại tạo một file quản trị riêng. Như trong demo này Step 3: Dựng giao diện cơ bản của màn hình Dựng cơ bản giao diện cho màn hình chính, đi tư file TableViewController.swift trước. Tương tự như trong như khi custom cell tableview, cần đăng ký chúng và đặt nó trong viewdidload
    override func viewDidLoad() {
        super.viewDidLoad()
        registerForCell()
    }
private func registerForCell() {
        self.tableView.register(UINib(nibName: kContentCell, bundle: nil), forCellReuseIdentifier: kContentCell)
        self.tableView.register(UINib(nibName: kInputContentCell, bundle: nil), forCellReuseIdentifier: kInputContentCell)
        self.tableView.register(UINib(nibName: kButtonCell, bundle: nil), forCellReuseIdentifier: kButtonCell)
    }

kế tiếp là tạo các func để return các cell giao diện vào tableview parent. Các cell còn lại sẽ tương tự

    private func cellForContent(in tableview: UITableView, at indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: kContentCell, for: indexPath) as? ContentCell else {
            return UITableViewCell()
        }
        return cell
    }

Sau khi tạo các func để return các cell giao diện. Chúng ta cần phỉa handle chúng. Viết hàm để handle việc return các cell

private func handleReturnCell(in tableview: UITableView, at indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.row {
        case 0:
            self.isNumber = false
            return self.cellForInputContent(in: tableview, at: indexPath)
        case 1:
            self.isNumber = true
            return self.cellForInputContent(in: tableview, at: indexPath)
        case 2:
            return self.cellForButton(in: tableview, at: indexPath)
        case 3:
            return self.cellForContent(in:tableview, at: indexPath)
        default:
            return UITableViewCell()
        }
    }

Trong này có biến isNumber đây là biến để controll việc textfield là number hay text. Tiếp theo là gọi nó trong data source của tableview.

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return self.handleReturnCell(in: tableView, at: indexPath)
    }

thêm một vài cấu hình cơ bản

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath.row == 3 {
            return 300
        }
        return 50
    }

Step 4: Thêm logic vào cell Đi từ cell input. Trong cell này có bao gồm 2 thuộc tính image và textfield. Cứ mỗi khi input vào textfield thì cell này sẽ trả về cho tableview parent chứa nó text vừa được input. Để làm việc này ở đây sử dụng một biến closure để controll việc này.

var completion: ((_ text: String, _ isNumber: Bool)->Void)?

Trong closure này sẽ truyền lại text đã input và kiểu textfield là gì ( là number hay text) dĩ nhiên cũng cần một function để thực hiện

fileprivate func inputText() -> Bool {
        if let textInput = self.myTextField.text, let completion = self.completion {
            completion(textInput, isNumber)
            return true
        } else {
            return false
        }
    }

Ngoai ra sẽ có hàm delegate của textfield để biết khi nào sẽ thực hiện việc truyền đi

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        return self.inputText()
    }
    
    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
        return self.inputText()
    }

Kế tiếp là tableview con nằm trong màn hình chính. Đầy là cell chứa tableview để hiển thị thông tin vừa nhập từ textfield. Trong cell này thao tác là khá đơn giản. tương tự như khi custom cell thông thường, Kế tiếp đến cell button, đây là cell thực hiện hành động thêm thông tin vào danh sách con nằm bên trong tableview. Tương tự với cell textfield cũng sử dụng một closure để truyền đi số lượng cell sẽ hiện thị lên tableview.

 var completion: ((_ count: Int)->Void)?

mỗi khi nhấn button thì tăng cell lên 1 phần tử,

@IBAction func actionAddCell(_ sender: UIButton) {
        if let completion = completion {
            self.countCell += 1
            completion(self.countCell)
        }
    }

Step 5: Thêm logic cho view parent với cell textfield tùy thuộc đó là số hay text sẽ nhận về tương ứng

private func cellForInputContent(in tableview: UITableView, at indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: kInputContentCell, for: indexPath) as? InputContentCell else {
            return UITableViewCell()
        }
        cell.isNumber = self.isNumber
        isNumber ? (cell.myImage.image = #imageLiteral(resourceName: "ic_phone")) : (cell.myImage.image = #imageLiteral(resourceName: "ic_user"))
        cell.completion = { (text, isNumber) in
            isNumber ? (self.phoneNumber = Int(text) ?? 0) : (self.userName = text)
        }
        return cell
    }

Với cell chưa tableview con, chỉ đơn thuần là set text và reload data tableview đó. Trong cell button, khi nhận về giá trị cần thực hiện thêm mới cell vào tableview con. Tới đây bạn đã có thể nắm được cơ bản hướng làm cũng như cách thực hiện. Hy vọng với cách làm này bạn sẽ có thể áp dụng tốt hơn trong các dự án sắp tới của minh. link github refer: https://github.com/tienbm92/TableviewInTableview


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí