Storyboard & Tạo giao diện cơ bản trong iOS
iOS & Swift . TutorialsContents
Chào mừng bạn đến với Fx Studio. Chủ đề bài viết này là về Storyboard và cách tạo giao diện cơ bản trong iOS. Đó là một trong những thành phần cơ bản đầu tiên khi bạn mới bắt tay vào học iOS. Tuy nhiên, vì hoàn cảnh lịch sử phát triển phần mềm trong iOS, mà vai trò của Storyboard không ảnh hưởng nhiều. Nhưng nó vẫn là một trong 3 cách tạo giao diện trong iOS nói chung.
Nếu mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
Storyboard là một trong những tính năng được cập nhật mới trong iOS 5. Do đó, hầu như Xcode của bạn đang dùng lúc này đều đảm bảo có tính năng Storyboard trong iOS project rồi.
Như mình nói ở trên, hầu hết các dev iOS đều không thích Storyboard. Việc đầu tiên mà họ sẽ là khi tạo mới một iOS Project là sẽ xóa nó và thay đổi cấu hình project lại. Nếu bạn không biết cách xóa nó, thì hãy tham khảo bài viết dưới đây.
Về mặt kiến thức, yêu cầu bạn đã biết về lập trình iOS ở mức cơ bản. Mình sẽ không giải thích hay đi hướng dẫn những gì quán cơ bản, như là: IBAction, IBOutlet, UIViewController … Và nếu bạn chưa biết về lập trình iOS thì có thể đọc qua loạt bài về Lập trình iOS cho mọi người này nhóe!
Storyboard
Storyboard là gì?
Bảng phân cảnh (Storyboard) là một tính năng thú vị được giới thiệu lần đầu tiên trong iOS 5, giúp tiết kiệm thời gian xây dựng giao diện người dùng cho ứng dụng của bạn. Storyboard cho phép bạn tạo prototype và thiết kế nhiều viewcontroller cho nhiều view trong một file và cũng cho phép bạn tạo chuyển tiếp giữa các viewcontroller.
Trước khi có Storyboard, chúng ta sẽ thiết kế giao diện trên các XIB files (hay còn gọi là NIB file). Và bạn chỉ tạo được mỗi XIB file cho mỗi View mà thôi.
Trong mỗi Storyboard, bạn sẽ có:
- Scene: là các màn hình. Có thể có nhiều màn hình và chúng kết hợp tạo nên nhiều luồng màn hình trong đó.
- Segue: là các cầu nối giữa các màn hình với nhau. Segue giúp bạn tạo hiệu ứng chuyển cảnh và truyền dữ liệu qua lại giữa các màn hình.
Storyboard sẽ thể hiện cho bạn một bức tranh toàn cảnh về các màn hình và mối quan hệ giữa các chúng. Tuy nhiên, dev iOS không sử dụng nó cũng bởi vì là:
Quá lag!
Và trong bài viết này, ta sẽ không bàn về nhược điểm của chúng. Mà tập trung vào những gì bạn có thể làm được với Storyboard.
Cách tạo
Không cần quá phức tạp, thì bạn có thể tạo được một project có sẵn Storyboad. Mở Xcode lên và tạo mới một project.
Điều bạn cần làm chính là lựa chọn tại mục Interface. Vì hiện tại với Xcode 12 & 13 thì bạn sẽ có thêm một lựa chọn cho Interface nữa là SwiftUI.
Hãy chọn cho đúng nhóe!
Còn nếu bạn lỡ tạo nhầm hoặc sử dụng một project không có Storyboard, thì vẫn có thể tạo mới một file Storyboard vẫn được.
Điều quan trọng chính là cấu hình của bạn phải chọn đúng Storyboard nào cho việc khởi chạy một ứng dụng iOS nhóe. Xem hình sau là biết liền.
Bạn có thể có 1 hoặc có nhiều file, nhiệm vụ của bạn sẽ chọn đúng file nào cho Main Interface mà thôi. Ngoài ra, cấu hình còn quy định thêm nữa ở file info.plist
cho file nào sẽ là scene chính.
Hiển thị ViewController
Khi bạn tạo mới một Project, thì đã có sẵn một ViewController rồi. Nhiệm vụ tiếp theo, chúng ta sẽ hiển thị một ViewController mới và thêm nó vào Storyboard. Bắt đầu, bạn tạo mới 1 file ViewController và đặt tên là HomeViewController
.
(Chú ý là bạn không tích vào “Also create XIB file” nhóe.)
Tiếp theo, bạn vào file Storyboard và kéo thả một UIViewController mới nhóe.
Tiếp theo nữa, bạn cần xác định giao diện của UIViewController vừa kéo thả là 1 thể hiện của class HomeViewController
nhóe. Bạn làm theo như hình nhóe!
Cuối cùng, bạn cần phải xác định Scene (ViewController) nào sẽ được hiển thị ngay đầu tiên khi khởi động ứng dụng. Bạn chọn vào mỗi Scene và tới mục Attributes Inspector. Xác định Scene nào thì tích vào “Is initial View Controller” của Scene đó.
Build project và cảm nhận kết quả nhóe!
Như vậy, khi bạn muốn hiển thị màn hình (scene) nào, thì hãy tích vào “Is initial View Controller” là okay. Và chúng ta đã xong phần cơ bản với Storyboard rồi. Tiếp theo sang phần Scene & cách kết nối các màn hình lại với nhau.
Scene & Segue
Khái niệm
Mỗi scene (màn hình) sẽ do View Controller quản lý việc hiển thị trên giao diện người dùng. Do đó để thêm scene vào storyboard chính là việc bạn tạo đối tượng View Controller. Phần này, chúng ta đã thực hiện ở trên rồi. Do đó, cách hiểu đơn giản nhất là:
Scene = View Controller
Ứng dụng iOS chỉ có thể hiển thị 01 màn hình tại một thời điểm. Do đó nếu ứng dụng của bạn có nhiều màn hình, thì lúc này bạn sẽ cần một phương pháp nào đó để chuyển đổi qua lại giữa các màn hình. Và Segue sẽ đảm nhiệm việc này, bạn sẽ dùng Segue để chuyển tiếp giữa hai màn hình với nhau.
Có 04 loại Segue:
- Show (e.g. Push): hiển thị view controller mới, nhưng đồng thời đưa view controller cũ trước đó vào stack để có thể “điều hướng” quay trở lại.
- Present Modally: hiển thị view controller mới nằm “đè” lên view controller cũ, khi view controller mới đóng thì view controller cũ vẫn còn được hiển thị.
- Present As Popover: hiển thị view controller vào trong popover.
- Custom: cho phép bạn tự tạo thao tác chuyển tiếp.
Kết nối Scene trên Storyboard
Chúng ta cần tạo 2 Scene (2 View Controller) mới để phục vụ cho ví dụ này. Sau đó, bạn tiến hành thực hiện việc kết nối bằng việc kéo thả trên chính Storyboard.
Đầu tiên, bạn tạo một Button (là cái bắt đầu sự kiện người dùng) và giữ Control để kéo thả Button từ Scene A sang Scene B.
Sau đó, bạn có thể lựa chọn các kiểu di chuyển màn hình. Vì chúng ta đang sử dụng các View Controller riêng lẻ, nên lựa chọn Present Modally. Build và cảm nhận kết quả nhóe!
Transition
Đi kèm với kiểu di chuyển là hiện ứng chuyển cảnh (transition) của từng Segue. Bạn có thể lựa chọn các kiểu mà bạn thích ở mục Transition nhóe.
Bạn có thể tham khảo 04 hiệu ứng chuyển cảnh trong demo dưới đây.
Di chuyển Scene bằng code
Vì một lý do nào đó, mà chúng ta sẽ tiến hành di chuyển sang các màn hình khác bằng việc thực thi code. Lúc này, iOS vẫn hỗ trợ bạn thực hiện công việc đó.
Ví dụ: tại AViewController và sau 5 giây thì chúng ta tiến hành di chuyển sang BViewController nhóe. Bạn tham khảo đoạn code sau:
class AViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() DispatchQueue.main.asyncAfter(deadline: .now() + 5) { self.performSegue(withIdentifier: "SegueGotoBView", sender: self) } } }
Trong đó:
performSegue
sẽ tiến hành gọi Segue thực thi- Xác định Segue dựa vào Identifier của nó
Bạn có thể thêm Identifier cho Segue như hình sau:
Tuy nhiên, công việc này cũng cần một số lưu ý như sau:
- Cần phải kéo thả và tạo các Segue ra trước
- Không thay thế được việc di chuyển bằng kéo thả
- Chỉ giúp trong một vài trường hợp nhất định hoặc tái sử dụng lại Segue mà thôi
Build project và cảm nhận kết quả nhóe!
Điều hướng
Đây là công việc quan trọng nhất cho dù là bạn sử dụng Storyboard hay XIB để tạo giao diện cho ứng dụng. Trong phạm vi bài viết, mình sẽ sử dụng điều hướng cơ bản với UINavigationController. Và cách điều hướng là push & pop.
Đầu tiên, chúng ta cần kéo thả một UINaviagationController vào trong file Storyboard. Và sử dụng Naviagtion Controller với AViewController là root
của nó.
Để xác định 1 Scene là root
của một Navigation Controller, bạn hãy giữ control và kéo thả tới scene đó. EZ game!
Push
Push với Storyboard
Phần này tương tự như việc di chuyện giữa 2 Scene ở trên. Tuy nhiên, chúng ta sẽ dùng kiểu là Push thay cho Present Modal. Bạn vẫn kéo thả và tạo Segue như trên, sau đó hãy chọn là push
nhóe!
Ngay lập tức thì trên Storyboard tại BViewController kia sẽ xuất hiện nút Back Button trong NavigationBar. Và cũng đơn giản vậy thôi, bạn hãy build lại project & cảm nhận kết quả nhóe!
Push với Segue identifier
Phần này sẽ khác với cách dùng code cho Segue. Ta sẽ tạo ra sẵn các Segue của riêng một View Controller và nó không phụ thuộc vào hành động hay sự kiện người dùng nào hết.
Bắt đầu, bạn sẽ cần tạo ra một Segue mới. Bằng việc sử dụng chính đối tượng ViewController này kéo thả tới ViewController khác.
Chúng ta chọn Show (trong Manual Segue) nhóe. Tiếp theo, bạn cần đặt tên cho phần Identifier của Segue. Ví dụ như hình ở dưới.
Cuối cùng, bạn tạo tiếp một IBAction cho bất cứ button nào của ViewController. Hoặc bất cứ sự kiện nào khác cũng được. Sau đó, chúng ta sẽ triệu hồi Segue với Identifier kia. Bạn xem qua code ví dụ nhóe!
@IBAction func gotoHome(_ sender: Any) { self.performSegue(withIdentifier: "segueGotoHome", sender: self) }
Với cách làm này, bạn sẽ:
- Quản lý được các sự kiện di chuyển, nhằm xử lý dữ liệu hoặc gọi api …
- Tận dụng được khả năng trực quan của Storyboard
Công việc này tương tự việc bạn define ra sẵn hết các trường hợp di chuyển sang màn hình khác từ 1 màn hình. Tất cả được thực hiện kéo thả một cách trực quan. Sau đó, tại vị trí nào muốn chúng di chuyển thì bạn sẽ triệu hồi các Segue tương ứng.
Hoặc chúng ta có thể tạo thêm các việc push liên tiếp khi bạn có nhiều màn hình cho một luồng màn hình.
Pop (unwind segue)
Chúng ta đã có việc di chuyển đi tới các màn hình rồi. Giờ chúng ta thực hiện công việc quay lại màn hình trước đó. Phương pháp này sẽ liên quan tới việc sử dụng Storyboard. Ngoài ra, bạn có thể sử dụng bất cứ cách khác để pop hay quay về nhóe, nhưng mình sẽ không đề cập trong bài viết này.
unwind function
Trước tiên chúng ta cần xác định tư tương của Storyboard là thế này:
Ta sẽ cài đặt các unwind segue cho từng màn hình (scene) khác nhau. Do đó, mỗi màn hình sẽ có 1 unwind function riêng của chính nó.
Như vậy, bạn có sơ đồ các màn hình ở trên. Thì chúng ta sẽ cần tạo thêm các function như sau:
- Tại AViewController tạo 1 function
returnAView
- Tại BViewController tạo 1 function
returnBView
Code ví dụ như sau:
// A View class AViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func returnAView(segue: UIStoryboardSegue) { print("back to A View") } } // B View class BViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func returnBView(segue: UIStoryboardSegue) { print("back to B View") } }
Trong đó:
- Vẫn là với @IBAction để có thể kéo thả vào các Button
- Cần tham số với kiểu dữ liệu là UIStoryboardSegue, để Storyboard có thể nhận được
- Nếu bạn không cần xử lý gì thì có thể để trống function đó
Các function này được xem như là điểm cuối cùng mà sự kiện quay trở về màn hình trước đó.
Exit
Tại phần dock của mỗi Scene trong Storyboard, chúng ta có phần Exit. Đó là nới chúng ta sẽ xác định triệu hồi unwind nào.
Bạn sẽ lựa chọn Button nào cần thực hiện hành động quay trở về. Sau đó, bạn nhấn giữ control và kéo chuột tới phần Exit. Lựa chọn function unwind mà mình mong muốn. Như trong hình, Button “Back to A View” sẽ về màn hình A và “Back to B View” sẽ về màn hình B.
Bạn chú ý rằng màn hình A đang root
và chúng ta từ màn hình C di chuyển về. Do đó, đây cũng là cách bạn pop to root
một cách nhanh chóng. Build lại project và cảm nhận kết quả nhóe!
Truyền dữ liệu giữa các màn hình
Chúng ta sẽ thực hiện việc truyền dữ liệu đi giữa các màn hình với nha. Và storyboard cũng hỗ trợ chúng ta thực hiện công việc này luôn. Và điều này đã được View Controller hỗ trợ sẵn phương thức prepareForSegue:sender: để truyền dữ liệu giữa các scene.
Cú pháp như sau:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { }
Trong ví dụ demo của chúng ta, thì sẽ thực hiện việc truyền dữ liệu như sau:
- Tại màn hình A, tạo một UITextField để nhập dữ liệu và truyền cho màn hình Home
- Tại màn hình Home, tạo một UILabel để nhận dữ liệu từ màn hình A.
Màn hình Home với code tiếp nhận như sau:
class HomeViewController: UIViewController { @IBOutlet weak var nameLabel: UILabel! var name: String? override func viewDidLoad() { super.viewDidLoad() nameLabel.text = name ?? "noname" } }
Màn hình A với code truyền dữ liệu như sau:
class AViewController: UIViewController { @IBOutlet weak var nameTextField: UITextField! override func viewDidLoad() { super.viewDidLoad() } @IBAction func returnAView(segue: UIStoryboardSegue) { print("back to A View") } @IBAction func gotoHome(_ sender: Any) { self.performSegue(withIdentifier: "segueGotoHome", sender: self) } // MARK: - Navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let destination = segue.destination as? HomeViewController { destination.name = self.nameTextField.text } } }
Như vậy, bạn chỉ cân quan tâm tới Scene gốc hay Sence trước. Và bạn sẽ chuẩn bị đầy đủ dữ liệu cho destination
(là Scene sau). Còn với màn hình tiếp theo thì xem như đã nhận được dữ liệu đầy đủ khi khởi tạo nó.
Bạn chỉ cần tập trung vào Segue thôi nhóe!
Build lại project và cảm nhận kết quả nha.
Tạm kết
- Tìm hiểu về Storyboard và các thành phần có trong nó
- Sử dụng Segue trong việc quản lý và di duyển giữa các màn hình
- Điều hướng cơ bản trong Storyboard
Okay! Tới đây, mình xin kết thúc bài viết giới thiệu về Storyboard. Nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.
- Bạn có thể checkout code tại đây.
- Bài viết tiếp theo tại đây.
- Tham khảo: Quản lý màn hình với Storyboard và Segue
Cảm ơn bạn đã đọc bài viết này!
Related Posts:
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
Fan page
Tags
Recent Posts
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
- Lập trình hướng giao thức (POP) với Swift
You may also like:
Archives
- 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)