0Pricing
Swift Academy · Lezione

Creazione di un flow layout

Disponi dinamicamente gli elementi su più righe.

Creazione di un flow layout è una lezione Swift Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Swift Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Swift Academy include 4 lezioni in totale.

Che cos'è un layout a flusso

Un layout a flusso dispone gli elementi da sinistra a destra, andando a capo quando l'elemento successivo supererebbe la larghezza disponibile, come accade con i tag o con il testo disposto su più righe.

Il piano

Calcoleremo le interruzioni di riga in un helper condiviso, restituiremo l'altezza totale da sizeThatFits e riutilizzeremo lo stesso calcolo in placeSubviews. Condividere la logica mantiene coerenti i due metodi.

Modellazione di un posizionamento

Una piccola struct registra la posizione di ogni sottovista. Calcolare in anticipo tutti i posizionamenti rende semplici entrambi i metodi del protocollo.

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

L'algoritmo di disposizione su più righe

Scorra le sottoviste tenendo traccia della posizione x corrente. Se una vista figlia supererebbe la larghezza massima, reimposti x e scenda di un'altezza di riga più la spaziatura.

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

Implementazione di sizeThatFits

sizeThatFits esegue l'algoritmo in base alla larghezza proposta e restituisce la larghezza della proposta insieme all'altezza totale calcolata.

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

Implementazione di placeSubviews

placeSubviews esegue lo stesso algoritmo, quindi sposta ogni posizionamento di bounds.origin e chiama 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))
    }
}

Il tipo FlowLayout completo

Unendo i vari elementi si ottiene un layout a flusso completo e riutilizzabile. L'helper condiviso layout garantisce la coerenza tra dimensionamento e posizionamento.

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

Utilizzo del layout a flusso

Lo utilizzi come un qualsiasi contenitore. I tag andranno automaticamente a capo quando la larghezza diminuisce.

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

Gestione del primo elemento di ogni riga

La condizione x > 0 impedisce di andare a capo prima del primo elemento di una riga. Senza di essa, un elemento più largo del contenitore potrebbe entrare in un ciclo infinito o essere spostato inutilmente in una riga vuota.

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

Scelte per la spaziatura

Qui la spaziatura è una proprietà memorizzata con un valore predefinito. In produzione, legga la preferenza spacing di ogni sottovista per rispettare le metriche di sistema invece di usare una costante codificata.

var spacing: CGFloat = 8 // configurable per instance

Test dei casi limite

Esegua test con zero elementi, un solo elemento molto grande e molti elementi piccoli. Un layout a flusso corretto restituisce un'altezza pari a zero per i contenuti vuoti e non ritaglia mai una singola vista figlia troppo grande.

Verifica rapida: layout a flusso

Verifichi la Sua comprensione dell'algoritmo di disposizione su più righe.

Riepilogo: creazione di un layout a flusso

Un layout a flusso dispone gli elementi su più righe tenendo traccia della x corrente e passando a una nuova riga quando la vista figlia successiva supererebbe i limiti. Un helper condiviso calcola tutti i posizionamenti e l'altezza totale, mantenendo coerenti sizeThatFits e placeSubviews.

Sposti i posizionamenti di bounds.origin, gestisca il primo elemento di ogni riga e testi i casi con contenuto vuoto, elementi troppo grandi e molti elementi. Il risultato è un contenitore riutilizzabile con disposizione su più righe.

Domande Frequenti

La lezione «Creazione di un flow layout» è gratuita?

Sì — il testo completo di «Creazione di un flow layout» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Swift Academy, passa a CoddyKit PRO. Il corso Swift Academy include 4 lezioni in totale.

Cosa imparerò in «Creazione di un flow layout»?

Disponi dinamicamente gli elementi su più righe. Eserciti Swift Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Swift Academy?

Non è richiesta alcuna esperienza precedente. Swift Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Creazione di un flow layout»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Swift Academy?

Sì. Ogni lezione Swift Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Basi del protocollo Layout
  2. Misurazione delle subview
  3. Creazione di un flow layout
  4. Cache del layout e prestazioni
← Torna a Swift Academy