Contents
Chào bạn đến với Fx Studio. Bài viết này sẽ giúp bạn tạo một Subview với SwiftUI. Và cũng là bài viết tiếp nối của series SwiftUI Notes.
Bạn có thể đọc lại bài viết trước tại đây, nhằm để bổ sung thêm kiến thức trước khi vào bài viết này.
Và nếu như mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
Về mặt cấu hình và phiên bản sử dụng thì như sau:
-
- Xcode 12
- Swift 5.3
- SwiftUI 2.0
- MacOS 10.15.x
Về demo bài viết, chúng ta sử dụng tiếp project ở bài trước. Bạn có thể checkout code tại đây:
-
- Toàn bộ repo: https://github.com/fx-studio/swiftui-notes
- Project bài trước: https://github.com/fx-studio/swiftui-notes/tree/main/004-PresentingAlert
Với bài viết này, bạn có thể sử dụng code của ContentView từ bài viết trước. Tạm thời, mình sẽ dụng đoạn code tạo giao diện từ bài viết trước như sau:
VStack(alignment: .center) { Button(action: { if number < 20 { number += 1 } else { isShowAlert = true message = "Maximum" } }) { Image(systemName: "arrowtriangle.up.fill") .resizable() .frame(width: 50.0, height: 50.0) .foregroundColor(.red) } .frame(width: 50.0, height: 50.0) Text("\(number)") .font(.title) .fontWeight(.bold) .foregroundColor(Color.blue) .multilineTextAlignment(.center) Button(action: { if number > 0 { number -= 1 } else { isShowAlert = true message = "Minimum" } }) { Image(systemName: "arrowtriangle.down.fill") .resizable() .frame(width: 50.0, height: 50.0) } .frame(width: 50.0, height: 50.0) }
Giao diện hiển thị sẽ trông như thế này:
Tại bài viết trước, chúng ta có một vấn đề cần phải được giải quyết ngày. Đó là:
Số dòng code phát sinh trong SwiftUI quá dài.
1. Tái sử dụng
Mọi việc đơn giản khi mỗi View được tạo ra là duy nhất. Tuy nhiên với các ứng dụng, nhiều Control lặp lại trong chính một màn hình. Ta có bài toán mới với giao diện như sau:
Ta sẽ có tới 3 block giao diện nhỏ trong một màn hình. Và chúng giống nhau. Nếu như bạn suy nghĩ code của nó như thế nào. Thì bạn hãy nhìn đoạn code ở phần Chuẩn bị. Các bước như sau:
- Đưa block view cơ bản đầu tiền vào một HStack
- Sao chép lại block code view cơ bản đó
- Dán lại chúng nó thêm 2 lần nữa vào trong HStack
Cũng khá là EZ nhỉ!
Chúng ta sẽ nhân 3 lần số dòng code đó lên. Tuy nhiên, ở đời không ai làm như vậy cả. Vậy giải pháp kinh điểm muôn đời đó là:
Tạo một Subview, để tái sử dụng nhiều lần.
(Xin lỗi vì file code lúc này quá dài nên mình xin phép không bỏ vào đây!)
2. Extract Subview
Bạn sẽ bắt đầu lại bằng file code ở phần Chuẩn bị. Và ta có code của ContentView như sau:
struct ContentView: View { @State var number: Int = 10 @State var isShowAlert: Bool = false @State var message: String = "" var body: some View { HStack { VStack(alignment: .center) { Button(action: { if number < 20 { number += 1 } else { isShowAlert = true message = "Maximum" } }) { Image(systemName: "arrowtriangle.up.fill") .resizable() .frame(width: 50.0, height: 50.0) .foregroundColor(.red) } .frame(width: 50.0, height: 50.0) Text("\(number)") .font(.title) .fontWeight(.bold) .foregroundColor(Color.blue) .multilineTextAlignment(.center) Button(action: { if number > 0 { number -= 1 } else { isShowAlert = true message = "Minimum" } }) { Image(systemName: "arrowtriangle.down.fill") .resizable() .frame(width: 50.0, height: 50.0) } .frame(width: 50.0, height: 50.0) } } .alert(isPresented: $isShowAlert, content: { Alert(title: Text("Error"), message: Text(message)) }) } }
Bạn hãy đưa con trỏ về VStack và nhấn tổ hợp phím Command + click. Sau đó chọn như hình dưới đây.
Sau đó, bạn bấm Enter và đổi tên đoạn code của mình vừa mới tách ra.
Rất nhanh chóng, với chức này hữu ích này của Xcode thì bạn đã nhanh chóng có một Custom View theo đúng cú pháp của SwiftUI rồi.
3. Hoàn thiện
Tuy nhiên, vì View và các Subview sẽ bị ràng buộc với nhau bởi các thuộc tính. Nên MyView mới sẽ bị lỗi. Công việc của bạn là fix chúng nó.
Đầu tiên, chuyển các thuộc tính ở ContentView về MyView.
@State var number: Int @Binding var isShowAlert: Bool @Binding var message: String
Có một chút khác biệt ở đây. Đó là @Binding
. Vì chúng ta thay đổi giá trị tại Subview và giá trị này sẽ ảnh hưởng tới thuộc tính ở ContentView.
@Binding
là một Property Wrappers trong SwiftUI, nó cho phép chúng ta khai báo một giá trị thực chất được khai báo ở một nơi khác, nhưng được chia sẻ lẫn nhau.
Và ta sẽ khởi tạo MyView trong ContentView như sau:
HStack { MyView(number: 10, isShowAlert: $isShowAlert, message: $message) }
Chú ý, bạn sẽ thấy các thuộc tính truyền vào hàm khởi tạo của MyView và bạn phải thêm từ khoá $
vào. Nôm na là báo cho View con kia biết địa chỉ gốc của thuộc tính mà dữ liệu mà nó sẽ gởi về mà thôi.
Okay! Ta sẽ có code hoàn thiện của MyView như sau:
struct MyView: View { @State var number: Int @Binding var isShowAlert: Bool @Binding var message: String var body: some View { VStack(alignment: .center) { Button(action: { if number < 20 { number += 1 } else { isShowAlert = true message = "Maximum" } }) { Image(systemName: "arrowtriangle.up.fill") .resizable() .frame(width: 50.0, height: 50.0) .foregroundColor(.red) } .frame(width: 50.0, height: 50.0) Text("\(number)") .font(.title) .fontWeight(.bold) .foregroundColor(Color.blue) .multilineTextAlignment(.center) Button(action: { if number > 0 { number -= 1 } else { isShowAlert = true message = "Minimum" } }) { Image(systemName: "arrowtriangle.down.fill") .resizable() .frame(width: 50.0, height: 50.0) } .frame(width: 50.0, height: 50.0) } } }
Và code của ContentView như sau:
struct ContentView: View { @State var isShowAlert: Bool = false @State var message: String = "" var body: some View { HStack { MyView(number: 10, isShowAlert: $isShowAlert, message: $message) MyView(number: 10, isShowAlert: $isShowAlert, message: $message) MyView(number: 10, isShowAlert: $isShowAlert, message: $message) } .alert(isPresented: $isShowAlert, content: { Alert(title: Text("Error"), message: Text(message)) }) } }
Bạn hãy bấm Live Preview và tự cảm nhận kết quả nha! Giờ thì code của ContentView đã thu gọn đi nhiều lần rồi.
Tạm kết
- Cách tách một đoạn code thành một Subview
- Binding dữ liệu từ View con về View cha
@Binding
vs.@State
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)