Để chèn (insert) cell mới vào UITableView, quy trình chuẩn là: cập nhật data source, sau đó gọi insertRows(at:with:) trên table view – không reload toàn bộ table.
Ví dụ hoàn chỉnh
1. Setup TableView cơ bản
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
var items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
// DataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
2. Hàm chèn cell mới
@IBAction func insertNewCell(_ sender: UIButton) {
// 1. Cập nhật data source TRƯỚC
let newIndex = items.count // Chèn ở cuối
items.append("New Item \(items.count + 1)")
// 2. Tạo IndexPath cho vị trí mới
let indexPath = IndexPath(row: newIndex, section: 0)
// 3. Insert với animation
tableView.insertRows(at: [indexPath], with: .automatic)
// Animation options: .automatic, .fade, .right, .left, .top, .bottom, .middle, .none
}
3. Chèn ở vị trí tùy ý (ví dụ: đầu danh sách)
func insertAtTop() {
items.insert("New Top Item", at: 0)
let indexPath = IndexPath(row: 0, section: 0)
tableView.insertRows(at: [indexPath], with: .top)
}
Kết quả

Quy tắc vàng
- Luôn cập nhật data source (array) trước khi gọi insertRows.
- Số lượng row trong
numberOfRowsInSectionsau khi insert phải khớp với số IndexPath truyền vào. - Dùng
tableView.performBatchUpdates({ ... }, completion: nil)nếu insert/xóa/sửa nhiều row cùng lúc. - Đối với Custom Cell: register nib/class, dequeue với identifier đúng.
Xóa cell (Bonus)
func deleteRow(at indexPath: IndexPath) {
items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}