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 December 5, 2019

Basic iOS tutorial : Collection View

iOS & Swift

Contents

  • Chuẩn bị
  • 1. Giới thiệu Collection View
    • 1.1. Collection View
    • 1.2. Cấu trúc
      • 1.2.1. Cell
      • 1.2.2. Layout
  • 2. Tạo Collection View
  • 3. Custom Collection View Cell
  • 4. Cell Layout
    • 4.1. Tuỳ chỉnh tại file giao diện
    • 4.2. Code cell layout
      • 4.2.1 UICollectionViewFlowLayout
      • 4.2.2. Delegate UICollectionViewDelegateFlowLayout
  • 5. Collection View Header
  • Tạm kết

Chào bạn, chúng ta lại tiếp tục seri Lập trình iOS cho mọi người. Chủ đề bài viết này sẽ liên quan tới việc hiển thị một danh sách, nhưng không sử dụng TableView. Đó là Collection View.

Và nếu bạn chưa tìm hiểu về TableView thì bạn có thể đọc qua 2 bài viết sau:

  • Basic iOS tutorial : Table View
  • Basic iOS tutorial : Custom Cell

Mọi thứ đã okay thì …

Bắt đầu thôi!

Chuẩn bị

  • MacOS 10.14.4
  • Xcode 11.0
  • Swift 5.1

1. Giới thiệu Collection View

1.1. Collection View

Khi bạn chán hiển thị một danh sách theo từng dòng (TableView) thì còn một dạng khác. Đó là dạng các ô lưới. Đây cũng là  dạng phổ biến. Tuy nhiên, nó xuất hiện khá muộn trong iOS (từ iOS 6 trở đi). Và apple gọi nó là Collection View.

An object that manages an ordered collection of data items and presents them using customizable layouts.

Theo Apple.

Ta có thể bắt gặp Collection View ở các UI của một số ứng dụng cơ bản trong iOS.

  • Collection View hiển thị danh sách dữ liệu thành dạng ô, bao gồm: hàng và cột.
  • So với TableView, Collection View linh hoạt và dễ thay đổi hơn.
  • Tương tự TableView, Collection View được tạo bởi không hoặc nhiều section, mỗi section sở hữu nhiều item. Mỗi section có thể có section header và section footer

1.2. Cấu trúc

UICollectionView là subclass của UIScrollView, nên nó cho phép người dùng cuộn table. UICollectionView cho phép cuộn theo cả hai chiều.

1.2.1. Cell

  • Thành phần thể hiện các item trên collection view là những đối tượng của lớp UICollectionViewCell.
  • Đối tượng UICollectionView phải cần đối tượng hoạt động như một data source và delegate.
    • Data source: nhận protocol UICollectionViewDataSource, cung cấp thông tin cần thiết cho việc xây dựng collection và quản lý dữ liệu khi một item được thêm vào, xoá hay sắp xếp lại.
    • Delegate: nhận protocol UICollectionViewDelegate, quản lý việc config hoặc lựa chọn item, sắp xếp lại item, highlight, trang trí view và các thao tác thay đổi khác.

1.2.2. Layout

  • Một đối tượng vô cùng quan trọng đối với việc xây dựng collection view là đối tượng layout, là subclass của lớp UICollectionViewLayout .
  • Đối tượng này có nhiệm vụ khai báo cách sắp xếp và vị trí của tất cả các cell, cũng như các view bổ trợ cho việc xây dựng collection view như: header, footer,…
  • Thay đổi layout của collection bằng cách thay đổi thuộc tính collectionViewLayout, cài đặt thuộc tính này sẽ trực tiếp cập nhật layout ngay lập tức, mà không có hiệu ứng chuyển đổi.
  • Thay vào đó nếu muốn cấu hình hiệu ứng chuyển đổi, chúng ta phải sử dụng phương thức setCollectionViewLayout(_:animated:completion:)

2. Tạo Collection View

Tạo 1 project không sử dụng Storyboard. Trong đó:

  • 1 UIViewController
  • Add 1 UICollectionView vào View của Controller
    • Tạo IBOutlet cho UICollectionView

Kéo thả các delegate và data source của UICollectionView cho ViewController

Hoặc bạn có thể xét trực tiếp bằng code.

override func viewDidLoad() {
        super.viewDidLoad()
        title = "Home"
        
        collectionView.delegate = self
        collectionView.dataSource = self
    }

Tiếp tục, tạo mới class User để làm dữ liệu cho Collection View

  • Có 1 static function để cung cấp dữ liệu giả (dummy datas) cho ViewController
import Foundation

final class User {
    var name: String
    var avatar: String
    
    init(name: String, avatar: String) {
        self.name = name
        self.avatar = avatar
    }
}

extension User {
    static func getDummyDatas() -> [User] {
        var users: [User] = []
        
        for i in 1...30 {
            let user = User(name: "User \(i)", avatar: "\(i%10)")
            users.append(user)
        }
        
        return users
    }
}

Tại file HomeViewController.swift, thêm 1 property là users. Để chứa dữ liệu cho CollectionView

var users: [User] = User.getDummyDatas()

Tới đây, thì cơ bản đã xong phần chuẩn bị dữ liệu cho Collection View. Giờ chuyển sang phần tạo cell

3. Custom Collection View Cell

Tạo mới một cell với tên là HomeCell, kế thừa từ UICollectionViewCell

Tiến hành kéo thả giao diện và tạo các outlet cho cell

  • avatarImageView : cho ảnh hiển thị
  • nameLabel : cho tên hiển thị

Quay về file HomeViewController.swift, tiến hành một số các thủ tục sau:

  • Bước 1 : register cell (tương tự như cách register của TableViewCell)
let nib = UINib(nibName: "HomeCell", bundle: .main)
collectionView.register(nib, forCellWithReuseIdentifier: "cell")
  • Bước 2: tạo extention. Có 2 function quan trọng nhất
    • numberOfItemInSection số lượng các item cho section của Collection View
      • Mặc định nếu không có định nghĩa lại function numberOfSection, thì mặc định có 1 section
    • cellForItemAt IndexPath
      • Tương tự Tableview
      • Sử dụng hàm dequeue, để lấy ra 1 cell với indexPath và reuser identifier
      • Ép kiểu cell đó về HomeCell
      • Lấy 1 phần tử ra từ array dữ liệu
      • Cập nhật dữ liệu cho cell
      • Return cell đó
extension HomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        users.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeCell
        
        let item = users[indexPath.row]
        cell.nameLabel.text = item.name
        cell.avatarImageView.image = UIImage(named: item.avatar)
        
        return cell
    }
}

Kết quả như sau:

Tới đây, thì chúc mừng bạn đã xong việc tạo 1 Collection View đơn giản. Tuy nhiên, bạn sẽ thấy là nameLabelđã biến mất. Và cell lại hình vuông. Vậy tiếp tục giải quyết phần tiếp theo của Collection View là layout.

4. Cell Layout

4.1. Tuỳ chỉnh tại file giao diện

Mở file HomeViewController.swift, chọn Collection View Flow Layout. Và tiến hành điều chỉnh

  • Cell size là kích thước của cell

Kết quả như sau:

Đôi lúc, chúng ta muốn code và nó sẽ linh hoạt hơn, đẹp hơn. Thì sang cách thứ 2, là tuỳ chỉnh layout bằng code.

4.2. Code cell layout

4.2.1 UICollectionViewFlowLayout

Sử dụng 1 đối tượng UICollectionViewFlowLayout, xét nó cho Collection View. Tham khảo đoạn code sau:

  1. Lấy giá trị của chiều ngang màn hình
  2. Tạo đối tượng layout
    1. Cách mép trên 20px, mép dưới 10px
    2. Kích thước bằng 1/3 màn hình
    3. Các khoảng cách là 5px
  3. Xét cho thuộc tính collectionViewLayout, của Collection View
        //1
        let screenWidth = UIScreen.main.bounds.width - 10
        //2
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
        layout.itemSize = CGSize(width: screenWidth/3, height: (screenWidth/3)*5/4)        
        layout.minimumInteritemSpacing = 5
        layout.minimumLineSpacing = 5
        //3
        collectionView!.collectionViewLayout = layout

Kết quả như sau:

4.2.2. Delegate UICollectionViewDelegateFlowLayout

Đây là một cách khác để custom layout của Collection View, mà không cần xét trực tiếp đối tượng layout như ở trên. Đó là dùng delegate của FlowLayout.

Tiến hành implement các function của delegate UICollectionViewDelegateFlowLayout. Trong đó một số hàm cần thiết như sau:

  • sizeForItem: kích thước của cell
  • minimunLineSpacing & minimunInteritem: cho các khoảng cánh
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let screenWidth = UIScreen.main.bounds.width - 10
        return CGSize(width: screenWidth/3, height: (screenWidth/3)*5/4)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 5
    }

    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 5
    }

Kết quả như sau:

5. Collection View Header

Đối với dạng danh sách nhiều chiều, thì chúng ta sẽ sử dụng việc hiển thị theo từng section. Trong TableView, thì các header và footer của TableView là các UIView. Còn với Collection View, thì chúng được gọi là supplementary view. Và class đại diện cho chúng là UICollectionReusableView.

Nó cũng tương tự như UICollectionViewCell, chúng ta phải thực hiện công việc custom chúng. Công việc này cũng tương tự như việc custom cell. Bây giờ, thì bắt đầu bằng việc tạo ra 1 Header cho Section của Collection View.

Tạo mới một file với tên là HomeHeaderView, kế thừa từ class UICollectionReusableView

Tiến hành kéo thả giao diện, tạo các outlet.

Mở file HomeViewController.swift, tiến hành các bước sau:

  • Bước 1: register header
    • Header là một supplementary view, nên phải chọn function register cho phù hợp
    • Kiểu là UICollectionView.elementKindSectionHeader
    • Re-use identifier là header
let headerNib = UINib(nibName: "HomeHeaderView", bundle: .main)
collectionView.register(headerNib, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
  • Bước 2: implement các function để load header view
    • referenceSizeForHeaderInSection: xác định kích thước của header view
    • viewForSupplementaryElementOfKind : return về 1 supplementary view phù hợp
    • Sẽ có các kiểu là header hoặc footer, nên cần phải lựa chọn cho phù hợp
    • Xét các thuộc tính cho header
   func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: 50)
    }
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        switch kind {
        case UICollectionView.elementKindSectionHeader:
               let reusableview = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header", for: indexPath) as! HomeHeaderView

               reusableview.frame = CGRect(x: 0 , y: 0, width: self.view.frame.width, height: 50)
               
               reusableview.titleLabel.text = "Users"
               reusableview.totalLabel.text = "\(users.count)"
               
                return reusableview
        default:
            fatalError("Unexpected element kind")
        }
    }

Kết quả sau khi thực thi:

Tới đây, thì bạn đã hoàn thành xong việc sử dụng 1 Collection View ở mức độ cơ bản. Còn rất nhiều kĩ thuật hay xử lý nâng cao, cũng như custom layout nâng cao cho layout của Collection View. Hẹn bạn ở 1 bài viết với cấp độ nâng cao nhiều hơn.

Tạm kết

  • Tìm hiểu về Collection View
  • Cấu trúc
  • Tạo 1 Collection View đơn giản
  • Custom Cell layout
  • Custom Header/Footer

Cảm ơn bạn đã theo dõi bài viết này!

 

FacebookTweetPinYummlyLinkedInPrintEmailShares10

Related Posts:

  • feature_bg_3
    Clean Architecture trong iOS
Tags: basic ios tutorial, collectionview, iOS
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

  • Role-playing vs. Persona-based Prompting
  • [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?

You may also like:

  • Clean Architecture trong iOS
    feature_bg_3

Archives

  • May 2025 (2)
  • 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 (44)
  • Code (11)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (102)
  • No Category (1)
  • RxSwift (37)
  • SwiftUI (80)
  • Tutorials (87)

Newsletter

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

    Copyright © 2025 Fx Studio - All rights reserved.