Xây dựng Giao diện Người dùng Cơ bản với UIKit: Views, Controllers và Storyboards

Chào mừng bạn quay trở lại với loạt bài “Lộ trình học Lập trình viên iOS 2025“! Sau khi đã làm quen với những kiến thức Swift cơ bản, thiết lập Xcode, và tạo dự án đầu tiên, đã đến lúc chúng ta bắt tay vào xây dựng thứ mà người dùng sẽ thực sự tương tác: giao diện người dùng (User Interface – UI).

Trong thế giới iOS, UIKit là bộ khung (framework) truyền thống và mạnh mẽ để xây dựng các UI phức tạp. Mặc dù SwiftUI đang dần phổ biến, UIKit vẫn đóng vai trò cực kỳ quan trọng, đặc biệt khi làm việc với các codebase cũ hoặc khi cần kiểm soát chi tiết từng thành phần UI. Bài viết này sẽ đưa bạn đi sâu vào ba trụ cột cơ bản của việc xây dựng UI với UIKit: Views, View Controllers và Storyboards.

UIKit là Gì và Tại Sao Vẫn Quan Trọng?

UIKit là framework chính của Apple để phát triển ứng dụng cho iOS, tvOS và watchOS (phiên bản giới hạn). Nó cung cấp sẵn các lớp (classes) cho phép bạn tạo và quản lý các thành phần giao diện như nút bấm, nhãn văn bản, bảng biểu, thanh cuộn, và nhiều hơn nữa. UIKit hoạt động dựa trên mô hình hướng đối tượng chặt chẽ, tận dụng các tính năng mạnh mẽ của ngôn ngữ Swift (mà chúng ta đã tìm hiểu sâu hơn về lịch sử và lợi ích của nó, cũng như cách áp dụng OOP).

Sự quan trọng của UIKit vẫn còn rất lớn vì:

  • Hàng triệu ứng dụng iOS hiện có được xây dựng bằng UIKit. Việc hiểu biết về nó là cần thiết để bảo trì hoặc mở rộng các dự án này.
  • Nhiều tính năng nâng cao hoặc tích hợp sâu với hệ thống đôi khi vẫn dễ dàng thực hiện hơn với UIKit.
  • UIKit cung cấp các thành phần UI được tối ưu hóa và quen thuộc với người dùng iOS.

Việc nắm vững UIKit là nền tảng vững chắc, ngay cả khi sau này bạn chuyển sang làm việc nhiều hơn với SwiftUI. Cả hai framework này có thể được sử dụng kết hợp trong cùng một ứng dụng.

Views (UIView): Những Khối Xây Dựng Cơ Bản

Tại trung tâm của mọi giao diện người dùng trong UIKit là lớp UIView. Hãy nghĩ về UIView như một hình chữ nhật (hoặc hình dạng phức tạp hơn) trên màn hình thiết bị của bạn. Mỗi UIView chịu trách nhiệm vẽ nội dung của nó, xử lý các sự kiện chạm (touches), và quản lý các View con (subviews) của nó.

Một số đặc điểm chính của UIView:

  • Vẽ (Drawing): Mỗi View biết cách tự vẽ nó trên màn hình. Khi bạn thêm một View vào giao diện, hệ thống sẽ gọi phương thức vẽ của nó.
  • Hệ thống Phản hồi (Responder Chain): Views là một phần của hệ thống phản hồi của iOS, cho phép chúng nhận và xử lý các sự kiện như chạm, vuốt, lắc,…
  • Quản lý View con (Subview Management): Một View có thể chứa nhiều View con khác. Mối quan hệ này tạo nên một hệ thống phân cấp (view hierarchy). View chứa được gọi là Superview, và các View bên trong nó là Subviews.
  • Vị trí và Kích thước: Mỗi View có một vị trí và kích thước được xác định bởi thuộc tính frame (tọa độ và kích thước trong hệ tọa độ của superview) và bounds (tọa độ và kích thước trong hệ tọa độ của chính nó).

Một số thuộc tính quan trọng của UIView:

  • backgroundColor: Màu nền của View.
  • alpha: Độ mờ đục (opacity) của View (từ 0.0 hoàn toàn trong suốt đến 1.0 hoàn toàn mờ đục).
  • isHidden: Một Boolean xác định View có bị ẩn đi hay không.
  • tag: Một số nguyên để bạn có thể định danh View một cách dễ dàng (hữu ích khi làm việc với các View được tạo động hoặc lấy từ Storyboard).
  • layer: Lớp CALayer bên dưới chịu trách nhiệm cho việc vẽ và hoạt ảnh. Bạn có thể truy cập nó để tùy chỉnh các hiệu ứng nâng cao như bo góc (cornerRadius), đổ bóng (shadow), viền (border),…

Tạo UIView: Programmatic vs. Storyboard

Bạn có thể tạo và cấu hình Views theo hai cách chính:

  1. Programmatic (Bằng code): Tạo đối tượng UIView trong code Swift của bạn, thiết lập các thuộc tính, và thêm nó vào Superview. Phương pháp này mang lại sự linh hoạt tối đa và dễ dàng kiểm soát động các View.
  2. Storyboard: Kéo và thả Views từ thư viện đối tượng (Object Library) vào giao diện thiết kế của Storyboard, sau đó cấu hình chúng bằng Interface Builder. Phương pháp này nhanh chóng để thiết kế UI tĩnh và xem trước trực quan.

Ví dụ tạo UIView bằng code:

let myView = UIView(frame: CGRect(x: 20, y: 100, width: 200, height: 100))
myView.backgroundColor = .blue
myView.alpha = 0.7
// Để hiển thị View này, bạn cần thêm nó vào một Superview,
// ví dụ: view (root view của ViewController)
view.addSubview(myView)

UIKit cung cấp rất nhiều lớp con của UIView cho các mục đích cụ thể, ví dụ: UILabel (hiển thị văn bản), UIButton (nút bấm), UIImageView (hiển thị ảnh), UITextField (nhập văn bản một dòng), UITextView (nhập văn bản đa dòng), UITableView (bảng biểu), UICollectionView (tập hợp dữ liệu dạng lưới),… Chúng ta sẽ khám phá chi tiết các lớp này trong các bài viết tiếp theo.

View Controllers (UIViewController): Người Quản Lý

Nếu Views là các khối xây dựng, thì View Controllers (lớp UIViewController) là những người quản lý chúng. Một UIViewController chịu trách nhiệm quản lý một tập hợp các Views tạo nên một màn hình (hoặc một phần màn hình) trong ứng dụng của bạn. Nó đóng vai trò trung gian giữa các Views và dữ liệu (Model) của ứng dụng, xử lý các sự kiện từ người dùng và cập nhật Views cho phù hợp.

Vai trò chính của View Controller:

  • Quản lý Root View: Mỗi View Controller có một thuộc tính view, là View gốc của hệ thống phân cấp View mà nó quản lý. Tất cả các View con khác đều được thêm vào View gốc này.
  • Xử lý Tương tác Người dùng: View Controller nhận các sự kiện từ các Views (như chạm vào nút) và phản hồi lại bằng cách cập lý UI, thay đổi dữ liệu, hoặc thực hiện các hành động khác.
  • Quản lý Vòng đời (Lifecycle): View Controller có một tập hợp các phương thức được gọi tự động tại các thời điểm khác nhau trong vòng đời của nó (ví dụ: khi View được tải vào bộ nhớ, khi xuất hiện trên màn hình, khi biến mất,…). Hiểu rõ vòng đời của ViewController là cực kỳ quan trọng để viết code đúng thời điểm.
  • Quản lý Chuyển màn hình (Navigation): View Controller thường làm việc với các Navigation Controller (UINavigationController) hoặc Tab Bar Controller (UITabBarController) để quản lý việc chuyển đổi giữa các màn hình khác nhau trong ứng dụng.

Cấu trúc cơ bản của một View Controller:

import UIKit

class MyViewController: UIViewController {

    // Thuộc tính IBOutlet để liên kết với các Views từ Storyboard
    @IBOutlet weak var myLabel: UILabel!
    @IBOutlet weak var myButton: UIButton!

    // Phương thức này được gọi sau khi View controller tải View của nó vào bộ nhớ
    override func viewDidLoad() {
        super.viewDidLoad()
        // Thực hiện các thiết lập ban đầu cho View, dữ liệu,...
        myLabel.text = "Xin chào UIKit!"
    }

    // Phương thức IBAction để xử lý sự kiện từ các Views (ví dụ: chạm vào nút)
    @IBAction func buttonTapped(_ sender: UIButton) {
        print("Nút đã được chạm!")
        myLabel.text = "Nút đã được nhấn!"
    }

    // Các phương thức vòng đời khác (ví dụ: viewWillAppear, viewDidAppear,...)
    // override func viewWillAppear(_ animated: Bool) { ... }
    // override func viewDidAppear(_ animated: Bool) { ... }
    // ...

    // Các phương thức xử lý dữ liệu, logic nghiệp vụ khác
}

Trong ví dụ trên, @IBOutlet là cách chúng ta tạo một tham chiếu từ một View trên Storyboard đến code của View Controller. @IBAction là cách chúng ta liên kết một sự kiện từ View (như sự kiện “Touch Up Inside” của Button) đến một phương thức trong View Controller để xử lý sự kiện đó.

Storyboards: Thiết Kế Giao Diện Trực Quan

Storyboards là một công cụ thiết kế trực quan trong Xcode cho phép bạn thiết kế giao diện người dùng của ứng dụng, định nghĩa các View Controllers và cách chúng liên kết với nhau. Thay vì viết code cho mọi thứ từ đầu, bạn có thể kéo và thả các View, sắp xếp chúng, đặt thuộc tính và tạo kết nối ngay trên một canvas đồ họa.

Lợi ích của Storyboards:

  • Trực quan: Dễ dàng hình dung cấu trúc UI và luồng chuyển đổi giữa các màn hình.
  • Thiết kế nhanh: Kéo và thả giúp tạo layout tĩnh nhanh chóng.
  • Segues: Dễ dàng định nghĩa các chuyển đổi (transition) giữa các View Controllers chỉ với vài cú nhấp chuột.
  • Prototype Cell: Thiết kế các mẫu cell cho Table View hoặc Collection View ngay trong Storyboard.

Một Storyboard chứa nhiều Scenes. Mỗi Scene thường đại diện cho một View Controller và hệ thống phân cấp Views của nó. Các Scene được kết nối với nhau bằng Segues, định nghĩa cách người dùng di chuyển từ màn hình này sang màn hình khác (ví dụ: chuyển tiếp kiểu push, modal, …).

Làm việc với Storyboard:

  1. Mở tệp .storyboard trong Xcode.
  2. Từ Object Library (thường ở góc dưới bên phải hoặc có thể mở bằng Shift + Command + L), kéo các View Controller hoặc các Views (Label, Button, Image View,…) vào canvas.
  3. Sắp xếp các Views trên màn hình. Sử dụng các thanh căn chỉnh hoặc Auto Layout (một chủ đề quan trọng sẽ đề cập sau) để định vị chúng.
  4. Mở Assistant Editor để xem code của View Controller tương ứng (hoặc tạo một lớp View Controller mới và gán cho Scene).
  5. Kết nối Views trên Storyboard với code View Controller bằng cách Control-drag từ View trên canvas đến code, tạo ra `@IBOutlet`.
  6. Kết nối các sự kiện từ Views (như Button) đến code View Controller bằng cách Control-drag tương tự, tạo ra `@IBAction`.
  7. Để kết nối các View Controller với nhau, Control-drag từ một View (ví dụ: Button) hoặc từ chính View Controller đến View Controller đích, chọn kiểu Segue phù hợp.

Mặc dù Storyboards rất hữu ích, chúng cũng có nhược điểm, đặc biệt là khi làm việc theo nhóm (dễ gây xung đột khi nhiều người cùng sửa một tệp Storyboard) hoặc khi UI quá phức tạp. Đây là lúc việc tạo UI bằng code hoặc sử dụng file .xib (file giao diện cho một View Controller hoặc View đơn lẻ) có thể được cân nhắc.

Code vs. Storyboard: Lựa Chọn Nào?

Đây là một câu hỏi thường gặp đối với các nhà phát triển iOS mới. Không có câu trả lời đúng hay sai tuyệt đối, nó phụ thuộc vào dự án, đội nhóm và sở thích cá nhân. Nhiều dự án sử dụng cả hai phương pháp kết hợp.

Dưới đây là bảng so sánh giúp bạn hình dung rõ hơn:

Đặc điểm Programmatic UI (Bằng Code) Storyboards
Tốc độ Thiết kế Ban đầu Chậm hơn, cần viết code chi tiết cho từng thành phần và layout. Nhanh chóng với kéo/thả, xem trước trực quan ngay lập tức.
Khả năng Kiểm soát Động Kiểm soát hoàn toàn, dễ dàng tạo/xóa/cấu hình Views dựa trên dữ liệu hoặc sự kiện runtime. Hạn chế hơn, cần kết nối Outlet/Action và thay đổi thuộc tính trong code.
Bảo trì và Đọc hiểu Code Code rõ ràng, dễ theo dõi logic tạo UI, dễ tìm kiếm và sửa lỗi. UI nằm trong file Storyboard, logic tương tác nằm trong code. Cần chuyển đổi qua lại. File Storyboard lớn có thể khó đọc.
Xử lý Xung đột (Conflict) trong Version Control (ví dụ: Git) Xung đột code dễ giải quyết hơn. Xung đột trong file XML của Storyboard (khó đọc) thường phức tạp và dễ mắc lỗi khi giải quyết. (Nhắc đến Git, bạn có thể xem lại bài viết về Bắt Đầu Với Git và GitHub Cho Dự Án iOS Của Bạn).
Khả năng Tái sử dụng Dễ dàng tạo các View hoặc View Controller tùy chỉnh có thể tái sử dụng ở nhiều nơi. Việc tái sử dụng các phần UI nhỏ phức tạp hơn, thường cần tách ra file .xib riêng.
Kích thước Dự án Tạo ra nhiều file code hơn cho UI. Tập trung UI vào các file Storyboard/xib.
Hỗ trợ Thiết bị/Màn hình khác nhau Cần viết code để xử lý các kích thước và hướng màn hình khác nhau, thường kết hợp với Auto Layout. Thiết kế trực quan với Auto Layout trong Interface Builder rất mạnh mẽ.

Đối với người mới bắt đầu, Storyboards là một cách tuyệt vời để làm quen với cách các thành phần UI hiển thị và tương tác. Khi dự án phức tạp hơn hoặc làm việc trong nhóm lớn, việc cân nhắc sử dụng UI bằng code (hoặc kết hợp linh hoạt) là cần thiết.

Kết nối mọi thứ: Một ví dụ Đơn giản

Hãy hình dung bạn muốn tạo một màn hình đơn giản với một nhãn văn bản và một nút bấm. Khi bấm nút, văn bản trên nhãn sẽ thay đổi. Sử dụng Storyboard và code Swift, quy trình sẽ như sau:

  1. Tạo một dự án iOS mới trong Xcode (nếu chưa biết cách, hãy xem lại bài Tạo Dự Án iOS Mới). Chọn template “App” và đảm bảo giao diện là Storyboard và ngôn ngữ là Swift.
  2. Mở file ViewController.swift. Đây là View Controller mặc định.
  3. Mở file Main.storyboard. Bạn sẽ thấy Scene mặc định được liên kết với ViewController.swift.
  4. Từ Object Library, kéo một “Label” và một “Button” vào Scene này.
  5. Sắp xếp chúng trên màn hình. Sử dụng các đường căn chỉnh màu xanh để giúp đặt chúng vào vị trí trung tâm. (Để ứng dụng trông tốt trên mọi kích thước màn hình, bạn cần học về Auto Layout, nhưng tạm thời cứ sắp xếp bằng mắt).
  6. Mở Assistant Editor (biểu tượng hai vòng tròn lồng vào nhau ở góc trên bên phải hoặc Editor > Assistant). Đảm bảo nó hiển thị file ViewController.swift.
  7. Tạo IBOutlet: Control-drag từ Label trên Storyboard sang code trong lớp ViewController (ví dụ, ngay dưới dòng khai báo lớp). Thả ra và đặt tên cho Outlet là statusLabel. Xcode sẽ tạo dòng code:
    @IBOutlet weak var statusLabel: UILabel!
  8. Tạo IBAction: Control-drag từ Button trên Storyboard sang code trong lớp ViewController (ví dụ, dưới phương thức viewDidLoad). Thả ra, chọn “Action” làm Connection, đặt tên là buttonTapped, và chọn Type là UIButton. Xcode sẽ tạo phương thức:
    @IBAction func buttonTapped(_ sender: UIButton) { }
  9. Quay lại file ViewController.swift. Cập nhật phương thức viewDidLoadbuttonTapped:
    override func viewDidLoad() {
        super.viewDidLoad()
        // Thiết lập văn bản ban đầu cho Label
        statusLabel.text = "Chưa bấm nút"
        // Thiết lập văn bản cho Button (có thể làm trong Storyboard hoặc code)
        myButton.setTitle("Bấm vào đây", for: .normal) // Nếu bạn đã tạo Outlet cho Button
        // Hoặc truy cập nút thông qua sender trong action nếu không có Outlet riêng
    }
    
    @IBAction func buttonTapped(_ sender: UIButton) {
        // Thay đổi văn bản của Label khi nút được bấm
        statusLabel.text = "Nút đã được bấm!"
        print("Người dùng đã bấm nút!") // In ra console để kiểm tra
    }
    

    (Lưu ý: Nếu bạn đã tạo Outlet cho Button ở bước 7 như trong ví dụ code cơ bản trước đó, bạn có thể sử dụng myButton.setTitle(...). Nếu không, bạn chỉ cần truy cập statusLabel.)

  10. Chạy ứng dụng trên simulator hoặc thiết bị thật (Product > Run hoặc Cmd + R). Bạn sẽ thấy màn hình với nhãn và nút. Khi bấm nút, văn bản trên nhãn sẽ thay đổi.

Chúc mừng! Bạn vừa xây dựng thành công một UI cơ bản, kết nối nó với code, và xử lý tương tác người dùng bằng UIKit và Storyboard.

Lời khuyên cho Người mới bắt đầu

  • Thực hành là chìa khóa: Cách tốt nhất để học là làm. Hãy thử tạo các UI đơn giản khác nhau, kéo thả các loại Views khác nhau, và xem chúng hoạt động như thế nào.
  • Hiểu hệ thống phân cấp View: Luôn hình dung cấu trúc cha-con của Views. Điều này rất quan trọng khi thêm/xóa Views hoặc xử lý sự kiện.
  • Làm quen với Auto Layout: Ngay sau khi thoải mái với Views cơ bản, hãy dành thời gian học Auto Layout. Đây là cách tiêu chuẩn để tạo UI thích ứng với các kích thước màn hình khác nhau. Nó là một chủ đề lớn và quan trọng tiếp theo trong lộ trình của bạn.
  • Sử dụng tên biến rõ ràng: Đặt tên cho các Outlet và Action sao cho dễ hiểu chức năng của chúng.
  • Giữ View Controllers gọn gàng: Tránh đưa quá nhiều logic nghiệp vụ vào View Controller. Đây là một phần của kiến trúc ứng dụng tốt (như MVC mà UIKit tuân theo) mà chúng ta sẽ nói đến sau.
  • Đừng ngại đọc tài liệu Apple: Tài liệu chính thức là nguồn thông tin đáng tin cậy và chi tiết nhất.

Kết luận

Views, View Controllers và Storyboards (hoặc UI bằng code) là những khái niệm cốt lõi để xây dựng giao diện người dùng trong UIKit. Views là các thành phần hiển thị, View Controllers quản lý Views và logic tương tác, còn Storyboards cung cấp một phương pháp trực quan để thiết kế và liên kết chúng. Nắm vững cách chúng hoạt động cùng nhau là bước đệm vững chắc trên con đường trở thành nhà phát triển iOS chuyên nghiệp.

Trong các bài viết tiếp theo của loạt bài này, chúng ta sẽ đi sâu hơn vào các loại Views cụ thể như Table View và Collection View, tìm hiểu về Auto Layout để tạo giao diện thích ứng, và khám phá cách quản lý dữ liệu cũng như xử lý đa luồng hoặc xử lý lỗi trong ứng dụng của bạn.

Hãy tiếp tục thực hành, xây dựng những UI đầu tiên của bạn, và đừng ngần ngại thử nghiệm nhé! Hẹn gặp lại trong bài viết tiếp theo!

Chỉ mục