Untuk menyisipkan saran tanpa menerapkan nilai kolom, pisahkan pelengkapan dari pengiriman. Di macOS 15 atau lebih baru, SwiftUI menyediakan saran teks bawaan; gunakan submitScope() untuk memblokir handler pengiriman pada leluhur dan tombol eksplisit untuk tindakan akhir.
Tutorial ini membuat pemilih label kecil di dalam app Mac Anda sendiri. Mengetik Bl menawarkan label sintetis seperti Blue Tile. Memilih saran akan memperbarui draf yang dapat diedit; mengeklik Apply Label menyalin draf itu ke nilai yang diterapkan. Perbedaan ini penting: mencegah tindakan app dan menangani perintah tombol Return adalah dua kebutuhan yang berbeda.
Pilih API yang sesuai dengan interaksi
Modifier textInputSuggestions(_:) dari Apple menampilkan pilihan untuk kolom teks. Setiap saran yang dapat ditindaklanjuti memerlukan textInputCompletion(_:), yang menyediakan string penggantinya. Kedua API didokumentasikan untuk macOS 15 dan lebih baru. Saran tanpa modifier pelengkapan dapat muncul, tetapi tidak merespons klik. Apple: textInputSuggestions(_:), Apple: textInputCompletion(_:)
Untuk kolom label singkat, mengganti seluruh draf adalah pilihan yang tepat. Dokumentasi saran Apple menjelaskan penggantian seluruh kolom ini. Jangan berasumsi bahwa API ini menyediakan editor token yang mengikuti posisi kursor atau pratinjau akhiran sebaris; keduanya memerlukan rancangan pengeditan yang lebih spesifik. Apple: textInputSuggestions(_:)
| Kebutuhan Anda | Titik awal yang sesuai |
|---|---|
| Menawarkan label lengkap di macOS 15 atau lebih baru | Saran teks bawaan SwiftUI |
| Mencegah kolom memanggil handler pengiriman pada leluhur | submitScope() pada kolom |
| Menggunakan tata letak saran khusus atau mendukung target deployment yang lebih lama | Tombol saran milik app yang memperbarui binding draf |
| Membuat Return menerima item yang disorot dan menangani perintah tersebut | Kontrol AppKit milik sendiri dengan penanganan perintah |
| Mempratinjau akhiran atau mengganti kata pada posisi sisipan | Pelengkapan teks AppKit dan rentang pelengkapan eksplisit |
Dua baris terakhir merupakan pilihan arsitektur yang diturunkan dari API perintah dan pelengkapan yang terdokumentasi. Keduanya berguna saat interaksi memerlukan kontrol lebih besar daripada yang disediakan string pengganti dan batas pengiriman.
Buat pemilih label SwiftUI yang lengkap
Buat proyek App macOS di Xcode dengan SwiftUI menggunakan SDK yang menyertakan API macOS 15. Atur target deployment ke macOS 15 atau lebih baru. Ganti deklarasi app dan tampilan konten yang dihasilkan dengan satu file sumber berikut, dan sisakan hanya satu deklarasi app @main di target.
Contoh ini memisahkan draft dan appliedLabel. Aturan pencocokan lokalnya sengaja sederhana: pencocokan awalan peka huruf besar-kecil, tidak ada saran untuk teks kosong, dan tidak ada saran untuk kecocokan persis. Swift mendokumentasikan hasPrefix(_:) sebagai perbandingan awalan string; aturan pencocokan yang lebih luas merupakan pilihan aplikasi. Swift: Strings and Characters
import SwiftUI
@main
struct LabelCompletionApp: App {
var body: some Scene {
WindowGroup {
LabelCompletionView()
}
}
}
struct LabelCompletionView: View {
private let labels = [
"Blue Tile",
"Blue Tray",
"Green Tile",
"Round Tray"
]
@State private var draft = ""
@State private var appliedLabel = ""
@State private var applyCount = 0
@FocusState private var fieldIsFocused: Bool
private var matches: [String] {
guard !draft.isEmpty else { return [] }
return labels.filter {
$0.hasPrefix(draft) && $0 != draft
}
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Choose a label")
.font(.headline)
TextField("Type Bl to see suggestions", text: $draft)
.textFieldStyle(.roundedBorder)
.focused($fieldIsFocused)
.textInputSuggestions {
ForEach(matches, id: \.self) { label in
Text(label)
.textInputCompletion(label)
}
}
.submitScope()
Button("Apply Label") {
appliedLabel = draft
applyCount += 1
}
.disabled(draft.isEmpty)
Text("Draft: \(draft)")
Text("Applied: \(appliedLabel)")
Text("Apply count: \(applyCount)")
}
.padding(24)
.frame(width: 420)
.onAppear {
fieldIsFocused = true
}
}
}
Ini adalah contoh app lengkap, termasuk label sampel dan statusnya. Contoh ini telah diperiksa berdasarkan dokumentasi API yang diambil, tetapi tidak dikompilasi atau dijalankan di sini karena toolchain Swift dan runtime UI macOS tidak tersedia.
Mengapa memilih saran tidak menerapkan label
Setiap pelengkapan menyediakan string untuk kolom teks, yang binding-nya menunjuk ke draft. Satu-satunya kode yang menetapkan appliedLabel atau menambah applyCount adalah tindakan tombol Apply Label. Karena itu, dalam contoh ini, pembaruan draf tidak memiliki jalur menuju tindakan akhir tersebut.
Kolom itu juga memiliki submitScope(). Apple mendokumentasikan modifier ini sebagai pencegah pemicu pengiriman mencapai modifier pengiriman yang lebih tinggi dalam hierarki tampilan. Ini penting saat komponen tersebut nantinya ditempatkan di dalam formulir yang lebih besar dengan onSubmit pada leluhur. Apple: submitScope(_:)
Fokus awal ditetapkan menggunakan @FocusState. Apple mendokumentasikan bahwa menetapkan nilai terikatnya secara terprogram akan memindahkan fokus ke tampilan terkait. Fokus di sini memudahkan pengeditan; fokus tidak menentukan apakah label diterapkan. Apple: FocusState
Hal yang sengaja diizinkan oleh contoh
Daftar label menyediakan saran, bukan membatasi input yang valid. Mengetik Square Tile tetap mengaktifkan Apply Label. Jika antarmuka Anda hanya mengizinkan label yang terdaftar, ubah aturan validasinya secara eksplisit—misalnya, aktifkan tombol hanya jika draf sama dengan salah satu entri katalog. Jangan diam-diam mengganti draf yang tidak cocok dengan saran pertama.
Contoh ini juga mengganti seluruh label. Jika draf berisi beberapa kata, memilih pelengkapan harus dipahami sebagai memilih nilai akhir kolom. Editor token yang harus mempertahankan teks di sekitarnya memerlukan penggantian yang memperhatikan pilihan teks.
Pahami arti “tanpa mengirimkan”
Dokumentasi onSubmit(of:_:) SwiftUI menyebutkan bahwa kolom teks memicu tindakan pengirimannya saat pengguna menekan Return. Tindakan pengiriman dapat dipasang pada satu tampilan atau hierarki yang lebih besar. Periksa kedua lokasi tersebut saat pemilihan saran pelengkapan tampak memicu tindakan app yang tidak diharapkan. Apple: onSubmit(of:_:)
submitScope() menetapkan batas bagi handler leluhur. Dokumentasi tidak menjanjikan bahwa tidak ada peristiwa pengiriman internal, pengeditan tetap aktif, atau setiap perintah keyboard ditangani. Demikian pula, modifier pelengkapan Apple mendokumentasikan penggantian teks, bukan penekanan universal terhadap perilaku tombol Return. Pertimbangkan batasan ini saat memilih interaksi. Apple: submitScope(_:), Apple: textInputCompletion(_:)
Untuk komponen yang sudah ada, periksa jalur berikut secara terpisah:
- Hapus tindakan aplikasi akhir dari
onSubmitmilik kolom jika Return tidak boleh menerapkan nilainya. - Pasang
submitScope()pada kolom untuk memblokir tindakan pengiriman leluhur. - Periksa pintasan keyboard tombol secara terpisah, terutama tindakan default eksplisit.
- Periksa observer perubahan atau setter model yang menjalankan tindakan akhir setiap kali draf berubah.
Closure onSubmit lokal yang kosong bukan pengganti batas pengiriman yang didokumentasikan. Apple secara khusus menyediakan submitScope() untuk menghentikan propagasi. Jika app harus mengirimkan saat Return ditekan hanya ketika tidak ada pelengkapan aktif, kebijakan keyboard bersyarat itu memerlukan status dan penanganan perintah tambahan. Apple: onSubmit(of:_:)
Gunakan AppKit saat Return harus menerima dan menghentikan
Untuk NSTextField yang Anda miliki, AppKit menyediakan control(_:textView:doCommandBy:). Argumen text-view-nya adalah editor kolom untuk kontrol tersebut. Delegat dapat menangani perintah yang terikat dan mengembalikan true; mengembalikan false berarti perintah itu tidak ditangani oleh delegat tersebut. Ini adalah titik ekstensi terdokumentasi untuk menyesuaikan perintah di kolom teks. Apple: control(_:textView:doCommandBy:)
Pengontrol pelengkapan khusus dapat menggunakan titik ekstensi ini dengan urutan berikut:
- Lacak apakah daftar saran terbuka dan item mana yang disorot.
- Saat perintah Return tiba dengan item yang disorot valid, sisipkan item tersebut ke dalam draf.
- Tutup daftar saran dan sinkronkan model draf app.
- Kembalikan
truekarena pengontrol menangani perintah tersebut. - Kembalikan
falseuntuk perintah yang tidak ditangani pengontrol.
Urutan ini adalah rancangan implementasi yang diturunkan dari kontrak delegat, bukan kebijakan pelengkapan otomatis bawaan. Tentukan secara terpisah apa yang harus dilakukan Return saat daftar tertutup. Jika Anda memiliki NSTextView mandiri, titik kait yang sesuai adalah textView(_:doCommandBy:); Apple menyatakan bahwa mengembalikan true mencegah text view menjalankan perintah itu sendiri. Apple: textView(_:doCommandBy:)
Untuk pelengkapan teks itu sendiri, NSTextView.insertCompletion(_:forPartialWordRange:movement:isFinal:) mendukung penggantian dan perubahan pilihan. Dengan isFinal: false, metode ini memilih akhiran yang diusulkan; dengan isFinal: true, metode ini menempatkan pilihan setelah kata yang dilengkapi. Flag ini menjelaskan status pelengkapan, bukan pengiriman aplikasi. Argumen movement mengidentifikasi cara navigasi atau pemilihan pelengkapan dilakukan. Apple: insertCompletion(_:forPartialWordRange:movement:isFinal:)
Pertahankan rentang teks yang tepat
rangeForUserCompletion milik AppKit mengidentifikasi kata parsial dari awal hingga posisi sisipan. Gunakan rentang yang sesuai dengan penggantian yang diinginkan dan hitung ulang setelah pengeditan atau perpindahan kursor. Jangan gunakan kembali rentang yang sudah ditangkap sebelumnya secara sembarangan setelah teks berubah. Apple: rangeForUserCompletion
Saat menjembatani rentang teks, jangan mengganti panjang NSString dengan jumlah karakter Swift. Dokumentasi Swift menjelaskan bahwa String menghitung klaster grafem diperluas, sedangkan panjang NSString menghitung unit kode UTF-16. Nilai-nilai tersebut dapat berbeda. Penggantian binding seluruh kolom pada contoh SwiftUI sama sekali menghindari konversi rentang ini. Swift: Strings and Characters)
Verifikasi penyisipan dan penerapan secara terpisah
Gunakan draf yang terlihat, nilai yang diterapkan, dan penghitung untuk memeriksa hasil pada setiap versi macOS yang didukung. Hal-hal berikut adalah pemeriksaan yang diharapkan untuk contoh ini, bukan hasil pengujian yang dilaporkan.
| Tindakan | Hasil yang dapat diamati dan diharapkan |
|---|---|
Ketik Bl |
Draf berisi Bl; label yang cocok ditawarkan |
Klik Blue Tile |
Draf menjadi Blue Tile; nilai yang diterapkan dan jumlah tetap tidak berubah |
| Tekan Return saat mengedit | App tidak menerapkan draf |
Klik Apply Label |
Nilai yang diterapkan menyalin draf; jumlah bertambah satu kali |
| Edit draf setelahnya | Nilai yang sebelumnya diterapkan tetap terlihat |
| Kosongkan draf | Saran kosong dan Apply Label dinonaktifkan |
Ketik bl |
Tidak ada kecocokan berdasarkan aturan peka huruf besar-kecil dalam contoh |
Uji juga pemilihan dengan keyboard di daftar saran bawaan dan ulangi pemeriksaan setelah kolom ditempatkan di dalam tampilan induk yang sebenarnya. Apple mencatat bahwa peristiwa seperti memindahkan jendela macOS dapat menutup antarmuka saran bawaan, jadi draf harus tetap dapat digunakan tanpa daftar tersebut. Apple: textInputSuggestions(_:)
Jika target deployment Anda lebih lama dari macOS 15, sediakan tampilan bawaan dengan pemeriksaan ketersediaan dan fallback, atau gunakan tombol saran milik app di seluruh versi. Tombol fallback dapat menetapkan labelnya langsung ke draf dan membiarkan tindakan akhir pada Apply Label. Cara ini memberikan pemisahan draf dan penerapan yang sama, sementara navigasi keyboard dan presentasi tetap menjadi tanggung jawab komponen Anda.
Untuk tugas pemilih label ini, batas yang berguna dapat diamati: menerima pelengkapan mengubah draf yang dapat diedit, dan hanya tindakan akhir eksplisit yang mengubah label yang diterapkan. Gunakan saran bawaan SwiftUI dan cakupan pengiriman untuk batas tersebut; beralihlah ke penanganan perintah AppKit saat menangani tombol Return itu sendiri menjadi bagian dari kebutuhan.
Sumber
- textInputSuggestions(_:) — Apple
- textInputCompletion(_:) — Apple
- submitScope(_:) — Apple
- onSubmit(of:_:) — Apple
- FocusState — Apple
- control(_:textView:doCommandBy:) — Apple
- textView(_:doCommandBy:) — Apple
- insertCompletion(_:forPartialWordRange:movement:isFinal:) — Apple
- rangeForUserCompletion — Apple
- Strings and Characters — Swift.org