0Pricing
Swift Academy · บทเรียน

การสร้างการจัดวางแบบไหล

จัดรายการให้ขึ้นบรรทัดใหม่แบบไดนามิก

การสร้างการจัดวางแบบไหล เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

เลย์เอาต์แบบไหลคืออะไร

เลย์เอาต์แบบไหลจะจัดวางรายการจากซ้ายไปขวา และขึ้นแถวใหม่เมื่อรายการถัดไปจะล้นความกว้างที่มีอยู่ เช่น ป้ายกำกับแบบชิปหรือการตัดคำขึ้นบรรทัดใหม่

แผนการ

เราจะคำนวณจุดแบ่งแถวในฟังก์ชันช่วยที่ใช้ร่วมกัน คืนค่าความสูงทั้งหมดจาก sizeThatFits และนำการคำนวณเดียวกันกลับมาใช้ใน placeSubviews การใช้ตรรกะร่วมกันช่วยให้เมธอดทั้งสองสอดคล้องกัน

การสร้างแบบจำลองการจัดวาง

โครงสร้างขนาดเล็กจะบันทึกตำแหน่งของมุมมองย่อยแต่ละรายการ การคำนวณตำแหน่งการจัดวางทั้งหมดไว้ล่วงหน้าทำให้เมธอดของโพรโทคอลทั้งสองเรียบง่าย

struct Placement {
    let index: Int
    let point: CGPoint
    let size: CGSize
}

อัลกอริทึมการขึ้นแถวใหม่

วนดูมุมมองย่อยทีละรายการพร้อมติดตามค่า x ปัจจุบัน หากมุมมองย่อยจะเกินความกว้างสูงสุด ให้รีเซ็ตค่า x และเลื่อนลงตามความสูงของแถวบวกกับระยะห่าง

func layout(_ subviews: Subviews, maxWidth: CGFloat) -> (placements: [Placement], height: CGFloat) {
    var placements: [Placement] = []
    var x: CGFloat = 0, y: CGFloat = 0, rowHeight: CGFloat = 0
    let spacing: CGFloat = 8
    for (i, s) in subviews.enumerated() {
        let size = s.sizeThatFits(.unspecified)
        if x + size.width > maxWidth, x > 0 {
            x = 0; y += rowHeight + spacing; rowHeight = 0
        }
        placements.append(Placement(index: i, point: CGPoint(x: x, y: y), size: size))
        x += size.width + spacing
        rowHeight = max(rowHeight, size.height)
    }
    return (placements, y + rowHeight)
}

การใช้งาน sizeThatFits

sizeThatFits จะเรียกใช้อัลกอริทึมกับความกว้างที่เสนอ และคืนค่าความกว้างของข้อเสนอคูณด้วยความสูงทั้งหมดที่คำนวณได้

func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
    let maxWidth = proposal.width ?? .infinity
    let result = layout(subviews, maxWidth: maxWidth)
    return CGSize(width: maxWidth == .infinity ? result.placements.map { $0.point.x + $0.size.width }.max() ?? 0 : maxWidth,
                  height: result.height)
}

การใช้งาน placeSubviews

placeSubviews จะเรียกใช้อัลกอริทึมเดียวกัน จากนั้นเลื่อนตำแหน่งการจัดวางแต่ละรายการตาม bounds.origin และเรียกใช้ place

func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
    let result = layout(subviews, maxWidth: bounds.width)
    for p in result.placements {
        let pt = CGPoint(x: bounds.minX + p.point.x, y: bounds.minY + p.point.y)
        subviews[p.index].place(at: pt, anchor: .topLeading,
                                proposal: ProposedViewSize(p.size))
    }
}

ประเภท FlowLayout ฉบับสมบูรณ์

เมื่อนำส่วนต่าง ๆ มารวมกัน จะได้เลย์เอาต์แบบไหลที่สมบูรณ์และนำกลับมาใช้ใหม่ได้ ฟังก์ชันช่วย layout ที่ใช้ร่วมกันรับประกันว่าขนาดและการจัดวางสอดคล้องกัน

struct FlowLayout: Layout {
    var spacing: CGFloat = 8
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let r = layout(subviews, maxWidth: proposal.width ?? .infinity)
        return CGSize(width: proposal.width ?? 0, height: r.height)
    }
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        let r = layout(subviews, maxWidth: bounds.width)
        for p in r.placements {
            subviews[p.index].place(at: CGPoint(x: bounds.minX + p.point.x, y: bounds.minY + p.point.y),
                                    anchor: .topLeading, proposal: ProposedViewSize(p.size))
        }
    }
}

การใช้เลย์เอาต์แบบไหล

นำไปใช้ได้เหมือนคอนเทนเนอร์ทั่วไป ป้ายกำกับจะขึ้นแถวใหม่โดยอัตโนมัติเมื่อความกว้างลดลง

var body: some View {
    FlowLayout {
        ForEach(tags, id: \.self) { tag in
            Text(tag)
                .padding(8)
                .background(.blue.opacity(0.2))
                .clipShape(Capsule())
        }
    }
}

การจัดการรายการแรกของแต่ละแถว

เงื่อนไข x > 0 ป้องกันไม่ให้ขึ้นแถวใหม่ก่อนรายการแรกของแถว หากไม่มีเงื่อนไขนี้ รายการที่กว้างกว่าคอนเทนเนอร์อาจทำให้เกิดการวนซ้ำไม่สิ้นสุด หรือถูกผลักไปยังแถวว่างโดยไม่จำเป็น

if x + size.width > maxWidth, x > 0 {
    // wrap only if the row already has content
}

ตัวเลือกระยะห่าง

ในที่นี้ระยะห่างเป็นคุณสมบัติที่จัดเก็บไว้พร้อมค่าเริ่มต้น สำหรับการใช้งานจริง ให้อ่านค่ากำหนด spacing ของมุมมองย่อยแต่ละรายการ เพื่อเคารพค่ามาตรฐานของระบบแทนการใช้ค่าคงที่ที่กำหนดตายตัว

var spacing: CGFloat = 8 // configurable per instance

การทดสอบกรณีขอบ

ทดสอบด้วยกรณีไม่มีรายการ มีรายการขนาดใหญ่มากหนึ่งรายการ และมีรายการขนาดเล็กจำนวนมาก เลย์เอาต์แบบไหลที่ถูกต้องจะรายงานความสูงเป็นศูนย์เมื่อไม่มีเนื้อหา และจะไม่ตัดมุมมองย่อยที่มีขนาดใหญ่เกินไปแม้แต่รายการเดียว

ตรวจสอบสั้น ๆ: เลย์เอาต์แบบไหล

ทดสอบความเข้าใจเกี่ยวกับอัลกอริทึมการขึ้นแถวใหม่

ทบทวน: การสร้างเลย์เอาต์แบบไหล

เลย์เอาต์แบบไหลจะขึ้นแถวใหม่ให้รายการต่าง ๆ โดยติดตามค่า x ที่เพิ่มขึ้นเรื่อย ๆ และรีเซ็ตไปยังแถวใหม่เมื่อมุมมองย่อยถัดไปจะล้น ฟังก์ชันช่วยที่ใช้ร่วมกันจะคำนวณตำแหน่งการจัดวางและความสูงทั้งหมด เพื่อให้ sizeThatFits กับ placeSubviews สอดคล้องกัน

เลื่อนตำแหน่งการจัดวางตาม bounds.origin ตรวจสอบรายการแรกของแต่ละแถว และทดสอบกรณีไม่มีรายการ รายการใหญ่เกินไป และมีหลายรายการ ผลลัพธ์คือคอนเทนเนอร์แบบขึ้นแถวใหม่ที่นำกลับมาใช้ได้

คำถามที่พบบ่อย

บทเรียน “การสร้างการจัดวางแบบไหล” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การสร้างการจัดวางแบบไหล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การสร้างการจัดวางแบบไหล”

จัดรายการให้ขึ้นบรรทัดใหม่แบบไดนามิก คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การสร้างการจัดวางแบบไหล” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม

ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. พื้นฐานโพรโทคอล Layout
  2. การวัดมุมมองย่อย
  3. การสร้างการจัดวางแบบไหล
  4. แคชการจัดวางและประสิทธิภาพ
← กลับไปที่ Swift Academy