← Back to list

สอน SwiftUI: Back to Basic SwiftUI Layout Foundation ที่ควรรู้ — PART 2

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

PNYWT · 2026-05-23 16:41 · 0 claps · 5.6 min read
#swiftui #ios-app-development #swift #mobile-development #programming
Open on Medium ↗
Wiki topics: 💻 · Programming 📱 · Mobile Development

สอน 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

[embed]GitHub — PNYWT/SwiftUI-Stack-Foundation: ทำความเข้าใจ SwiftUI Layout Foundation ทำความเข้าใจ SwiftUI Layout Foundation. Contribute to PNYWT/SwiftUI-Stack-Foundation development by creating an account…github.com

บทความอื่น ๆ


메타데이터
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