Skip to content
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Fx Studio
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Written by chuotfx on November 19, 2019

Basic iOS tutorial : Custom Cell

iOS & Swift

Contents

  • Chuẩn bị
  • 1. Add Subview
    • Nguyên tắc làm việc với reuseable như sau:
  • 2. Custom Cell
    • 2.1. Create cell
    • 2.2. Register Cell
    • 2.3. Load cell
  • 3. Dynamic cell
  • Tạm kết

Chào bạn đến với Fx Studio, bài viết hôm nay sẽ là phần tiếp theo của:

    • Basic iOS tutorial : Table View

Nếu bạn chưa biết về Table View thì có thể ghé link trên. Về Table View thì còn nhiều phần tiếp theo, mình xin phép chia ra thành nhiều bài với nhiều chủ đề trọng tâm. Chủ để phần này là về tuỳ chỉnh giao diện cho cell, hay giang hồ gọi với cái tên thân mật là Custom Cell.

Bắt đầu thôi!

Về giao diện của các ứng dụng iOS, hầu như 90% đều phải tuỳ chỉnh và chúng không sử dụng các giao diện mặc định của iOS. Để tuỳ chỉnh về giao diện thì chúng ta có bài Custom View:

    • Basic iOS tutorial : Custom View

Vẫn nguyên tình thần của Custom View, giờ chúng ta tiến hoá sang Custom UITableViewCell (hay gọi là Custom Cell). Và cũng áp dụng nguyên các cách custom tương tự như UIView.

Chuẩn bị

  • MacOS 10.14.4
  • Xcode 11.0
  • Swift 5.1

1. Add Subview

UITableViewCell là class của các row (hay cell) trong UITableView. Nó là sub-class của UIView. Nên theo nguyên tắc của View, thì nó có các view con (subviews). Vì vậy, việc custom đơn giản nhất là addSubview cho cell.

Chúng ta sử dụng lại project của phần trước. Các UITableViewCell có 1 UILabel với tên là textLabel để hiện thị thông tin. Bây giờ, chúng ta thêm 1 label nữa để hiển thị thông tin phụ.

Mở file HomeViewController.swift và edit đoạn code sau:

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = names[indexPath.section][indexPath.row]
        
        let label = UILabel(frame: CGRect(x: 20, y: 30, width: 100, height: 20))
        label.text = "sub title"
        label.textColor = .red
        cell.addSubview(label)
        
        return cell
    }

Trong đó:

  • label là đối tượng UILabel
  • Xét các thuộc tính frame, text, và textColor cho label
  • AddSubView cho cell là label

Kết quả:

Quá đơn giản!

Tuy nhiên, việc xác định giao diện rất khó khắn và tối rất nhiều công sức tính toán toạ độ. Bên cạnh đó là việc re-use thì các cell không tạo mới. Cứ như vậy, qua mỗi hàm cellForRow  chạy, thì lại có label mới tạo ra và thêm vào, chứ chúng không bị remove đi.

Khi danh sách của bạn quá dài và scroll lên xuống nhiều lần. Với ví dụ trên, bạn sẽ thấy một số cell sẽ có màu đỏ của sub title đậm hơn 1 số cell khác. Đó chính là vấn đề của reuseable.

Nguyên tắc làm việc với reuseable như sau:

  • Có init() thì phải có deinit()
  • Có add thì phải có remove
  • Có if thì phải có else

Và, việc addSubview không là giải pháp tối ứu nhất. Bây giờ chúng ta chuyển sang giải pháp kinh điển hơn, là kéo thả giao diện.

2. Custom Cell

2.1. Create cell

Kích chuột phải và new file, như sau:

Chú ý:

  • Chọn vào also create XIB file để Xcode tạo sẵn cho bạn file giao diện cho Cell
  • Sub-class là UITableViewCell

Tiến hành kéo thả giao diện và tạo các IBOutlet tương ứng.

Code của file HomeCell.swift như sau:

import UIKit

class HomeCell: UITableViewCell {
    
    @IBOutlet weak var avatarImageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var subTitleLabel: UILabel!
    

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }
    
}

2.2. Register Cell

Mở file HomeViewController.swift để tiến hành register cell cho Table View. Công việc này rất quan trọng và quyết định sống còn của việc Custom Cell. Tại hàm viewDidLoad, thêm đoạn code sau:

        //1
        let nib = UINib(nibName: "HomeCell", bundle: .main)
        //2
        tableview.register(nib, forCellReuseIdentifier: "cell")

Trong đó:

  1. Tạo 1 đối tượng nib, với tên nibName chính là tên file *.xib. Bundle chính là main bundle
  2. Sử dụng hàm register với nib
  3. Identifier cho cell để re-use là cell (bạn có thể đổi theo ý riêng của bạn)

Tới đây thì xong phần register cell cho Table View.

2.3. Load cell

Tới phần extension cho delegate của Table View trong HomeViewController.swift. Edit lại hàm cellForRow

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HomeCell
        
        cell.nameLabel.text = names[indexPath.section][indexPath.row]
        cell.subTitleLabel.text = "Sub title"
        
        return cell
    }

Trong đó:

  • Vẫn sử dụng dequeue với identifier và indexPath để lấy đối tượng cell ra đúng với indexPath.
  • Ép kiểu về kiểu HomeCell
  • Xét các giá trị cho các IBOutlet của cell

Tiếp tục thêm 1 function nữa của UITableViewDelegate, mục đích:

  • Xác định chiều cao của Custom Cell
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80
    }

Chạy chương trình và xem kết quả.

Nếu bạn ra được kết quả như hình, thì chúc mừng bạn đã hoàn thành việc custom cell. Tuy nhiên, vẫn còn 1 xí nữa cho phần dữ liệu của TableView.

3. Dynamic cell

Qua ví dụ trên, thì bạn sẽ thấy là:

  • Dữ liệu cung cấp cho cell là 1 array String.
  • Dữ liệu cần cho Custom Cell sẽ gồm nhiều trường. Tức là cần nhiều hơn 1 array.

Có thể nhiều bạn sẽ suy nghĩ như thế này để cung cấp dữ liệu cho Table View.

var names: [String] = ...
var ages: [Int] = ...
var genders: [Bool] = ...

Điều này thì …

Ta không thể nào tạo nhiều array để cung cấp dữ liệu cho cell. Nó làm việc quản lý dữ liệu sẽ gặp rất nhiều khó khăn. Giải pháp là nhóm tụi nó về 1 array với 1 kiểu dữ liệu mới.

Nói dễ hiểu thì tạo class/struct cho dữ liệu của Table View. Hehe!

Kích chuột phải và tiếp tục new file mới, tên là User.swift. Trong đó:

  • tạo 1 class User
  • các thuộc tính: tên, tuổi và giới tính
  • hàm trả về thông tin của user
final class User {
    var name: String
    var age: Int
    var gender: Bool
    
    init(name: String, age: Int, gender: Bool) {
        self.name = name
        self.age = age
        self.gender = gender
    }
    
    func getSubTitle() -> String {
        return "\(gender ? "Male" : "Female") - \(age) year olds"
    }
}

Tiếp tục, viết 1 function để trả về dữ liệu giả là 1 array User. Trong đó:

  1. Tạo 1 array user rỗng
  2. Lặp từ 0 đến 30 lần
  3. Tạo mới 1 đối tượng user với dữ liệu cung cấp là random
  4. Thêm đối tượng mới vào mãng.
func getUser() -> [User] {
        //1
        var users = [User]()
        //2
        for i in 0...30 {
            //3
            let user = User(name: "Name \(i+1)", age: Int.random(in: 10...30), gender: Bool.random())
            //4
            users.append(user)
        }
        
        return users
    }

Tiến hành chỉnh sửa lại dữ liệu cho Table View của HomeViewController.swift

var users: [User] = []

Cập nhật dữ liệu cho users

override func viewDidLoad() {
        super.viewDidLoad()
        title = "Home"
        
        let nib = UINib(nibName: "HomeCell", bundle: .main)
        tableview.register(nib, forCellReuseIdentifier: "cell")
        
        tableview.delegate = self
        tableview.dataSource = self
        
        users = getUser()
    }

Cập nhật lại các function của delegate & datasouce của Table View.

extension HomeViewController : UITableViewDelegate, UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return users.count
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HomeCell
        
        let user = users[indexPath.row]
        cell.nameLabel.text = user.name
        cell.subTitleLabel.text = user.getSubTitle()
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let user = users[indexPath.row]
        let vc = DetailViewController()
        vc.name =  user.name
    
        self.navigationController?.pushViewController(vc, animated: true)
    }
    
}

Chú ý:

  • let user = users[indexPath.row] sẽ giúp bạn lấy ra đối tượng cần thiết để hiển thị hay sử dụng dữ liệu.

Sau này, dữ liệu cũng cấp cho Table View sẽ lấy từ API/Webservice/Database … và không lệ thuộc vào việc hardcode. Khuyến khích khi làm việc với Table View thì nên tạo các function với dữ liệu giả để cũng cấp dữ liệu cho các cell.

Tới đây, thì bạn đã xong phần Custom Cell, hẹn gặp lại bạn ở các phần sau. Cảm ơn đã đọc!

Tạm kết

  • AddSubview vào Cell
  • Custom Cell
  • Dynamic cell

 

FacebookTweetPinYummlyLinkedInPrintEmailShares3

Related Posts:

  • testing
    Hello Testing iOS
  • feature_bg_3
    Clean Architecture trong iOS
Tags: basic ios tutorial, iOS, tableview
Written by chuotfx

Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Donate – Buy me a coffee!

Fan page

Fx Studio

Tags

Actor Advanced Swift AI api AppDistribution autolayout basic ios tutorial blog ci/cd closure collectionview combine concurrency crashlytics dart dart basic dart tour Declarative delegate deploy design pattern fabric fastlane firebase flavor flutter GCD gradients iOS MVVM optional Prompt engineering protocol Python rxswift safearea Swift Swift 5.5 SwiftData SwiftUI SwiftUI Notes tableview testing TravisCI unittest

Recent Posts

  • [Swift 6.2] Raw Identifiers – Đặt tên hàm có dấu cách, tại sao không?
  • Vibe Coding là gì?
  • Cách Đọc Sách Lập Trình Nhanh và Hiệu Quả Bằng GEN AI
  • Nỗ Lực – Hành Trình Kiến Tạo Ý Nghĩa Cuộc Sống
  • Ai Sẽ Là Người Fix Bug Khi AI Thống Trị Lập Trình?
  • Thời Đại Của “Dev Tay To” Đã Qua Chưa?
  • Prompt Engineering – Con Đường Để Trở Thành Một Nghề Nghiệp
  • Vấn đề Ảo Giác (hallucination) khi tương tác với Gen AI và cách khắc phục nó qua Prompt
  • Điều Gì Xảy Ra Nếu… Những Người Dệt Mã Trở Thành Những Người Bảo Vệ Cuối Cùng Của Sự Sáng Tạo?
  • Khi Cô Đơn Gặp Python

You may also like:

  • Hello Testing iOS
    testing
  • Clean Architecture trong iOS
    feature_bg_3

Archives

  • May 2025 (1)
  • April 2025 (1)
  • March 2025 (8)
  • January 2025 (7)
  • December 2024 (4)
  • September 2024 (1)
  • July 2024 (1)
  • June 2024 (1)
  • May 2024 (4)
  • April 2024 (2)
  • March 2024 (5)
  • January 2024 (4)
  • February 2023 (1)
  • January 2023 (2)
  • November 2022 (2)
  • October 2022 (1)
  • September 2022 (5)
  • August 2022 (6)
  • July 2022 (7)
  • June 2022 (8)
  • May 2022 (5)
  • April 2022 (1)
  • March 2022 (3)
  • February 2022 (5)
  • January 2022 (4)
  • December 2021 (6)
  • November 2021 (8)
  • October 2021 (8)
  • September 2021 (8)
  • August 2021 (8)
  • July 2021 (9)
  • June 2021 (8)
  • May 2021 (7)
  • April 2021 (11)
  • March 2021 (12)
  • February 2021 (3)
  • January 2021 (3)
  • December 2020 (3)
  • November 2020 (9)
  • October 2020 (7)
  • September 2020 (17)
  • August 2020 (1)
  • July 2020 (3)
  • June 2020 (1)
  • May 2020 (2)
  • April 2020 (3)
  • March 2020 (20)
  • February 2020 (5)
  • January 2020 (2)
  • December 2019 (12)
  • November 2019 (12)
  • October 2019 (19)
  • September 2019 (17)
  • August 2019 (10)

About me

Education, Mini Game, Digital Art & Life of coders
Contacts:
contacts@fxstudio.dev

Fx Studio

  • Home
  • About me
  • Contact us
  • Mail
  • Privacy Policy
  • Donate
  • Sitemap

Categories

  • Art (1)
  • Blog (43)
  • Code (11)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (102)
  • No Category (1)
  • RxSwift (37)
  • SwiftUI (80)
  • Tutorials (86)

Newsletter

Stay up to date with our latest news and posts.
Loading

    Copyright © 2025 Fx Studio - All rights reserved.