Untuk koleksi kustom satu kolom di app Mac, gunakan List SwiftUI dengan binding pilihan yang didukung oleh sekumpulan ID item yang stabil. Sesuaikan barisnya, sambil membiarkan kontrol asli menangani pemilihan rentang dan pemilihan dengan Command-klik.
Tutorial ini membuat koleksi label sintetis, dari Tile 01 hingga Tile 12. Tugas pembaca adalah memilih item yang berurutan, menambahkan atau menghapus item satu per satu, serta menjaga agar pilihan tetap mudah dipahami saat memfilter atau membalik urutan tampilan. Bagian berikutnya menjelaskan jalur AppKit untuk koleksi yang memerlukan kisi atau tata letak kustom lainnya.
Bagaimana seharusnya pemilihan rentang dan Command-klik bekerja?
Panduan Pengguna Mac dari Apple menjelaskan interaksi yang lazim: klik item pertama lalu Shift-klik item terakhir untuk memilih item yang berurutan, termasuk item di antaranya. Command-klik memilih item satu per satu yang tidak harus berurutan; Command-klik pada item yang sudah dipilih akan menghapusnya dari pilihan berganda. Inilah interaksi dasar yang perlu dipertahankan dalam koleksi ini. Memilih item di layar Mac Anda.
Untuk implementasi awal, gunakan satu daftar berurutan. Dengan begitu, makna “di antara” tidak ambigu: baris di antara dua titik akhir dalam urutan tampilan saat ini. Koleksi spasial menimbulkan keputusan lain—apakah rentang mengikuti urutan data atau wilayah tata letak—yang sebaiknya diuji secara terpisah.
Pilih kontrol berdasarkan apa yang dipilih pengguna:
| Tugas pembaca | Titik awal yang sesuai | Representasi pilihan |
|---|---|---|
| Memilih seluruh baris kustom | SwiftUI List |
Sekumpulan ID item |
| Memilih seluruh ubin dalam tata letak kustom | AppKit NSCollectionView |
Jalur indeks pilihan, dipetakan ke ID model |
| Memilih karakter dalam label | Pemilihan teks SwiftUI | Pemilihan teks yang disediakan oleh tampilan teks |
| Memilih rentang dalam dokumen teks | Sistem teks AppKit | Rentang karakter atau teks |
Apple mendokumentasikan List sebagai wadah satu kolom dengan pilihan opsional, dan NSCollectionView sebagai koleksi berurutan dengan tata letak yang dapat disesuaikan. Keduanya merupakan titik awal yang didukung untuk pemilihan item. List, NSCollectionView.
Buat contoh SwiftUI yang lengkap
Buat proyek app macOS di Xcode menggunakan SwiftUI. Atur target deployment ke macOS 14 atau lebih baru untuk contoh ini: closure onChange dengan dua parameter yang digunakan di bawah memerlukan macOS 14. Ganti deklarasi app yang dibuat secara otomatis dengan kode ini, dan pastikan hanya ada satu titik masuk @main di target. onChange(of:initial:_:), App.
Berikut app kecil yang lengkap, termasuk data sintetis dan jendelanya. App ini tidak menggunakan aset eksternal. Kodenya telah ditinjau berdasarkan API yang didokumentasikan, tetapi belum dikompilasi atau dijalankan di macOS di sini.
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)
}
}
}
Tipe elemen binding pilihan cocok dengan Tile.ID: keduanya menggunakan Int. Apple mendokumentasikan bahwa mengikat pilihan daftar ke sekumpulan tipe ID dari datanya memungkinkan pemilihan berganda. Di Mac, pemilihan berganda tidak memerlukan mode edit. Mendukung pilihan dalam daftar.
Barisnya tetap dapat disesuaikan: baris ini memuat bentuk netral, judul, dan label sekunder. Tidak ada penangan klik tingkat baris atau operasi toggle terpisah. Dengan membiarkan daftar mengelola pilihan, dua bagian kode yang berbeda tidak akan memperbarui pilihan untuk satu interaksi yang sama.
Pisahkan identitas item dari urutan tampilan
Contoh ini sengaja menggunakan tiga konsep terpisah: larik item berurutan, sekumpulan ID yang dipilih, dan penyajian larik yang telah difilter. Swift mendefinisikan larik sebagai koleksi berurutan dan set sebagai koleksi nilai unik yang tidak memiliki urutan tertentu. Karena itu, set cocok untuk keanggotaan, sedangkan larik menyediakan urutan yang digunakan untuk penyajian. Tipe Koleksi.
ID bilangan bulat bersifat tetap untuk set data sintetis ini. Membalik larik mengubah posisi kemunculan item tanpa mengubah identitasnya. Dalam model nyata, tetapkan pengenal stabil untuk setiap item saat membuat atau memuatnya, lalu pertahankan pengenal tersebut pada pembaruan tampilan berikutnya. Hindari membuat identitas baru setiap kali baris dievaluasi.
Ringkasan pilihan mengikuti visibleTiles dan memeriksa keanggotaan dalam selectedIDs. Ini adalah pilihan implementasi berdasarkan karakteristik koleksi tersebut: ringkasan dihasilkan sesuai urutan tampilan, bukan mengandalkan urutan iterasi sebuah set.
Pemfilteran memerlukan keputusan produk yang eksplisit. Contoh ini menerapkan kebijakan hanya-item-yang-terlihat: setiap kali filter berubah, pilihan dibatasi pada ID yang masih terlihat. Item tersembunyi kehilangan status pilihannya, dan menghapus filter tidak memulihkan pilihan sebelumnya. Label dan larik sumber tidak berubah.
Sebagai contoh, pilih Tile 02, Tile 03, dan Tile 04. Masukkan 04 di Filter tiles. Berdasarkan kebijakan contoh ini, hanya Tile 04 yang tetap dipilih. Hapus filter, dan dua item lainnya muncul kembali tanpa dipilih. Ini adalah uraian ilustratif tentang kode, bukan hasil runtime yang dilaporkan.
App lain mungkin perlu mempertahankan pilihan saat difilter. Dalam hal itu, pindahkan kepemilikan pilihan ke model, tentukan bagaimana pilihan tersembunyi ditampilkan dalam hitungan dan ringkasan, lalu uji rekonsiliasinya dengan kontrol yang ditampilkan. Menghapus operasi irisan saja tidak menetapkan kebijakan pilihan tersembunyi yang lengkap.
Kapan sebaiknya menggunakan tampilan koleksi AppKit?
Gunakan NSCollectionView ketika koleksi memerlukan ubin dalam beberapa kolom atau tata letak selain satu daftar. Apple memisahkan sumber data koleksi, pengontrol tampilan item, delegat, dan objek tata letak. Dengan demikian, tampilan dan posisi dapat disesuaikan sambil tetap menjadikan kontrol koleksi sebagai pengelola interaksi. NSCollectionView.
Konfigurasikan implementasi AppKit dengan urutan berikut:
- Buat tampilan koleksi dan tetapkan tata letak modern, seperti
NSCollectionViewFlowLayout. - Sediakan sumber datanya dan daftarkan kelas item atau nib yang digunakan untuk membuat sel.
- Atur
isSelectableketruedanallowsMultipleSelectionketrue. - Atur
allowsEmptySelectionsesuai apakah tugas tersebut mengizinkan tidak ada pilihan. - Baca
selectionIndexPaths, lalu petakan setiap jalur indeks yang valid ke ID model yang sesuai. - Tampilkan pilihan pada tampilan item menggunakan status
isSelected.
Pemilihan berganda dinonaktifkan secara default. Karena itu, mengaktifkannya merupakan langkah penyiapan yang eksplisit, bukan sesuatu yang otomatis tersedia hanya karena ubin tampak dapat dipilih. Properti item isSelected melaporkan apakah item tersebut dipilih. allowsMultipleSelection, isSelected.
Perlakukan jalur indeks sebagai lokasi dalam sumber data saat ini. Apple mendokumentasikan bahwa selectionIndexPaths diperbarui selama pemilihan interaktif dan juga dapat ditetapkan secara terprogram. Menetapkan jalur indeks yang tidak menunjuk item sumber data yang valid akan memicu pengecualian. Setelah menyusun ulang atau memfilter, bangun ulang lokasi pilihan dari ID stabil alih-alih menggunakan kembali posisi yang sudah usang. selectionIndexPaths.
Pertahankan batasan yang terdokumentasi: allowsMultipleSelection memungkinkan pemilihan lebih dari satu item; dokumentasinya tidak menjelaskan algoritme jangkar rentang yang lengkap untuk setiap tata letak kustom atau kombinasi modifier. Verifikasi perilaku Shift-klik pada tata letak dan versi macOS yang sebenarnya didukung. Jika tugasnya memerlukan rentang linear yang presisi, implementasi satu kolom di atas merupakan titik awal yang lebih jelas.
Untuk app SwiftUI yang memerlukan kontrol AppKit, sematkan melalui NSViewRepresentable. Buat hierarki AppKit di makeNSView, rekonsiliasikan perubahan model di updateNSView, dan gunakan koordinator untuk meneruskan peristiwa delegat ke status SwiftUI. SwiftUI mengendalikan bingkai dan batas tampilan yang direpresentasikan. NSViewRepresentable.
Pembaruan terprogram juga memerlukan jalur sinkronisasi yang eksplisit. Apple menyatakan bahwa collectionView(_:shouldSelectItemsAt:) dan collectionView(_:didSelectItemsAt:) sama-sama dilewati ketika pilihan ditetapkan secara terprogram. Kelola pembaruan model dalam operasi bersama, alih-alih mengandalkan callback tersebut untuk berjalan setelah setiap penetapan. collectionView(_:shouldSelectItemsAt:), collectionView(_:didSelectItemsAt:).
Apakah pemilihan teks menyediakan fitur yang sama?
textSelection(.enabled) di SwiftUI mengendalikan pemilihan teks di dalam tampilan. Menerapkannya pada sebuah wadah membuat setiap tampilan teks di dalamnya dapat dipilih. API ini tidak mendokumentasikan binding pilihan item koleksi atau model pemilihan rentang bersama di berbagai kartu sembarang. Gunakan ini ketika tugasnya adalah memilih karakter dari label. textSelection(_:).
Untuk antarmuka dokumen teks, AppKit menyediakan pemilihan karakter melalui NSTextView.selectedRanges. Rentangnya berurutan, tidak saling tumpang tindih, dan tidak berurutan. NSTextSelection.textRanges dari TextKit juga merepresentasikan rentang teks yang diurutkan secara logis dan tersedia mulai macOS 12. API ini menjelaskan posisi teks, sehingga pemetaannya ke ID ubin merupakan rancangan khusus aplikasi. selectedRanges, textRanges.
Tentukan fungsi gestur untuk label baris sebelum mengaktifkan pemilihan teks di dalamnya. Untuk tugas tutorial ini, mengeklik label seharusnya menjadi bagian dari pemilihan seluruh baris, jadi contoh ini tidak mengatur pemilihan teks.
Verifikasi interaksi dengan matriks pilihan sederhana
Jalankan pemeriksaan ini di jendela app itu sendiri. Empat pemeriksaan pertama menguji konvensi dasar pemilihan di Mac; pemeriksaan berikutnya menguji keputusan contoh ini terkait identitas dan pemfilteran. Interaksi yang didokumentasikan Apple menjadi acuan, sedangkan label sintetis memudahkan pemeriksaan keanggotaan yang diharapkan. Memilih item di layar Mac Anda.
| Tindakan | Hasil yang diharapkan |
|---|---|
Klik Tile 02 |
Satu item dipilih |
Shift-klik Tile 05 |
Tile 02 hingga Tile 05 dipilih |
Command-klik Tile 08 |
Tile 08 ditambahkan ke pilihan |
Command-klik Tile 03 yang sudah dipilih |
Tile 03 dihapus dari pilihan |
Klik Clear selection |
Pilihan kosong dan ringkasan None selected |
Pilih Tile 04, lalu balik urutan |
ID yang dipilih tetap mengidentifikasi Tile 04 |
Pilih beberapa baris, lalu filter ke 04 |
Hanya item yang sebelumnya dipilih dan masih terlihat yang tetap dipilih |
| Hapus filter | Baris tersembunyi muncul kembali tanpa memulihkan pilihan yang telah dibuang |
Periksa juga navigasi papan ketik setelah mengeklik daftar, menggulir baris terpilih hingga keluar dari tampilan lalu kembali, serta memulai rentang baru setelah memfilter. Untuk kisi AppKit, tambahkan pemeriksaan yang melintasi batas baris. Jika hasilnya berbeda dari tugas yang dimaksud, sederhanakan antarmuka terlebih dahulu menjadi kontrol asli biasa, lalu tambahkan kembali konten baris kustom dan penangan gestur satu per satu.
Biarkan kontrol asli bertanggung jawab atas interaksi, ID stabil mengatur keanggotaan, dan larik yang ditampilkan mengatur urutan. Dengan tanggung jawab yang jelas, gaya baris kustom, pemfilteran, dan pengurutan dapat mendukung tugas pemilihan yang sama dan mudah dipahami.
Sumber
- Memilih item di layar Mac Anda — Dukungan Apple.
- List — Dokumentasi Developer Apple.
- App — Dokumentasi Developer Apple.
- onChange(of:initial:_:) — Dokumentasi Developer Apple.
- Tipe Koleksi — Bahasa Pemrograman Swift, proyek Swift.
- NSCollectionView — Dokumentasi Developer Apple.
- allowsMultipleSelection — Dokumentasi Developer Apple.
- selectionIndexPaths — Dokumentasi Developer Apple.
- isSelected — Dokumentasi Developer Apple.
- NSViewRepresentable — Dokumentasi Developer Apple.
- collectionView(_:shouldSelectItemsAt:) — Dokumentasi Developer Apple.
- collectionView(_:didSelectItemsAt:) — Dokumentasi Developer Apple.
- textSelection(_:) — Dokumentasi Developer Apple.
- selectedRanges — Dokumentasi Developer Apple.
- textRanges — Dokumentasi Developer Apple.