Per una raccolta personalizzata a colonna singola nella tua app per Mac, usa List di SwiftUI con un binding di selezione basato su un insieme di ID stabili degli elementi. Personalizza le righe lasciando che il controllo nativo gestisca la selezione degli intervalli e la selezione tramite clic con Comando.
Questo tutorial crea una raccolta di etichette sintetiche, da Tile 01 a Tile 12. Il compito del lettore è selezionare elementi adiacenti, aggiungere o rimuovere singoli elementi e mantenere comprensibile la selezione quando si applica un filtro o si inverte l’ordine di visualizzazione. Una sezione successiva spiega l’approccio con AppKit per una raccolta che richiede una griglia o un altro layout personalizzato.
Cosa dovrebbe fare la selezione degli intervalli e tramite clic con Comando?
La Guida utente del Mac di Apple descrive l’interazione abituale: fai clic sul primo elemento e Maiusc-clic sull’ultimo per selezionare gli elementi adiacenti, compresi quelli intermedi. Clic con Comando seleziona singoli elementi che non devono essere adiacenti; facendo clic con Comando su un elemento selezionato, questo viene rimosso da una selezione multipla. Queste sono le interazioni di base da preservare in questa raccolta. Selezionare elementi sullo schermo del Mac.
Per l’implementazione iniziale, usa un unico elenco ordinato. In questo modo, «intermedi» ha un significato inequivocabile: le righe comprese tra due estremi nell’ordine di visualizzazione corrente. Una raccolta spaziale introduce un’altra decisione: se un intervallo segue la sequenza dei dati o una regione del layout. Questa scelta merita verifiche separate.
Scegli il controllo in base a ciò che l’utente seleziona:
| Compito del lettore | Punto di partenza adatto | Rappresentazione della selezione |
|---|---|---|
| Selezionare righe personalizzate complete | List di SwiftUI |
Insieme di ID degli elementi |
| Selezionare riquadri completi in un layout personalizzato | NSCollectionView di AppKit |
Percorsi di indice della selezione, associati agli ID del modello |
| Selezionare caratteri all’interno di un’etichetta | Selezione del testo di SwiftUI | Selezione del testo fornita dalla vista di testo |
| Selezionare intervalli all’interno di un documento di testo | Sistema di testo di AppKit | Intervalli di caratteri o di testo |
Apple documenta List come contenitore a colonna singola con selezione opzionale e NSCollectionView come raccolta ordinata con un layout personalizzabile. Sono i punti di partenza supportati per la selezione degli elementi. List, NSCollectionView.
Crea l’esempio SwiftUI completo
Crea in Xcode un progetto di app per macOS usando SwiftUI. Per questo esempio, imposta la versione minima di macOS su 14 o successiva: la chiusura a due parametri onChange usata qui sotto richiede macOS 14. Sostituisci la dichiarazione dell’app generata con questo codice, lasciando esattamente un punto di ingresso @main nel target. onChange(of:initial:_:), App.
Di seguito trovi una piccola app completa, inclusi i dati sintetici e la finestra. Non usa risorse esterne. È stata esaminata rispetto alle API documentate, ma qui non è stata compilata né eseguita su macOS.
import Foundation
import SwiftUI
struct Tile: Identifiable {
let id: Int
let title: String
}
@main
struct CollectionSelectionDemoApp: App {
var body: some Scene {
WindowGroup {
TileCollectionView()
}
}
}
struct TileCollectionView: View {
private let tiles: [Tile] = (1...12).map {
Tile(id: $0, title: String(format: "Tile %02d", $0))
}
@State private var selectedIDs: Set<Int> = []
@State private var query = ""
@State private var reversed = false
private var visibleTiles: [Tile] {
let filtered = tiles.filter {
query.isEmpty ||
$0.title.localizedCaseInsensitiveContains(query)
}
return reversed ? Array(filtered.reversed()) : filtered
}
private var selectionDescription: String {
let names = visibleTiles
.filter { selectedIDs.contains($0.id) }
.map(\.title)
return names.isEmpty ? "None selected" : names.joined(separator: ", ")
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
TextField("Filter tiles", text: $query)
.textFieldStyle(.roundedBorder)
HStack {
Button("Reverse order") {
reversed.toggle()
}
Button("Clear selection") {
selectedIDs.removeAll()
}
.disabled(selectedIDs.isEmpty)
}
List(visibleTiles, selection: $selectedIDs) { tile in
HStack(spacing: 12) {
RoundedRectangle(cornerRadius: 4)
.fill(Color.gray.opacity(0.2))
.frame(width: 32, height: 32)
VStack(alignment: .leading) {
Text(tile.title)
Text("Sample item \(tile.id)")
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
}
.padding(.vertical, 4)
.tag(tile.id)
}
Text("\(selectedIDs.count) selected")
Text(selectionDescription)
.font(.caption)
}
.padding()
.frame(minWidth: 440, minHeight: 480)
.onChange(of: query) { _, _ in
let visibleIDs = Set(visibleTiles.map(\.id))
selectedIDs.formIntersection(visibleIDs)
}
}
}
Il tipo degli elementi del binding di selezione corrisponde a Tile.ID: entrambi usano Int. Apple documenta che associare la selezione di un elenco a un insieme del tipo ID dei dati consente la selezione multipla. Su Mac, la selezione multipla non richiede l’attivazione della modalità di modifica. Supportare la selezione negli elenchi.
La riga resta personalizzabile: contiene una forma neutra, un titolo e un’etichetta secondaria. Non c’è un gestore di clic a livello di riga né un’operazione di attivazione separata. Lasciare che sia l’elenco a gestire la selezione evita che due parti indipendenti del codice la aggiornino per una singola interazione.
Mantieni separati l’identità degli elementi e l’ordine di visualizzazione
L’esempio usa deliberatamente tre concetti distinti: un array ordinato di elementi, un insieme di ID selezionati e una presentazione filtrata di quell’array. Swift definisce gli array come raccolte ordinate e gli insiemi come raccolte di valori univoci senza un ordine definito. Per questo un insieme è adatto a rappresentare l’appartenenza, mentre un array fornisce l’ordine usato per la presentazione. Tipi di raccolta.
Gli ID interi sono fissi per questo insieme di dati sintetici. Invertire l’array cambia la posizione di un elemento senza modificarne l’identità. In un modello reale, assegna a ogni elemento un identificatore stabile quando lo crei o lo carichi e conserva quell’identificatore negli aggiornamenti successivi della vista. Evita di generare una nuova identità ogni volta che viene valutata una riga.
Il riepilogo della selezione segue visibleTiles e verifica l’appartenenza a selectedIDs. È una scelta implementativa derivata dalle proprietà di queste raccolte: produce un riepilogo nell’ordine di visualizzazione, senza dipendere dall’ordine di iterazione di un insieme.
Il filtraggio richiede una decisione esplicita sul prodotto. Questo esempio adotta una politica limitata agli elementi visibili: ogni volta che cambia il filtro, la selezione viene intersecata con gli ID ancora visibili. Gli elementi nascosti perdono la selezione e, quando si rimuove il filtro, la selezione precedente non viene ripristinata. Le etichette e l’array sorgente restano invariati.
Per esempio, inizia con Tile 02, Tile 03 e Tile 04 selezionati. Inserisci 04 in Filter tiles. In base alla politica di questo esempio, resta selezionato solo Tile 04. Rimuovi il filtro e gli altri due elementi riappaiono senza essere selezionati. È una dimostrazione illustrativa del codice, non un risultato di esecuzione riportato.
Un’altra app potrebbe dover conservare la selezione tra i filtri. In tal caso, sposta la gestione della selezione nel modello, definisci come mostrare nel conteggio e nel riepilogo le selezioni nascoste e verifica la sincronizzazione con il controllo visualizzato. Eliminare l’intersezione, da solo, non definisce una politica completa per le selezioni nascoste.
Quando dovresti usare una vista raccolta di AppKit?
Usa NSCollectionView quando la raccolta richiede riquadri su più colonne o un layout che vada oltre un singolo elenco. Apple distingue la sorgente dati della raccolta, i controller delle viste degli elementi, il delegato e l’oggetto layout. In questo modo puoi personalizzare l’aspetto e il posizionamento mantenendo un controllo di raccolta responsabile delle interazioni. NSCollectionView.
Configura l’implementazione AppKit in questo ordine:
- Crea la vista raccolta e assegnale un layout moderno, come
NSCollectionViewFlowLayout. - Fornisci la sorgente dati e registra la classe o il nib degli elementi usati per creare le celle.
- Imposta
isSelectablesutrueeallowsMultipleSelectionsutrue. - Imposta
allowsEmptySelectionin base alla possibilità che il compito consenta di non selezionare nulla. - Leggi
selectionIndexPaths, quindi associa ogni percorso di indice valido all’ID del modello corrispondente. - Visualizza la selezione nella vista dell’elemento usando il suo stato
isSelected.
La selezione multipla è disabilitata per impostazione predefinita. Abilitarla è quindi un passaggio di configurazione esplicito, non un comportamento implicito di riquadri che sembrano selezionabili. La proprietà isSelected dell’elemento indica se quell’elemento è selezionato. allowsMultipleSelection, isSelected.
Considera i percorsi di indice come posizioni nella sorgente dati corrente. Apple documenta che selectionIndexPaths si aggiorna durante la selezione interattiva e può anche essere assegnata a livello di codice. Assegnare un percorso di indice che non identifica un elemento valido della sorgente dati genera un’eccezione. Dopo aver riordinato o filtrato, ricostruisci le posizioni della selezione a partire dagli ID stabili invece di riutilizzare posizioni non più valide. selectionIndexPaths.
Rispetta il limite di quanto documentato: allowsMultipleSelection consente di selezionare più di un elemento; la relativa documentazione non specifica un algoritmo completo per l’ancoraggio degli intervalli valido per ogni layout personalizzato o combinazione di modificatori. Verifica il comportamento di Maiusc-clic nel layout effettivo e nelle versioni di macOS supportate. Se il compito richiede un intervallo lineare preciso, l’implementazione a colonna singola qui sopra è il punto di partenza più chiaro.
Per un’app SwiftUI che richiede il controllo AppKit, incorporalo tramite NSViewRepresentable. Crea la gerarchia AppKit in makeNSView, sincronizza le modifiche del modello in updateNSView e usa un coordinatore per inoltrare gli eventi del delegato allo stato SwiftUI. SwiftUI controlla la cornice e i limiti della vista rappresentata. NSViewRepresentable.
Anche gli aggiornamenti programmatici richiedono un percorso di sincronizzazione esplicito. Apple afferma che sia collectionView(_:shouldSelectItemsAt:) sia collectionView(_:didSelectItemsAt:) vengono ignorati quando la selezione è impostata a livello di codice. Gestisci gli aggiornamenti del modello in un’operazione condivisa invece di dipendere dall’esecuzione di questi callback dopo ogni assegnazione. collectionView(_:shouldSelectItemsAt:), collectionView(_:didSelectItemsAt:).
La selezione del testo offre la stessa funzionalità?
textSelection(.enabled) di SwiftUI controlla la selezione del testo all’interno delle viste. Applicandola a un contenitore, ogni vista di testo contenuta diventa selezionabile. Non documenta un binding di selezione degli elementi di una raccolta né un modello condiviso di selezione degli intervalli tra schede arbitrarie. Usala quando il compito consiste nel selezionare caratteri da un’etichetta. textSelection(_:).
Per un’interfaccia di documenti di testo, AppKit espone le selezioni di caratteri tramite NSTextView.selectedRanges. I relativi intervalli sono ordinati, non sovrapposti e non contigui. Anche NSTextSelection.textRanges di TextKit rappresenta intervalli di testo ordinati logicamente ed è disponibile da macOS 12. Queste API descrivono posizioni nel testo; convertirle in ID di riquadri richiederebbe una progettazione specifica dell’app. selectedRanges, textRanges.
Decidi quale gesto deve attivare l’etichetta di una riga prima di abilitare la selezione del testo al suo interno. Per il compito di questo tutorial, facendo clic su un’etichetta si deve selezionare l’intera riga, quindi l’esempio non configura la selezione del testo.
Verifica l’interazione con una piccola matrice di selezione
Esegui questi controlli nella finestra dell’app. I primi quattro verificano le convenzioni di base della selezione su Mac; i restanti mettono alla prova le decisioni dell’esempio sull’identità e sul filtraggio. L’interazione documentata da Apple è il riferimento, mentre le etichette sintetiche rendono facile controllare quali elementi sono selezionati. Selezionare elementi sullo schermo del Mac.
| Azione | Risultato previsto |
|---|---|
Fai clic su Tile 02 |
Un elemento selezionato |
Fai Maiusc-clic su Tile 05 |
Sono selezionati da Tile 02 a Tile 05 |
Fai clic con Comando su Tile 08 |
Tile 08 si aggiunge alla selezione |
Fai clic con Comando su Tile 03 già selezionato |
Tile 03 viene rimosso dalla selezione |
Fai clic su Clear selection |
Selezione vuota e riepilogo None selected |
Seleziona Tile 04, poi inverti l’ordine |
L’ID selezionato continua a identificare Tile 04 |
Seleziona diverse righe, poi filtra usando 04 |
Resta selezionato solo un elemento visibile già selezionato |
| Rimuovi il filtro | Le righe nascoste ricompaiono senza ripristinare le selezioni eliminate |
Controlla anche la navigazione da tastiera dopo aver fatto clic sull’elenco, lo scorrimento delle righe selezionate fuori dalla vista e poi di nuovo all’interno, e l’avvio di un nuovo intervallo dopo aver applicato un filtro. Per una griglia AppKit, aggiungi verifiche che attraversino il confine tra due righe. Se il risultato non corrisponde al compito previsto, prima riduci l’interfaccia a un semplice controllo nativo, poi reintroduci i contenuti personalizzati delle righe e i gestori dei gesti uno alla volta.
Affida al controllo nativo la gestione delle interazioni, agli ID stabili l’appartenenza alla selezione e all’array visualizzato l’ordine. Con queste responsabilità definite, lo stile personalizzato delle righe, il filtraggio e l’ordinamento possono supportare lo stesso compito di selezione comprensibile.
Fonti
- Selezionare elementi sullo schermo del Mac — Supporto Apple.
- List — Documentazione per sviluppatori Apple.
- App — Documentazione per sviluppatori Apple.
- onChange(of:initial:_:) — Documentazione per sviluppatori Apple.
- Tipi di raccolta — The Swift Programming Language, progetto Swift.
- NSCollectionView — Documentazione per sviluppatori Apple.
- allowsMultipleSelection — Documentazione per sviluppatori Apple.
- selectionIndexPaths — Documentazione per sviluppatori Apple.
- isSelected — Documentazione per sviluppatori Apple.
- NSViewRepresentable — Documentazione per sviluppatori Apple.
- collectionView(_:shouldSelectItemsAt:) — Documentazione per sviluppatori Apple.
- collectionView(_:didSelectItemsAt:) — Documentazione per sviluppatori Apple.
- textSelection(_:) — Documentazione per sviluppatori Apple.
- selectedRanges — Documentazione per sviluppatori Apple.
- textRanges — Documentazione per sviluppatori Apple.