Contents
Chào bạn đến với Fx Studio. Hành trình SwiftUI vẫn tiếp tục với bài viết này. Chủ đề cho bài viết lần này là về thành phần quan trong nhất trong SwiftUI. Đó là Declaring View.
Trước tiên, bạn có thể ôn lại lý thuyết hoặc nếu bạn chưa biết về SwiftUI thì có thể đọc qua các bài viết sau:
Còn nếu mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
1. Tổng quan
Với cách truyền thống, mọi gánh nặng đều được đặt lên Controller. Từ các việc khởi tạo các đối tượng, tới việc bố trí layout, cấu hình cho view. Và quan trọng là phải cập nhật lại chúng khi có sự thay đổi.
Trái ngược, ta có cách tiếp cận khai báo. Và như trong bài viết trước về Declarative App, SwiftUI cung cấp cách tiếp cận với giao diện bằng việc khai báo. Bạn chỉ cần mô tả đơn giản về giao diện của bạn bằng các Declaring View trong hệ thông phân cấp mà ánh xạ với bố cục giao diện của bạn. SwiftUI sẽ quản lý chúng, vẽ, cập nhật lại các View khi có tác động của người dùng hay thay đổi trạng thái.
SwiftUI cung cấp cho bạn các công cụ để định nghĩa và cấu hình View trong giao diện.
- Bạn có thể sử dụng các View nguyên thuỷ. Như là: Button, Text …
- Tạo ra các View của riêng bạn
- Tuỳ chỉnh các view với Modifier
- Tương tác với Dữ liệu
- Tạo nên các giao diện hay phân cấp bằng việc kết hợp các View nhỏ hơn
2. View Protocol
Chúng ta có thể liên tưởng tới UIKit & AppKit. Các View cần phải được định nghĩa với một class cụ thể, như UIView
hay NSView
. Với SwiftUI, mọi việc đơn giản hơn với việc sử dụng View Protocol. Khi bạn cài đặt nó, thì yêu cầu cần phải khai báo thuộc tính body
.
Ta có các View nguyên thuỷ được SwiftUI cung cấp, thì đều kế thừa View Protocol. Ví dụ như Button, Text, VStack … Và bạn có thể tạo nên các Custom View của riêng bạn từ việc kế thừa View Protocol.
struct MyView: View { }
Tuy nhiên, các view của SwiftUI có một chút khác biết với các View mà ta đã làm việc trước đây với UIKit hay AppKit.
- View của SwiftUI về cơ bản
virtual view
. Mô tả cách hiển thị và cho phép SwiftUI chỉ định cách thực hiện việc hiển thị. Ví dụ: khi build SwiftUI lên iOS, thì ứng dụng được xây dựng từ UIView hoặc bằng CoreGraphics. - SwiftUI View có thể định nghĩa bằng các kiểu giá trị (value type), như
struct
. Và thật sự, tất cả các View nguyên thuỷ của SwiftUI đều là struct. Điều này cho phép trình biên dịch cấp phát vùng nhớ ít hơn so với việc cấp phát từ heap.
Bạn muốn sử dụng View Protocol với
class
thì hãy khai báo thêm từ khoáfinal
. Điều này đảm bảo nó không thể có subclass. Mình chưa thử tính năng vui này, lúc nào thứ ổn thì mình cập nhật lại.
3. Khai báo Body
Bạn cần phải cung cấp cho View Protocol một thuộc tính với tên là body
. Nó là một kiểu some View
. Có nghĩa là bạn sẽ cần cung cấp cho nó bất kì class/struct nào đó mà có kế thừa View Protocol đều được.
struct MyView: View { var body: some View { } }
Nếu bạn là người mới tìm hiểu, thì điều này rất đau não.
- View Protocol là một View
- Protocol đó cần 1 thuộc tính là
body
body
lại là một loại View nào đó
Dường như là một vòng lặp vô tận. Nhưng ta có từ khoá some
thì đây là kiểu Opaque Type. Với Opaque Type, là một kiểu mờ đục/mơ hồ … SwiftUI sẽ linh hoạt khi bạn không cần phải khai báo cụ thể body
là một kiểu cụ thể gì. SwiftUI sẽ suy ra chính xác kiểu nào cụ thể cho body
dựa vào nội dung bạn cung cấp cho body
.
4. Xây dựng nội dung cho View
Sau khi đi qua đống lý thuyết mệt mỏi của Declaring View, thì ta sẽ bắt đầu xây dựng nội dung cho View của bạn. Trước tiên sẽ là việc thêm cái gì đó cho body
. Bạn có thể sử dụng các View nguyên thuỷ được SwiftUI cung cấp hoặc custom view … Và ta có kiểu xây dựng nội dung cho View như sau:
4.1. Dạng View nguyên thuỷ
Ví dụ: Hello world
struct MyView: View { var body: some View { Text("Hello, World!") } }
Bạn sẽ thấy body
được cung cấp bằng một Text. Ngoài ra, bạn có thể thay Text bằng Button, Toggle … Đặc trưng của dạng này là các View đó không thể chứa thêm View nào nữa.
Xem như là tận cùng trong phân cấp View rồi.
4.2. Sự kết hợp View
Ví dụ: Hiển thị 2 Text trong một View
struct MyView: View { var body: some View { VStack { Text("Hello, World!") Text("Glad to meet you.") } } }
Muốn thực hiện điều này, bạn phải sử dụng các đối tượng để chứa. Đó là các Container, như VStack, HStack, ZStack, ScrollView … Các Container cũng là các View nguyên thuỷ nhưng đặc trưng của nó là có thể chứa thêm được View.
Các View con sẽ cần phải xác định từ trước và cố định.
4.3. ViewBuilder
Đối với trường hợp, bạn muốn số lượng các View con được hiển thị một cách linh động. Thì thường sử dụng ViewBuilder làm thuộc tính tham số cho các tham số của các closure tạo các view con, cho phép các closure đó cung cấp nhiều chế độ xem con. Bạn sẽ có tối đa là 10 tham số.
Chúng ta sẽ tìm hiểu nó sau. Vì cái này cũng khá là hại não.
Tạm thời mình sẽ ví dụ như sau. Ta sẽ khai báo một Declaring View với custom @ViewBuilder.
struct FxView<Content: View>: View { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } var body: some View { content } }
Khi sử dụng, chúng ta có thể linh hoạt việc cung cấp nội dung vào closure khởi tạo của FxView. Ví dụ:
- Với Text đơn thuần
struct DeclaringView: View { var body: some View { FxView { Text("Hello") } } }
- Với nội dung kết hợp
struct DeclaringView: View { var body: some View { FxView { VStack { Text("1") Text("2") Text("3") } } } }
Qua trên, ta đã thoát khỏi kiếp some View
rồi nhoé!
5. Cấu hình View với Modifiers
Với các Declaring View, chúng ta cần phải thay đổi giá trị thuộc tính của chúng. Để cho hiển thị theo đúng design hoặc theo đúng ý đồ. Những thuộc tính thường xuyên cần phải thay đổi như là: font, color, size … Nhưng đây là SwiftUI và ứng dụng của chúng lại là Declarative App. Nên việc thay đổi giá trị của thuộc tính đó sẽ khác cách truyền thống.
Đó là các Modifiers.
Các Modifiers là các method của View. Chúng sẽ không những thay đổi giá trị thuộc tính, mà chúng sẽ nhân bản đối tượng View và kèm với việc thay đổi giá trị của thuộc tính. Và đối tượng View nhân bản đó sẽ thay thế cho đối tượng View ban đầu. Vì tất cả đều là value type
nên chúng ta sẽ không cần lo lắng về bộ nhớ không được giải phóng.
Ta xem ví dụ sau để thay đổi màu chữ.
Text("Hello, World!") .foregroundColor(.red) // Display red text.
Điều này trái ngược với cách tiếp cận truyền thống. Đối tượng của bạn vẫn tồn tại, các thuộc tính sẽ được thay đổi. Và bạn sẽ phải ra lệnh cho chương trình cập nhật lại đối tượng trên giao diện.
let label = UILabel() label.text = "Hello, World" label.textColor = .red
Cuối cùng, bạn muốn thay đổi nhiều thuộc tính của một View. Thì bạn có thể gọi các Modifiers liên tiếp nhau. Nhưng điều cần chú ý …
Thứ tự của việc gọi các Modifiers sẽ quyết định tới việc hiển thị của View.
Ví dụ thay đổi 2 thuộc tính của Text như sau:
Text("Title") .frame(width: 100) .border(Color.gray)
Sẽ khác với ví dụ thay đổi dưới đây, bằng cách đảo ngược lại các Modifiers.
Text("Title") .border(Color.gray) // Apply the border first this time. .frame(width: 100)
Bạn sẽ có 2 giao diện hoàn toàn khác nhau với 2 ví dụ trên.
6. Phân cấp
Một điều hiển nhiên, giao diện ứng dụng của bạn sẽ rất là phức tạp. Bạn cần phải kết hợp nhiều loại Declaring View khác nhau để tạo nên chúng. Bạn dựa vào tính chất của một View mà có sử dụng cho hợp lý.
- Với các View nguyên thuỷ, như: Text, Button, Label … bạn hãy sử dụng chúng cho các phần chi tiết nhỏ nhất & cuối cùng trong hệ thống phân cấp View.
- Với các Layout, Stack … bạn hãy kết hợp các Stack với nhau để tạo thành bố cục cho giao diện. Sau đó, các View con sẽ được ráp vào. Nếu giao diện phức tạp quá thì hãy tiếp tục chia nhỏ bố cục.
- Cuối cùng, SwiftUI khuyến khích việc tái sử dụng lại các View. Bạn sẽ tạo ra các class/struct cho các View mà bạn sẽ dùng lại nhiều lần. Việc này sẽ làm cho code của bạn đơn giản hơn nhiều.
Ví dụ: bạn có thể thay thế nội dung body
của ContentView bằng MyView hoặc một View nào đó mà bạn tự tạo ra.
struct ContentView: View { var body: some View { MyView(helloFont: .title) } }
Tạm kết
Qua trên, mình trình bày các điểm lý thuyết về Declaring View trong SwiftUI. Ngoài ra, các điểm cần chú y khi làm việc với View. Bài viết này mang tính chất cơ bản để giúp bạn có cái nhìn tổng quát về Declaring View. Các bài viết tiếp theo hoặc ở phần tiếp theo sẽ đi vào chi tiết cụ thể điểm lý thuyết ở trên. Ví dụ, như ViewBuilder là cả một bầu trời kiến thức.
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.
- Bài viết tiếp theo tại đây.
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
- CO-STAR – Công thức vàng để viết Prompt hiệu quả cho LLM
- Prompt Engineering trong 10 phút
- Một số ví dụ sử dụng Prompt cơ bản khi làm việc với AI
- Prompt trong 10 phút
- 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
You may also like:
Archives
- 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)