สอน SwiftUI: Back to Basic SwiftUI Layout Foundation ที่ควรรู้ — PART 2
หลังจากใน Back to Basic SwiftUI Layout Foundation ที่ควรรู้ — PART 1 เราได้รู้จักพื้นฐานของ Stack Layout อย่าง VStack, HStack, ZStack…
สอน SwiftUI: Back to Basic SwiftUI Layout Foundation ที่ควรรู้ — PART 2

หลังจากใน Back to Basic SwiftUI Layout Foundation ที่ควรรู้ — PART 1 เราได้รู้จักพื้นฐานของ Stack Layout อย่าง VStack, HStack, ZStack รวมถึง Spacer และ Divider กันไปแล้ว
ใน PART 2 นี้ จะเริ่มครอบคลุมการจัดการ View ที่ซับซ้อนขึ้น เช่น การทำหน้าจอที่ Scroll ได้ การแสดงข้อมูลจำนวนมากด้วย Lazy Layout การจัด Grid รวมถึงการนำ Model มาแสดงเป็นรายการ List เหมือนกับที่ทำใน UITableView และ UICollectionView
หัวข้อในบทความนี้
- ScrollView
- LazyVStack / LazyHStack
- LazyVGrid / LazyHGrid
- ScrollView + Model + ForEach
ScrollView
ScrollView คือ Layout ของ SwiftUI ที่ช่วยให้ผู้ใช้สามารถเลื่อน (scroll) เนื้อหาที่มีขนาดยาวเกินพื้นที่หน้าจอได้ รองรับทั้งการ scroll แนวตั้งและแนวนอน จึงเหมาะกับหน้าที่มีข้อมูลจำนวนมาก เช่น feed, form, gallery หรือ card list ต่าง ๆ
โดยค่า default ของ ScrollView จะเป็นการ scroll แนวตั้ง (vertical) แต่สามารถเปลี่ยนเป็นแนวนอนได้ง่าย ๆ ผ่าน parameter ของ initializer
ScrollView Vertical

struct ScrollViewVStackVertical: View {
var body: some View {
ScrollView(.vertical) {
VStack(spacing: 16) {
ForEach(1...20, id: \.self) { index in
Text("Item \(index)")
.font(.headline)
.frame(maxWidth: .infinity)
.frame(height: 80)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.blue.opacity(0.15))
)
}
}
.padding()
}
}
}
ScrollView Horizontal

struct ScrollViewHStackHorizontal: View {
var body: some View {
ScrollView(.horizontal) {
HStack(spacing: 16) {
ForEach(1...10, id: \.self) { index in
Text("Card \(index)")
.font(.headline)
.frame(width: 140, height: 100)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.purple.opacity(0.15))
)
}
}
.padding()
}
}
}
ScrollView เป็น Layout สำคัญใน SwiftUI สำหรับการแสดง content ที่มีขนาดใหญ่เกินหน้าจอ รองรับทั้งแนวตั้งและแนวนอน ใช้งานง่าย และสามารถทำงานร่วมกับ Stack ต่าง ๆ ได้อย่างยืดหยุ่น
LazyVStackและ*LazyHStack* ช่วยให้การ scroll ลื่นขึ้น เพราะ SwiftUI จะ “สร้าง View เท่าที่จำเป็น” เฉพาะ item ที่กำลังจะเห็นบนหน้าจอ
ต่างจาก
*VStackและ `HStack`* จะสร้างทุก View พร้อมกันทันที ซึ่งเมื่อข้อมูลมีจำนวนมาก อาจทำให้กิน memory สูง ใช้ CPU มาก และเกิดอาการ scroll กระตุกได้
Lazy Stack
Lazy Stack คือ Stack Layout ของ SwiftUI ที่ทำงานแบบ “Lazy Loading” โดยจะสร้าง View เฉพาะ item ที่กำลังจะถูกแสดงบนหน้าจอ แทนการสร้างทั้งหมดพร้อมกันตั้งแต่เริ่มต้น
Lazy Loading คือ เทคนิคการโหลดหรือสร้างข้อมูลเมื่อจำเป็น แทนที่จะโหลดพร้อมกันทั้งหมดตั้งแต่แรก
แนวคิดนี้คล้ายกับการใช้ lazy ใน Swift lazy vat text: String = “Hello” ตัวแปรจะยังไม่ถูก initialize ทันที แต่จะถูกสร้างขึ้นเมื่อมีการ access ครั้งแรก หากไม่มีการใช้งานเลย ตัวแปรนั้นก็จะไม่ถูกสร้าง
LazyVStack และ LazyHStack ก็ใช้แนวคิดเดียวกัน โดย SwiftUI จะสร้าง View เฉพาะ item ที่กำลังจะถูกแสดงบนหน้าจอเท่านั้น
แนวคิดนี้ช่วยลดการใช้ RAM และ CPU ทำให้การ scroll มีประสิทธิภาพมากขึ้น โดยเฉพาะหน้าที่มีข้อมูลจำนวนมาก เช่น feed, chat, gallery หรือ product list
ต่างจาก VStack และ HStack ที่จะสร้างทุก View ทันทีตั้งแต่เริ่ม render ซึ่งเมื่อข้อมูลมีจำนวนมาก อาจทำให้กิน memory สูง ใช้ CPU มาก และเกิดอาการ scroll กระตุกได้
ข้อมูล 100 Rows
VStackRender 100 Rows ทีเดียวLazyVStackRender แค่ที่แสดงบนหน้าจอ 10 Rows ก่อน ถ้าเลื่อนไปเรื่อย ๆ ก็ ค่อย Render row ต่อ ๆ ไป
Lazy Stack แบ่งออกเป็น 2 แบบ LazyVStack และLazyHStack
LazyVStack = List แนวตั้งแบบ 1 Column LazyHStack = List แนวนอนแบบ 1 Row
LazyVStack
LazyVStack ทำงานคล้าย VStack โดยจะเรียง View ในแนวตั้งจากบนลงล่าง แต่ SwiftUI จะสร้าง View เฉพาะ item ที่กำลังจะถูกแสดงบนหน้าจอเท่านั้น ช่วยลดการใช้ RAM และ CPU ทำให้เหมาะกับหน้าที่มีข้อมูลจำนวนมาก เช่น Social Feed, Chat หรือ Product List

struct ScrollLazyVerticalView: View {
var body: some View {
ScrollView(.vertical) {
LazyVStack(spacing: 16) {
ForEach(1...100, id: \.self) { index in
RoundedRectangle(cornerRadius: 16)
.fill(Color.blue.opacity(0.15))
.frame(height: 120)
.overlay {
Text("Card \(index)")
.font(.headline)
}
}
}
.padding()
}
}
}
LazyHStack
LazyHStack ทำงานคล้าย HStack โดยจะเรียง View ในแนวนอนจากซ้ายไปขวา แต่ SwiftUI จะสร้าง View เฉพาะ item ที่กำลังจะถูกแสดงบนหน้าจอเท่านั้น ช่วยลดการใช้ RAM และ CPU ทำให้เหมาะกับหน้าที่มีข้อมูลแนวนอนจำนวนมาก เช่น Banner Slider, Card Carousel, Category List หรือ Horizontal Gallery

struct ScrollLazyHorizontalView: View {
var body: some View {
ScrollView(.horizontal) {
LazyHStack(spacing: 16) {
ForEach(1...1000, id: \.self) { index in
RoundedRectangle(cornerRadius: 16)
.fill(Color.purple.opacity(0.15))
.frame(width: 160, height: 120)
.overlay {
Text("Card \(index)")
.font(.headline)
}
}
}
.padding()
}
}
}
Lazy Stack เป็น Layout สำคัญใน SwiftUI สำหรับจัดการข้อมูลจำนวนมาก โดยใช้แนวคิด Lazy Loading เพื่อสร้าง View เฉพาะเมื่อจำเป็น ช่วยลดการใช้ RAM และ CPU ทำให้ application ทำงานได้ลื่นขึ้น
LazyVStackและ LazyHStackมักจะใช้ร่วมกับ ScrollView และเหมาะกับงานประเภท feed, gallery และ list ขนาดใน application
LazyGrid
LazyGrid คือ Layout สำหรับจัดเรียง View ในรูปแบบตาราง โดยทำงานแบบ Lazy Loading เหมือนกับ LazyVStack และ LazyHStack คือ SwiftUI จะสร้าง View เฉพาะ item ที่กำลังจะถูกแสดงบนหน้าจอเท่านั้น แทนการสร้างทุก item พร้อมกันตั้งแต่เริ่มต้น
LazyGrid แบ่งออกเป็น 2 แบบ คือ LazyVGrid และ LazyHGrid
LazyVGrid = Grid แนวตั้งที่แบ่งได้หลาย Columns LazyHGrid = Grid แนวนอนที่แบ่งได้หลาย Rows
LazyVGrid
LazyVGrid คือ Grid แนวตั้ง ใช้สำหรับแสดงข้อมูลเป็นหลาย column และ scroll จากบนลงล่าง เหมาะกับหน้าที่มีข้อมูลจำนวนมาก เช่น Gallery, Product List, Dashboard Card หรือ Menu Grid
นึกภาพไม่ออกกก็ให้นึถถึง UICollectionView แบบแนวตั้ง

struct ScrollLazyGridVertical: View {
let columns = [
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(1...20, id: \.self) { index in
RoundedRectangle(cornerRadius: 16)
.fill(Color.blue.opacity(0.15))
.frame(height: 120)
.overlay {
Text("Item \(index)")
.font(.headline)
}
}
}
.padding()
}
}
}
LazyVGrid ต่างจาก LazyVStack ตรงที่ LazyVStack จัดเรียง item เป็นแถวเดียวแนวตั้ง แต่ LazyVGrid สามารถแบ่ง item ออกเป็นหลาย column ได้โดยตรง
LazyHGrid
LazyHGrid คือ Grid แนวนอน ใช้สำหรับแสดงข้อมูลเป็นหลาย row และ scroll จากซ้ายไปขวา เหมาะกับ UI เช่น Category Grid, Game List, Horizontal Menu หรือ Section ที่ต้องการแสดง item หลายแถวในแนวนอน
นึกภาพไม่ออกกก็ให้นึถถึง UICollectionView แบบแนวนอน

struct ScrollLazyGridHorizontal: View {
let rows = [
GridItem(.fixed(100)),
GridItem(.fixed(100))
]
var body: some View {
ScrollView(.horizontal) {
LazyHGrid(rows: rows, spacing: 16) {
ForEach(1...20, id: \.self) { index in
RoundedRectangle(cornerRadius: 16)
.fill(Color.purple.opacity(0.15))
.frame(width: 140)
.overlay {
Text("Item \(index)")
.font(.headline)
}
}
}
.padding()
}
}
}
LazyHGrid แบ่ง item เป็นหลาย row ได้โดยตรง
ScrollView + Model + ForEach
หลังจากรู้จัก ScrollView, Lazy Stack และ Grid กันแล้ว ขั้นต่อไปคือการนำทั้งหมดมาประยุกต์ใช้กับ Model เพื่อสร้าง list ที่ใกล้เคียงการใช้งานจริงมากขึ้น
LazyVStack

struct User: Identifiable {
let id = UUID()
let name: String
let role: String
let status: String
}
struct ListUserVerticalView: View {
let users = [
User(name: "Alex", role: "iOS Developer", status: "Online"),
User(name: "Mary", role: "UX Designer", status: "Busy"),
User(name: "John", role: "Backend Developer", status: "Offline")
]
var body: some View {
ScrollView(.vertical, showsIndicators: true) {
LazyVStack(spacing: 12) {
ForEach(users) { user in
HStack {
VStack(alignment: .leading) {
Text(user.name)
.font(.headline)
Text(user.role)
.font(.caption)
.foregroundStyle(.gray)
}
.frame(maxWidth: .infinity, minHeight: 44, alignment: .leading)
VStack(alignment: .trailing) {
Text(user.status)
.font(.caption)
.fontWeight(.semibold)
.foregroundStyle(user.status == "Online" ? .green : .gray)
Text("View Profile")
.font(.caption2)
.foregroundStyle(.blue)
}
.frame(maxWidth: .infinity, minHeight: 44, alignment: .bottomTrailing)
}
.padding()
.frame(maxWidth: .infinity)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(.secondarySystemBackground))
.shadow(color: .black.opacity(0.25), radius: 8, y: 8)
)
.padding(.horizontal)
}
}
}
}
}
LazyVGrid

struct ListUserGridView: View {
let users = [
User(name: "Alex", role: "iOS Developer", status: "Online"),
User(name: "Mary", role: "UX Designer", status: "Busy"),
User(name: "John", role: "Backend Developer", status: "Offline")
]
private let columns = [
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
ScrollView(.vertical, showsIndicators: true) {
LazyVGrid(columns: columns, spacing: 12) {
ForEach(users) { user in
VStack(spacing: 8) {
Circle()
.fill(Color.blue.opacity(0.15))
.frame(width: 44, height: 44)
.overlay {
Text(String(user.name.prefix(1)))
.font(.headline)
.foregroundStyle(.blue)
}
Text(user.name)
.font(.headline)
.multilineTextAlignment(.center)
Text(user.role)
.font(.caption)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
Text(user.status)
.font(.caption2)
.fontWeight(.semibold)
.foregroundStyle(user.status == "Online" ? .green : .secondary)
}
.frame(maxWidth: .infinity, minHeight: 140)
.padding()
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color(.secondarySystemBackground))
)
}
}
.padding(.horizontal)
.padding(.vertical)
}
}
}
สรุป
ใน PART 2 นี้ เราได้รู้จัก Layout สำหรับการจัดการ content ที่สามารถ scroll ได้ รวมถึงการแสดงข้อมูลจำนวนมากอย่างมีประสิทธิภาพผ่าน Lazy Stack และ Lazy Grid
- ScrollView สำหรับการ scroll แนวตั้งและแนวนอน
- LazyVStack และ LazyHStack สำหรับแสดงข้อมูลจำนวนมากแบบ lazy loading
LazyVStack เหมาะกับ List แนวตั้งแบบ 1 Column LazyHStack เหมาะกับ List แนวนอนแบบ 1 Row
- LazyVGrid และ LazyHGrid สำหรับจัด layout แบบ grid
LazyVGrid เหมาะกับ Grid แนวตั้งที่มีหลาย Columns LazyHGrid เหมาะกับ Grid แนวนอนที่มีหลาย Rows
- การใช้งานร่วมกับ Model และ ForEach เพื่อให้ใกล้เคียงกับการพัฒนา UI จริง
ทั้งหมดนี้ถือเป็นพื้นฐานสำคัญของการสร้างหน้า Feed, Gallery, Product List, Dashboard และ UI รูปแบบต่าง ๆ ใน SwiftUI
สำหรับ PART 3 จะเริ่มเข้าสู่เรื่องเกี่ยวกับ “พื้นที่และขนาดของ View” เช่น
- frame
- GeometryReader
- background
- overlay
ซึ่งเป็นหัวข้อสำคัญในการควบคุม layout และจัดตำแหน่ง UI ให้ยืดหยุ่นมากขึ้นใน SwiftUI
Github Repository
Repository จะมีทั้งตัวอย่างโค้ด แยกตาม Finder สามารถดูได้ตามแต่ละ Finder เลย ในนั้นทำ Preview ไว้ให้แล้ว SwiftUI-Stack-Foundation
บทความอื่น ๆ
- Back to Basic Swift Foundation ที่ควรรู้
- แชร์การสร้าง Grocery App ด้วย SwiftUI + MVVM + Clean Architecture
- Swift Concurrency เปลี่ยน CallBack ให้กลายเป็น Code ที่อ่านง่ายด้วย Async/Await
- เปลี่ยน func Callback เดิมให้เป็น Async/Await ด้วย Continuation ใน Swift
- Structured Concurrency ใน Swift จัดการ Async Tasks อย่างเป็นระบบด้วย async/await
- สอน SwiftUI: Back to Basic SwiftUI Layout Foundation ที่ควรรู้ — PART 1
- สอน SwiftUI: Back to Basic SwiftUI Layout Foundation ที่ควรรู้ — PART 2
메타데이터
- post_id
- eed08aeb248e
- slug
- สอน-swiftui-back-to-basic-swiftui-layout-foundation-ที่ควรรู้-part-2-eed08aeb248e
- url
- https://medium.com/@punyawat.codercamp/%E0%B8%AA%E0%B8%AD%E0%B8%99-swiftui-back-to-basic-swiftui-layout-foundation-%E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%84%E0%B8%A7%E0%B8%A3%E0%B8%A3%E0%B8%B9%E0%B9%89-part-2-eed08aeb248e
- canonical_url
- https://medium.com/@punyawat.codercamp/%E0%B8%AA%E0%B8%AD%E0%B8%99-swiftui-back-to-basic-swiftui-layout-foundation-%E0%B8%97%E0%B8%B5%E0%B9%88%E0%B8%84%E0%B8%A7%E0%B8%A3%E0%B8%A3%E0%B8%B9%E0%B9%89-part-2-eed08aeb248e
- author_url
- https://medium.com/@punyawat.codercamp
- status
- ok
- fetched_at
- 2026-06-09 15:37:30