0Pricing
Swift Academy · Lekcja

Budowanie układu przepływowego

Dynamiczne przenoszenie elementów do kolejnych wierszy.

Budowanie układu przepływowego to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Swift Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Swift Academy zawiera 4 lekcji w sumie.

Czym jest układ przepływowy?

Układ przepływowy rozmieszcza elementy od lewej do prawej, przenosząc je do nowego wiersza, gdy kolejny element nie zmieściłby się w dostępnej szerokości — podobnie jak znaczniki lub zawijanie tekstu.

Plan

Obliczymy podziały wierszy we wspólnej funkcji pomocniczej, zwrócimy całkowitą wysokość z sizeThatFits i ponownie użyjemy tych samych obliczeń w placeSubviews. Współdzielenie logiki zapewnia spójność obu metod.

Modelowanie rozmieszczenia

Niewielka struktura zapisuje miejsce, w którym ma zostać umieszczony każdy podwidok. Wcześniejsze obliczenie wszystkich rozmieszczeń upraszcza obie metody protokołu.

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

Algorytm zawijania

Przejdź po podwidokach, śledząc bieżącą wartość x. Jeśli dziecko przekroczyłoby maksymalną szerokość, wyzeruj x i przesuń się w dół o wysokość wiersza powiększoną o odstęp.

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)
}

Implementowanie sizeThatFits

sizeThatFits wykonuje algorytm dla proponowanej szerokości i zwraca szerokość z propozycji oraz obliczoną całkowitą wysokość.

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)
}

Implementowanie placeSubviews

placeSubviews wykonuje ten sam algorytm, następnie przesuwa każde rozmieszczenie o bounds.origin i wywołuje 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))
    }
}

Pełny typ FlowLayout

Połączenie tych elementów daje kompletny, wielokrotnego użytku układ przepływowy. Wspólna funkcja pomocnicza layout gwarantuje zgodność rozmiaru i rozmieszczenia.

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))
        }
    }
}

Korzystanie z układu przepływowego

Można używać go jak dowolnego kontenera. Znaczniki automatycznie przechodzą do kolejnych wierszy, gdy szerokość się zmniejsza.

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

Obsługa pierwszego elementu w wierszu

Warunek x > 0 zapobiega zawijaniu przed pierwszym elementem wiersza. Bez niego element szerszy niż kontener mógłby powodować nieskończoną pętlę lub niepotrzebnie trafić do pustego wiersza.

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

Wybór odstępów

W tym przypadku odstęp jest przechowywaną właściwością z wartością domyślną. W kodzie produkcyjnym należy odczytywać preferencję spacing każdego podwidoku, aby respektować metryki systemowe zamiast używać stałej wpisanej na sztywno.

var spacing: CGFloat = 8 // configurable per instance

Testowanie przypadków brzegowych

Przetestuj układ z zerową liczbą elementów, jednym bardzo dużym elementem oraz wieloma małymi elementami. Poprawny układ przepływowy zgłasza wysokość równą zero dla pustej zawartości i nigdy nie przycina pojedynczego dziecka większego niż kontener.

Szybkie sprawdzenie: układ przepływowy

Sprawdź swoją wiedzę na temat algorytmu zawijania.

Podsumowanie: tworzenie układu przepływowego

Układ przepływowy zawija elementy, śledząc bieżącą wartość x i przechodząc do nowego wiersza, gdy kolejny element nie zmieściłby się w dostępnej szerokości. Wspólna funkcja pomocnicza oblicza wszystkie rozmieszczenia i całkowitą wysokość, dzięki czemu sizeThatFits i placeSubviews pozostają spójne.

Przesuwaj rozmieszczenia o bounds.origin, uwzględniaj pierwszy element każdego wiersza i testuj przypadki pustej zawartości, elementów zbyt dużych oraz wielu elementów. Rezultatem jest wielokrotnego użytku kontener zawijający elementy.

Często zadawane pytania

Czy lekcja „Budowanie układu przepływowego” jest bezpłatna?

Tak — pełny tekst „Budowanie układu przepływowego” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Swift Academy, przejdź na CoddyKit PRO. Kurs Swift Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Budowanie układu przepływowego”?

Dynamiczne przenoszenie elementów do kolejnych wierszy. Ćwiczysz Swift Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Swift Academy?

Nie wymagamy żadnego doświadczenia. Swift Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Budowanie układu przepływowego”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Swift Academy?

Tak. Każda lekcja Swift Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Podstawy protokołu Layout
  2. Mierzenie podwidoków
  3. Budowanie układu przepływowego
  4. Pamięć podręczna układu i wydajność
← Powrót do Swift Academy