Contents
Chào mừng bạn đến với Fx Studio. Chúng ta lại tiếp tục với vũ trụ SwiftUI bất tận này. Bài viết này cũng sẽ tiếp tục về thiết kế giao diện với bố cục Grid Layout. Chủ đề là GridItem, cách bạn sắp xếp bố cục các phần tử trong một Grid.
Nếu bạn chưa biết về Grid Layout trong SwiftUI, thì có thể tìm đọc lại bài viết trước. Còn nếu mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
Về mặt tool và version, các bạn tham khảo như sau:
-
- SwiftUI 3.0
- Xcode 13
Về mặt kiến thức, bạn cần biết trước các kiến thức cơ bản với SwiftUI & SwiftUI App. Tham khảo các bài viết sau, nếu bạn chưa đọc qua SwiftUI:
(Mặc định, mình xem như bạn đã biết về cách tạo project với SwiftUI & SwiftUI App rồi.)
Về mặt demo, bạn chỉ cần thực hiện demo trên các SwiftUI View đơn giản. Mình sẽ thực hiện các View riêng biệt với nhau, nên bạn không cần lo lắng gì nhiều về tính liên kết của các View trong một Project. Về mặt giao diện thì khá là đơn giản à.
(Hoặc bạn có thể checkout project demo tại đây.)
GridItem
GridItem là mô tả cho một ô lưới (a single grid) trong một Grid Layout, chẵng hạn như một hàng (row) hoặc một cột (column).
Dễ hiểu hơn thì nó như là config cho LazyHGrid hoặc LazyVGrid.
Tùy thuộc, đối tượng mà bạn sử dụng là LazyVGrid hay LazyHGrid mà các GridItem sẽ đại diện cho cột hay hàng. Và config này cần một mãng các GridItem để định nghĩa bố cục, không phải là 1 GridItem riêng lẻ. Điều này bạn đã được trải nghiệm trong bài viết trước rồi.
Ví dụ như là:
private var gridItemLayout = [GridItem(.fixed(80), spacing: 8), GridItem(.fixed(80), spacing: 8), GridItem(.fixed(80), spacing: 8)]
Các GridItem sẽ định nghĩa kích thước, hành vi, khoảng cách, căn chỉnh … cho các ô trong Grid. Và số lượng của các GridItem trong array sẽ kiểm soát số lượng hàng hay cột của Grid.
Ngoài ra, các GridItem còn nhận một đối số là GridItemSize. Đây là một đối số bắt buộc bạn phải cung cấp, nó sẽ có nhận 1 trong 3 giá trị:
- flexible
- adaptive
- fixed
Cũng chính từ đó, bạn sẽ có được những giao diện Grid Layout tương ứng theo. Hoặc bạn có thể kết hợp chúng để tạo ra các layout phức tạp hơn nữa.
Flexible
Cú pháp khởi tạo như sau:
GridItem(.flexible())
Flexible là các mà bạn muốn xác định cụ thể Grid của bạn sẽ có là bao nhiêu cột hoặc bao nhiêu hàng, tương ứng với LazyVGrid hoặc LazyHGrid.
Create
Chúng ta hãy lấy một ví dụ đơn giản nhóe. Bạn tham khảo đoạn code sau:
struct FlexibleGridDemo: View { private var gridConfig = [ GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10) ] var body: some View { ScrollView { LazyVGrid(columns: gridConfig, spacing: 10) { ForEach((0...99), id: \.self) { index in Image("img\(index % 10)") .resizable() .aspectRatio(1.0, contentMode: .fill) } } .padding(.horizontal) } .frame(maxHeight: .infinity) } }
Trong đó:
gridConfig
ta tạo sẵn 3 đối tượng GridItem. Điều này có nghĩa chúng ta sẽ có 3 cột với LazyVGrid.- Phần hiển thị LazyVGrid thì vẫn bình thường (như bài viết trước)
Qua đó, bạn chỉ cần tập trung vào số lượng phần tử cho gridConfig
là bao nhiêu phần tử. Từ đó, bạn chủ động quyết định được giao diện của Grid sẽ theo ý bạn với .flexible
Kết quả ban đầu sẽ như vậy.
Columns or Row
Để thấy được sự thay đổi giao diện từ việc số lượng của array GridItem, thì ta sẽ thay đổi code như sau:
private var gridConfig = [ GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10) ]
Như vậy, bạn sẽ có một Grid từ 3 cột thành Grid có 5 cột, tương ứng với array gridConfig
có tổng số phần tử là 5
. Quá đơn giản phải không nào. Bấm Resume và xem kết quả tiếp theo nhóe.
Tới đây, bạn muốn bao nhiêu hàng hoặc cột thì hãy thêm bớt vào Array GridItem. Ngoài ra, chúng ta không cần quan tâm tới việc tính toán kích thước hay khoảng cách các item trong Grid. EZ Game!
minimum & maximum
Bạn có thể quyết định thêm kích thước cho một GridItem với .flexible
thông qua 2 tham số là:
- minimum
- maximum
Giúp cho bạn xác định kích thước nhỏ nhất và lớn nhất. Bạn vẫn có thể cung cấp 1 trong 2 tham số vẫn được. Và bạn cũng không cần thiết phải cung cấp hết cho tất cả các phần tử trong array.
Chúng ta xem qua ví dụ code là hiểu liền à.
private var gridConfig = [ GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10), GridItem(.flexible(minimum: 100), spacing: 10), GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 10) ]
Cột thứ 3 sẽ có kích thước nhỏ nhất là 100px
. Các cột còn lại sẽ được tính toán lại kích thước sao cho phù hợp. Bấm Resume và xem kết quả nha.
Bạn để ý kĩ thì sẽ thấy, 2 cột bên phải có kích thước bé hơn 2 cột bên trái. Điều này xãy ra vì bản chất của layout trong SwiftUI sẽ tính toán kích thước lần lượt từ phần tử đầu tiên. Sau khi đã trừ hết các kích thước cố định (fix & spacing), các kích thước còn lại sẽ được chia đều cho các phần tử còn lại … Lần lượt sẽ được cấp phát hết. (nghe hơi rối phải không … ahihi!)
Bạn có thể đọc thêm về bản chất Layout trong SwiftUI tại bài viết này.
Fixed
Khi bạn chán với việc kích thước của item cứ không theo ý của bạn. Mọi việc sẽ đơn giản nếu bạn xác định luôn 1 kích thước cố định cho item. Thì hãy tạo các GridItem với .fixed
. Và bạn cần cung cấp cho nó 1 con số để xác định kích thước cố định.
Xem ví dụ code nhóe.
struct FixedGridDemo: View { private var gridConfig = [ GridItem(.fixed(100)), GridItem(.fixed(100)), GridItem(.fixed(100)) ] var body: some View { ScrollView { LazyVGrid(columns: gridConfig, spacing: 10) { ForEach((0...99), id: \.self) { index in Image("img\(index % 10)") .resizable() .aspectRatio(1.0, contentMode: .fill) } } .padding(.horizontal) } .frame(maxHeight: .infinity) } }
Trong đó:
- Tổng số cột là 3, tương ứng với tổng item trong Array GridItem là 3
- Kích thước mỗi phần tử là
100px
Bấm Resume và xem kết quả nha!
Bạn thử tăng số lượng items với mục đích cho kích thước tổng các items lớn hơn kích thước màn hình, để xem kết quả sẽ trông như thế nào nhóe.
private var gridConfig = [ GridItem(.fixed(100)), GridItem(.fixed(100)), GridItem(.fixed(100)), GridItem(.fixed(100)), GridItem(.fixed(100)) ]
Không ngoài dự án, chúng sẽ tràn ra ngoài ScrollView. Vì kích thước lúc này của tổng các GridItem lớn hơn kích thước màn hình rồi. Chúng ta cần thay đổi ScrollView một tí, để xem được các phần bị tràn kia. Code ví dụ như sau:
ScrollView([.horizontal, .vertical]) { LazyVGrid(columns: gridConfig, spacing: 10) { ForEach((0...99), id: \.self) { index in Image("img\(index % 10)") .resizable() .aspectRatio(1.0, contentMode: .fill) } } .padding(.horizontal) } .frame(maxHeight: .infinity)
Bạn chỉ cần thêm tham số với [.horizontal, .vertical]
, thì ScrollView sẽ hoạt động với việc scroll 2 chiều. Lúc này, tự động kích thước sẽ được hiển thị đầy đủ và người dùng chỉ việc scroll để xem hết thôi.
Adaptive
Khi bạn không biết trước số lượng hàng hay cột. Chỉ đơn giản là muốn các items tự động lấp đầy hết Grid của bạn. Thì sử dụng GridItem với .adaptive
sẽ giúp bạn rất nhiều. Bạn sẽ cần cung cấp thêm tham số minimum
hoặc maximum
cho chúng. Còn lại SwiftUI sẽ tự động giúp bạn xác định số lượng và kích thước của các items trong Grid sao cho phù hợp nhất.
Điều này rất có ích khi bạn sử dụng các nguồn dữ liệu động, như từ API …
Xem ví dụ code nhóe!
struct AdaptiveGridDemo: View { private var gridConfig = [ GridItem(.adaptive(minimum: 50)) ] var body: some View { ScrollView { LazyVGrid(columns: gridConfig, spacing: 10) { ForEach((0...99), id: \.self) { index in Image("img\(index % 10)") .resizable() .aspectRatio(1.0, contentMode: .fill) } } .padding(.horizontal) } .frame(maxHeight: .infinity) } }
Trong đó:
- Bạn sẽ thấy chỉ cần 1 phần tử GridItem với
.adaptive
duy nhất trong array là được. - SwiftUI sẽ tự động xác định và ráp đầy đủ các item vào Grid
Với .adaptive
thì mọi việc đã đơn giản thì càng đơn giản hơn nữa. EZ game!
Tuy nhiên, bạn vẫn có thể tạo thêm nhiều GridIem .adaptive
trong array config kia. Ví dụ như sau:
private var gridConfig = [ GridItem(.adaptive(minimum: 50)), GridItem(.adaptive(minimum: 80)), GridItem(.adaptive(minimum: 30)) ]
Chúng ta sử dụng các kích thước khác nhau. Giao diện lúc này sẽ khá là ảo ma lazada.
Đây cũng là nhược điểm của SwiftUI, khi chúng nó tự động tính toán và đề xuất kích thước. Hầu như bạn sẽ không kiểm soát được. Do đó, bạn cũng nên cẩn thận khi sử dụng đối tượng này. Ahuhu!
Mixing
Khi bạn kết hợp các loại GridItem với nhau để có được một Grid với các kích thước cho các item thay đổi. Hay còn gọi là trộn các GridItem lại (mixing).
Xem ví dụ code nhóe!
struct MixingGridDemo: View { private var gridConfig = [ GridItem(.fixed(150)), GridItem(.adaptive(minimum: 50)) ] var body: some View { ScrollView { LazyVGrid(columns: gridConfig, spacing: 10) { ForEach((0...99), id: \.self) { index in Image("img\(index % 10)") .resizable() .frame(height: 50) .scaledToFit() } } .padding(.horizontal) } .frame(maxHeight: .infinity) } }
Trong đó:
- Mỗi hàng sẽ được xác định bằng số lượng GridItem trong array config
- Phần tử đầu tiên là
.fixed
, có nghĩa là kích thước cố định và được ưu tiên đầu tiên - Phần tử tiếp theo là
.adaptive
, có nghĩa là các items lần lượt sẽ ráp vào trong hàng đó. Cho tới khi không còn khoảng trống để thêm item nữa.
Phù hợp khi cho bạn muốn nhất mạnh việc chú ý tới một item cụ thể nào đó. Thôi, bấm Resume để xem kết quả nào.
Default
Hoặc đôi khi bạn lười quá, không muốn sài loại gì cho GridItem hết. Cứ đề default như thế này GridItem()
. Code ví dụ như sau:
private var gridConfig = [ GridItem() ]
Kết quả như sau:
(Hình bóng List quen thuộc nhóe!)
Bạn thử thêm 1 GridItem nữa thì xem sao.
private var gridConfig = [ GridItem(), GridItem() ]
Kết quả như sau:
Do đó, bạn sẽ thấy một điều rằng: “mọi thứ hoạt động tự động & các kích thước cho các item cũng tự động được tính toán.” Chúng ta không cần quá suy nghĩ nhiều.
Thế giới đã có SwiftUI lo rồi!
EZ Game!
Tạm kết
- Biết về nhiều kiểu khởi tạo một GridItem
- Ý nghĩa của Array GridItem và sự ảnh hưởng của nó lên Grid Layout
- Kết hợp giữa các loại GridItem với nhau để tạo nên một Grid Layout có giao diện phức tạp
Okay! Tới đây, mình xin kết thúc bài viết về GridItem & Configuring GridLayout trong SwiftUI. 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)