Table of Contents
Introduzione alla Dynamic Collection Views in SwiftUI
Le applicazioni iOS moderne richiedono fluidi, interfacce adattative che rispondono con grazia a diverse dimensioni dei dati, orientamenti dei dispositivi e dimensioni dello schermo. In UIKit, le viste di raccolta forniscono un potente ma verbose meccanismo per la costruzione di layout simili a griglia. SwiftUI semplifica notevolmente questo con il suo codice dichiarativo API, in particolare attraverso LazyVGrid e iPhone
Un layout di visualizzazione della collezione dinamica[[] adatta il suo numero di colonne, altezza della riga, spaziatura, o comportamento di dimensionamento basato sulla larghezza del contenitore disponibile, il numero di elementi, o il contenuto stesso. Questo articolo ti guiderà attraverso la costruzione di tali layout in SwiftUI, coprendo tutto dalla configurazione della griglia di base ai comportamenti adattativi avanzati, la messa a punto delle prestazioni e l'integrazione con le animazioni.
Componenti principali: LazyVGrid e LazyHGrid
SwiftUI fornisce due contenitori di griglia primaria: LazyVGrid (Griglia di scorrimento verticale) e LazyHGrid (Griglia di scorrimento orizzontale) Entrambi caricano pigramente i loro figli, il che significa che le viste vengono create solo quando diventano visibili sullo schermo – una caratteristica cruciale per la gestione dei grandi data set di memoria
Configurazione GridItem
Per definire la struttura di una griglia, si crea una serie di istanze GridItem[]. Ciascuna ] rappresenta una singola colonna (in ]) o una singola riga (in ]).
- Size[] – Una delle tre modalità di dimensionamento: , , o .
- Pacing[] – Spaziatura orizzontale tra elementi nella stessa riga (per griglie verticali) o spaziatura verticale (per griglie orizzontali).
- Alignment[] – Controlla come gli elementi si allineano all'interno di una cella di rete.
La scelta della modalità di dimensionamento definisce la flessibilità complessiva del layout:
- Fixed[ – Larghezza esplicita (o altezza) per ogni colonna/row. Utile quando avete bisogno di dimensionamento esatto, ad esempio, un layout a tre colonne di celle a 100-punto fissi.
- Flexible – Ogni elemento della griglia può allungarsi per riempire lo spazio disponibile, con limiti minimi e massimi opzionali.
- Adaptive[] – La griglia determina automaticamente quanti elementi si adattano per riga di colonna in base a una dimensione minima specificata.
Ad esempio, per creare una griglia che si adatta automaticamente a quante più colonne possibile, ognuna con una larghezza minima di 100 punti, scriveresti:
let columns = [
GridItem(.adaptive(minimum: 100))
]
Questa singola linea fornisce un layout che si adatta da una colonna su un iPhone stretto a diverse colonne su un iPad in paesaggio – esattamente il comportamento che vogliamo per layout dinamici.
Costruire una griglia dinamica di base
Iniziamo con una griglia semplice e dinamica usando sizing. Di seguito è una vista completa che visualizza una serie di stringhe in una griglia reattiva:
struct AdaptiveGridView: View {
let items = Array(1...20).map { "Item \($0)" }
var body: some View {
ScrollView {
LazyVGrid(
columns: [GridItem(.adaptive(minimum: 80), spacing: 12)],
spacing: 12
) {
ForEach(items, id: \.self) { item in
Text(item)
.frame(minWidth: 80, maxWidth: .infinity, minHeight: 80)
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
}
}
.padding()
}
}
}
Qui, ogni elemento è costretto ad almeno 80 punti di larghezza e di altezza. L'elemento griglia calcola automaticamente quanti elementi possono adattarsi per riga data la larghezza disponibile, rispettando la larghezza minima. Il risultato è un layout che rifluisce senza soluzione di continuità quando il dispositivo ruota o la finestra è ridimensionata (su iPad o Mac).
Questo approccio funziona bene per contenuti semplici, ma per un maggior controllo è spesso necessario combinare con [] elementi, o utilizzare per calcolare i conteggi delle colonne personalizzate.
Utilizzo di GeometryReader per i conti di colonna dinamica precisi
Mentre gestisce molti casi, a volte si desidera impostare un numero esatto di colonne in base alla larghezza del contenitore – per esempio, sempre 2 colonne su un iPhone in ritratto, 3 in paesaggio, e 4 su iPad. Il GeometryReader vista fornisce la dimensione attuale del suo genitore, consentendo di calcolare il conteggio colonna ottimale dinamicamente.
Esempio: Un conteggio colonna adattivo
La seguente visualizzazione legge la larghezza disponibile e calcola un conteggio di colonne in base a una larghezza dell'elemento di destinazione:
struct DynamicColumnGridView: View {
let items = Array(1...30).map { "Item \($0)" }
let targetItemWidth: CGFloat = 120
var columns: [GridItem] {
let count = max(Int(geometryProxy.size.width / targetItemWidth), 1)
return Array(repeating: GridItem(.flexible(), spacing: 12), count: count)
}
var body: some View {
GeometryReader { geometry in
ScrollView {
let width = geometry.size.width
let count = max(Int(width / targetItemWidth), 1)
let columns = Array(repeating: GridItem(.flexible(), spacing: 12), count: count)
LazyVGrid(columns: columns, spacing: 12) {
ForEach(items, id: \.self) { item in
Text(item)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 80)
.background(Color.green.opacity(0.3))
.cornerRadius(8)
}
}
.padding()
}
}
}
}
Important:[] In pratica, è necessario memorizzare il valore [ all'interno di una variabile o usarlo nella stessa chiusura. L'esempio precedente utilizza una variabile di aiuto all'interno della chiusura per chiarezza.
Questa tecnica ti dà il pieno controllo sul numero di colonne mantenendo gli elementi flessibili in modo da riempire uniformemente la larghezza. Puoi anche aggiungere vincoli minimi e massimi agli elementi flessibili per evitare le celle estremamente allungate o schiacciate.
Adattare le altezze della riga dinamicamente
SwiftUI non fornisce una griglia di "sorzamento automatico" diretta come UIKit [] con dimensioni stimate. Tuttavia, è possibile ottenere risultati simili utilizzando dimensioni intrinseche del contenuto e o incorporando altezze data-driven.
Per esempio, è possibile fare ogni dimensione della cella della griglia omettendo altezze esplicite e lasciando che il contenuto interno determina la dimensione. SwiftUI calcola automaticamente l'altezza della cella in base all'elemento più alto in ogni riga (per le griglie verticali). Ma se avete bisogno di altezze di riga veramente variabili (come un layout della masonry), è necessario un approccio personalizzato – spesso usando con le guide di allineamento personalizzato o avvolgere la colonna in un [Favanti]
Gestione di grandi set di dati con carico pigro
Uno dei principali vantaggi di e è il loro comportamento di carico pigro. Essi creano solo viste per gli elementi che sono attualmente visibili (più un piccolo buffer), che li rende efficienti per i set di dati con centinaia o anche migliaia di elementi. Tuttavia, ci sono alcune migliori pratiche per mantenere le prestazioni lisce:
- ]Utilizza gli identificatori stabili[[] – Fornire sempre un parametro unico [] []. Per i modelli di dati che si conformi a , SwiftUI può diffare in modo efficiente.
- Avoid calcolo costoso all'interno del corpo[[] – Tenere le viste delle cellule semplici. Se avete bisogno di calcoli complessi di layout, precomputali fuori dalla griglia.
- Leverage [] – Se la tua vista cellulare si conforma a []] o utilizza il modifica, SwiftUI può saltare le modifiche di rendering quando i dati della cella non sono cambiati.
- ] Usa ] all'interno di un [] – Se si nidifica un all'interno di un altro contenitore pigro, si può rompere carico pigro. In genere, la griglia stessa dovrebbe essere il figlio diretto di un (o essere avvolto all'interno di uno).
Combinare i layout dinamici con le animazioni
Le immagini in SwiftUI possono rendere i cambiamenti di layout si sentono naturali e lucidi. Ad esempio, quando il numero di colonne cambia (a causa di rotazione o ridimensionamento della finestra), è possibile animare gli elementi della griglia alle loro nuove posizioni. Il modo più semplice è quello di avvolgere la griglia in un modificatore o utilizzare il ] modificatore con un valore che innesca cambiamento.
Cambiamenti di colonna animatrice
Ecco un'estensione dell'esempio della colonna dinamica precedente con una transizione animata:
struct AnimatedDynamicGridView: View {
let items = Array(1...20).map { "Item \($0)" }
@State private var targetItemWidth: CGFloat = 120
var body: some View {
GeometryReader { geometry in
ScrollView {
let width = geometry.size.width
let count = max(Int(width / targetItemWidth), 1)
let columns = Array(repeating: GridItem(.flexible(), spacing: 12), count: count)
LazyVGrid(columns: columns, spacing: 12) {
ForEach(items, id: \.self) { item in
Text(item)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 80)
.background(Color.orange.opacity(0.4))
.cornerRadius(8)
.transition(.scale.combined(with: .opacity))
}
}
.padding()
.animation(.spring(response: 0.4, dampingFraction: 0.7), value: count)
}
}
}
}
Digitando l'animazione al valore , SwiftUI animerà l'inserimento, la rimozione e il riarrangiamento degli elementi quando il numero di colonne cambia. Puoi anche usare per transizioni più avanzate tra layout.
Esempi pratici: Real‐World Dynamic Layouts
Passiamo attraverso due scenari comuni che beneficiano di layout di visualizzazione di collezione dinamica.
Galleria fotografica Griglia
Una galleria fotografica tipica utilizza una griglia quadrata che si adatta alla larghezza dello schermo. Si desidera che le celle siano il più grande possibile, mantenendo un numero fisso di colonne (ad esempio, 2 su, 3 su iPhone Plus, 4 su iPad).
struct PhotoGalleryGrid: View {
let images: [String]
@Environment(\.horizontalSizeClass) var sizeClass
var columns: [GridItem] {
let count = sizeClass == .compact ? 2 : 4
return Array(repeating: GridItem(.flexible(), spacing: 2), count: count)
}
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 2) {
ForEach(images, id: \.self) { imageName in
Image(imageName)
.resizable()
.aspectRatio(1, contentMode: .fill)
.clipped()
}
}
.padding(.horizontal, 2)
}
}
}
Lavagna dinamica con dimensioni variabili delle celle
Per un cruscotto, si potrebbero avere celle di diverse larghezze – ad esempio, un grande grafico che raccoglie due colonne, statistiche più piccole che assumono una colonna ciascuna. È possibile ottenere questo combinando elementi con moltiplicatori espliciti utilizzando un array personalizzato .
let columns: [GridItem] = [
GridItem(.flexible(minimum: 80), spacing: 12), // small stat cell
GridItem(.flexible(minimum: 160), spacing: 12), // wide cell
GridItem(.flexible(minimum: 80), spacing: 12) // small stat cell
]
Poi, nella griglia , è possibile mappare diversi tipi di dati a diverse campate di colonne avvolgendo gli elementi in un contenitore che si espande per riempire la larghezza della griglia. Questo richiede un coordinamento attento perché []] non supporta in modo nativo le campate multi-colonna.
Per iOS 16 e versioni successive, è possibile utilizzare il modificatore su una vista all'interno della griglia per specificare quante colonne che la vista dovrebbe abbracciare:
LazyVGrid(columns: columns, spacing: 12) {
ForEach(dashboardItems, id: \.id) { item in
DashboardCell(item: item)
.gridCellColumns(item.columnSpan)
}
}
Questo è il modo più pulito per creare un vero layout dinamico del cruscotto con dimensioni di celle miste.
Considerazioni sulle prestazioni per grandi set di dati
Quando si tratta di migliaia di elementi, anche il carico pigro può portare a ritardi se ogni cella è complessa. Ecco tecniche avanzate per mantenere le griglie dinamiche liscia:
- Prefetch immagini e dati[[] – Usa [] o ] per caricare i dati in modo asincrono, non nel corpo della cella.
- ]Usa per liste estremamente grandi – SwiftUI [] è più ottimizzato per migliaia di file con dimensionamento dinamico. Tuttavia, per un layout di griglia, con un layout personalizzato è possibile ma più difficile da raggiungere; considerare l'utilizzo bottiglia [[FlFlFln[Fl]
- Ricomputazione del layout[[] – Evitare di mettere la logica del layout dinamico all’interno del corpo della cella.
- Utilizza su tutta la griglia[] – Se cambi completamente la struttura della griglia (ad esempio, passando da 2 colonne a 3), assegnando una nuova alla griglia lo costrinse a ricaricare completamente, che può essere più veloce che provare ad animare un cambiamento complesso.
Risorse esterne per approfondire la tua conoscenza
Per approfondire ulteriormente le opinioni di raccolta dinamiche in SwiftUI, fare riferimento a queste risorse ufficiali e comunitarie:
- Documentazione LazyVGrid di Apple[ – Il riferimento ufficiale per tutti i parametri e comportamenti Documentazione degli sviluppatori Apple: LazyVGrid]
- Guida rapida per GridItem[[] – Copre tutte e tre le strategie di dimensionamento con esempi interattivi Apple Developer Documentazione: GridItem
- SwiftUI di esempio – Grid Layouts[ – Un tutorial pratico da Hacking con Swift che copre le griglie adattative e flessibili Accarezzando con Swift: Grid Layouts]
- SwiftUI Lab: Lazy Grids[ – Immersione profonda nelle prestazioni e personalizzazione. SwiftUI Lab: Lazy Grids
Migliori Pratiche Riepilogo
Creare un layout dinamico di visualizzazione della collezione in SwiftUI richiede una flessibilità di bilanciamento con le prestazioni.
- Inizia con per il conteggio della colonna dinamica più semplice; usa solo quando hai bisogno di un controllo preciso.
- Fornire sempre valori unici o conformi a .
- Usa con un valore che cambia (come il conteggio delle colonne) per transizioni lisce.
- Per le dimensioni delle celle miste, preferire su iOS 16+ o progettare un layout personalizzato di riga per riga.
- Profila la tua griglia con Strumenti per verificare che il carico pigro funzioni e le celle non rendano contenuti offscreen.
- Prova su più dimensioni e orientamenti del dispositivo – layout dinamici che sembrano grandi su uno schermo possono rompersi su un altro.
Grazie agli strumenti di chiarificazione SwiftUI, è possibile ottenere ciò che una volta richiesto decine di linee di codice UIKit in una manciata di componenti espressivi e riutilizzabili.