Tích hợp SwiftUI vào UIKit Project – SwiftUI Notes #13
SwiftUI . TutorialsContents
Chào bạn đến với Fx Studio. Chúng ta sẽ tiếp tục tìm hiểu về một phần mới trong hành trình SwiftUI. Phần mới này trình bày về việc tích hợp giữa hai nền tảng SwiftUI & UIKit. Bạn sẽ khám phá ra được rất nhiều điều thú vị từ chúng. Và chủ đề bài viết lần này là Tích hợp SwiftUI vào UIKit Project.
Nếu bạn chưa tìm hiểu về SwiftUI thì có thể tham khảo link bài viết tổng hợp của các phần trước:
Còn nếu mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
Bạn cần phải chuẩn bị thông số về môi trường như sau:
-
- Swift 5.3
- iOS 13.x (hoặc mới hơn)
- SwiftUI 2.0
Vì demo sử dụng cho bài viết lần này vừa dùng cả SwiftUI Project & UIKit Project. Nên bắt buộc iOS phải là 13 (hoặc mới hơn). Ngoài ra, bạn có thể checkout các demo của cả series tại đây.
Tiếp theo, bạn cần chuẩn bị một file SwiftUI View. Nhằm tiết kiệm thời gian thì mình dùng lại code từ các bài trước, đó là file UserView huyền thoại. Bạn tham khảo lại code của nó như sau.
import SwiftUI struct UserView: View { var name: String = "noname" var body: some View { VStack { Image(systemName: "person.crop.square") .resizable() .foregroundColor(.blue) Text(name) .fontWeight(.bold) .multilineTextAlignment(.center) Button(action: { print("Select: \(name)") }) { Text("Tap me!") } } } }
Bạn có thể tạo một file SwiftUI View riêng lẻ hoặc tạo luôn một SwiftUI Project cũng được. Mình sẽ gợi ý bạn nên tạo luôn một project, vì chúng ta sẽ dùng lại nó ở các bài viết sau.
Cuối cùng, bạn tạo tiếp một project với UIKit. Giao diện đơn giản & sử dụng Storyboard. Mình kéo thả giao diện với 2 UIButton như sau.
Vậy là ta đã xong phần chuẩn bị. Tiếp theo, chúng ta chuyển sang phần chính của bài viết.
1. Add SwiftUI Files
Hiện tại, mình đang có file UserView.swift
, nó là một SwiftUI View. Bạn tiến hành thêm nó vào Project bằng cách kéo thả vào Navigator của Project. Xem hình sau nha.
Bạn nhớ là phải thật sự copy file vào project, đừng trỏ Reference tới file. Nhằm tránh hậu hoạ sau này.
Quá EZ! Như vậy là bạn đã thêm được file SwiftUI View vào UIKit Project rồi. Nhưng …
SwiftUI chỉ là code, nó không có
*.xib
như các View/View Controller của UIKit.
Để sử dụng với kiểu giao diện kéo thả, thì bạn tiến hành kéo thả một UIHostingViewControll ở file giao diện (*.xib hoặc *.storyboard)
File StoryBoard của chúng ta sẽ trông như thế này.
Chúng ta đã hoàn thành việc thêm vào UIKit Project rồi. Sang phần sử dụng được các file đó với code của UIKit.
2. UIHostingController
Đầu tiên, bạn sẽ tạo sự kiện bằng cách tiến hành nối UIButton Tap 1
với UIHostingController vừa mới kéo thả. Xác nhận điều hướng nó là push
. Sau đó, bạn kéo thả để tạo IBAction xử lý thêm. Xem hình sau nha.
Kết quả của hành động kéo thả trên sẽ tạo ra một function là @IBSegueAction
. Nó sẽ kết nối với Segue ở StoryBoard. Và trả về một UIViewController. Bạn chỉnh sửa lại code function như sau:
@IBSegueAction func openUserView(_ coder: NSCoder) -> UIViewController? { let user = UserView() return UIHostingController(coder: coder, rootView: user) }
Bạn cần import thêm SwiftUI thì mọi thứ mới hoạt động nha.
import SwiftUI
Vì là Project với UIKit, nên bạn không có Live Preview nha. Bạn sẽ phải bấm run
và cảm nhận kết quả.
Quá đơn giản phải không? Thực tế, tất cả SwiftUI View đều được nhúng trong UIHostingController. Vì thế để nhúng SwiftUI vào UIKit Project, chỉ cần tạo một View Controller bằng cách sử dụng UIHostingController là xong.
Ta tóm tắt lại công việc như sau:
-
- Import SwiftUI framework
- Tạo instance của UIHostingController với rootView là instance của SwiftUIView
- Modal present instance của UIHostingController để hiển thị SwiftUIView
3. Passing Data
Nếu chỉ hiển thị đơn giản View lên trên UIKit, thì quả thật là vô vị. Bây giờ, ta sang phần khó hơn chút là truyền tải dữ liệu qua lại giữa SwiftUI & UIKit.
3.1. UIKit to SwiftUI
Chiều truyền dữ liệu đầu tiên là từ UIKit sang SwiftUI. Cái này khá là đơn giản, bạn chỉ cần
Gán dữ liệu cho đối tượng SwiftUI View được tạo ra.
Vì bản chất, bạn cần phải tạo ra một đối tượng View. Để nó hiển thị những gì bạn muốn, thì bạn có 2 cách sau:
- Truyền dữ liệu vào tham số của các hàm hay hàm khởi tạo các đối tượng.
- Gán trực tiếp dữ liệu vào thuộc tính của các đối tượng.
Ta thay đổi một chút code demo như sau:
- Tại file
UserView
, bạn thêm hàm khởi tạo và xoá đi việc gán dữ liệu cho thuộc tínhname
.
var name: String init(name: String) { self.name = name }
Các này sẽ đảm bảo mọi thứ được riêng tư, đúng theo tính chất của hướng đối tượng trong lập trình.
- Tại file
ViewController
bạn chỉnh lại functionopenUserView
như sau:
@IBSegueAction func openUserView(_ coder: NSCoder) -> UIViewController? { let user = UserView(name: "Fx Studio") return UIHostingController(coder: coder, rootView: user) }
Bạn thêm giá trị "Fx Studio"
cho tham số name
lúc khởi tạo đối tượng UserView. Sau đó, bạn build lại project và cảm nhận kết quả nha.
3.2. SwiftUI to UIKit
Chiều truyền dữ liệu ngược lại thì sẽ hơi khó hơn. Bạn muốn truyền dữ liệu từ SwiftUI sang UIKit, thì có thể sử dụng 2 cách sau:
- Protocol Delegate
- Closure call back
Với Protocol Delegate, mình sẽ không khuyến khích bạn sử dụng cái này. Vì nó là kiểu tham chiếu, nó sẽ kéo theo một loạt các thay đổi. Mình không dám chắc mọi thứ sẽ hoạt động trợ tru. Nhưng thôi và không dùng tới nó cho an tâm.
Chúng ta sẽ áp dụng cách khác, là cách lai giữa 2 thao tác:
Gán dữ liệu + call back
Đầu tiên, bạn mở file UserView thêm 1 typealias & 1 thuộc tính như sau. Tiện thể cập nhật lại hàm init
của nó.
typealias Action = (String) -> Void var name: String var action: Action? init(name: String, action: @escaping Action) { self.name = name self.action = action }
Trong đó:
- Định nghĩa một Action là 1 closure
- Tạo một thuộc tính mới với kiểu là
Action?
, đặt tên làaction
- Thêm
@escaping
vào trước tham số ởinit
. Để capture lại closure cho tiện sử dụng
Vì SwiftUI là View, nên điều kiện để việc truyền dữ liệu xảy thì chỉ khi có sự tác động của người dùng vào nó. Trong ví dụ, chúng ta có thể lợi dụng sự kiện của button
. Tại đó, bạn sẽ dùng thuộc tính action
và truyền dữ liệu vào closure. Code ví dụ như sau:
var body: some View { VStack { Image(systemName: "person.crop.square") .resizable() .foregroundColor(.blue) Text(name) .fontWeight(.bold) .multilineTextAlignment(.center) Button(action: { print("Select: \(name)") if let action = action { action(name) } }) { Text("Tap me!") } } }
Bạn chú ý tới đoạn action
của Button nha. Bạn cần phải kiểm tra xem nó có tồn tại hay không, vì ta đang sử dụng kiểu Optional. Tiếp theo, ta chuyển tới file ViewControler và cập nhật lại function openUserView
. Code ví dụ như sau:
@IBSegueAction func openUserView(_ coder: NSCoder) -> UIViewController? { let user = UserView(name: "Fx Studio") { (name) in print("UIKit > selected: \(name)") } return UIHostingController(coder: coder, rootView: user) }
Chỉ đơn giản, bạn thêm tiếp tham số action
lúc khởi tạo UserView. Sau đó build lại project và tận hưởng kết quả thôi.
4. Navigation
Phần này, chỉ là phụ thôi. Nếu bạn không phải là người thích phong cách kéo thả với Storyboard. Thì sử dụng cách truyền thống để điều hướng từ UIKit sang SwiftUI.
Bạn chỉ cần sử dụng lại UIHostingController làm cấu nối trung gian. Nó là một View Controller và bạn có thể sử dụng để push
& pop
từ đối tượng .navigationController
của nó.
Tham khảo code ví dụ cho xử lý action của UIButton Tap 2
như sau:
@IBAction func openUser2(_ sender: Any) { let user = UserView(name: "Fx Studio 2") { (name) in print("UIKit > selected: \(name)") } let vc = UIHostingController(rootView: user) self.navigationController?.pushViewController(vc, animated: true) }
Trong đó:
- Tạo một đối tượng UIHostingController với tên là
vc
bằng việc xétrootView
là một SwiftUI View - Sử dụng
navigationController
để pushvc
kia vào.
Build lại project và tận hưởng kết quả.
Tạm kết
- Thêm file từ SwiftUI vào UIKit Project
- Sử dụng UIHostingController để nhúng SwiftUI View trong UIKit
- Passing Data giữa SwiftUI & UIKit
- Điều hướng đơn giản với ViewController tạo bằng SwiftUI View
Okay! Tới đây, mình xin kết thúc bài viết này. Và 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.
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
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- 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
You may also like:
Archives
- 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)