Untuk memasukkan cadangan tanpa menggunakan nilai medan, asingkan pelengkapan daripada penghantaran. Pada macOS 15 atau lebih baharu, SwiftUI menyediakan cadangan teks natif; gunakan submitScope() untuk menyekat pengendali penghantaran leluhur dan butang khusus untuk tindakan akhir.

Tutorial ini membina pemilih label ringkas dalam app Mac anda sendiri. Menaip Bl menawarkan label sintetik seperti Blue Tile. Memilih cadangan mengemas kini draf yang boleh disunting; mengklik Apply Label menyalin draf itu ke nilai yang digunakan. Perbezaan ini penting: menghalang tindakan app dan menggunakan arahan kekunci Return ialah keperluan yang berbeza.

Pilih API yang sepadan dengan interaksi

Pengubah suai Apple textInputSuggestions(_:) memaparkan pilihan untuk medan teks. Setiap cadangan yang boleh diambil tindakan memerlukan textInputCompletion(_:), yang membekalkan rentetan gantinya. Kedua-dua API didokumenkan untuk macOS 15 dan lebih baharu. Cadangan tanpa pengubah suai pelengkapan boleh dipaparkan tetapi tidak bertindak balas terhadap klik. Apple: textInputSuggestions(_:), Apple: textInputCompletion(_:)

Untuk medan label pendek, menggantikan keseluruhan draf adalah sesuai. Dokumentasi cadangan Apple menerangkan penggantian seluruh medan ini. Jangan anggap API ini menyediakan penyunting token berasaskan kedudukan karet atau pratonton akhiran sebaris; kedua-duanya memerlukan reka bentuk penyuntingan yang lebih khusus. Apple: textInputSuggestions(_:)

Keperluan anda Titik permulaan yang sesuai
Tawarkan label lengkap pada macOS 15 atau lebih baharu Cadangan teks natif SwiftUI
Halang medan daripada memanggil pengendali penghantaran leluhur submitScope() pada medan
Gunakan reka letak cadangan tersuai atau sokong sasaran penggunaan yang lebih lama Butang cadangan yang dimiliki app dan mengemas kini binding draf
Pastikan Return menerima item yang diserlahkan dan menggunakan arahan itu Kawalan AppKit yang dimiliki app dengan pengendalian arahan
Pratonton akhiran atau ganti perkataan pada titik sisipan Pelengkapan teks AppKit dan julat pelengkapan yang jelas

Dua baris terakhir ialah pilihan seni bina yang berpandukan API arahan dan pelengkapan yang didokumenkan. Pilihan ini berguna apabila interaksi memerlukan kawalan yang lebih daripada rentetan ganti dan sempadan penghantaran.

Bina pemilih label SwiftUI yang lengkap

Cipta projek App macOS dalam Xcode dengan SwiftUI, menggunakan SDK yang menyertakan API macOS 15. Tetapkan sasaran penggunaan kepada macOS 15 atau lebih baharu. Gantikan deklarasi app dan paparan kandungan yang dijana dengan satu fail sumber berikut, dan pastikan hanya terdapat satu deklarasi app @main dalam sasaran.

Contoh ini mengasingkan draft dan appliedLabel. Peraturan pemadanan setempatnya sengaja ringkas: pemadanan awalan yang peka huruf besar dan kecil, tiada cadangan untuk teks kosong, dan tiada cadangan padanan tepat. Swift mendokumenkan hasPrefix(_:) sebagai perbandingan awalan rentetan; peraturan pemadanan yang lebih luas ialah 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 contoh app lengkap, termasuk label sampel dan keadaannya. Contoh ini telah disemak berdasarkan dokumentasi API yang diperoleh, tetapi tidak dikompil atau dijalankan di sini kerana rantaian alat Swift dan masa jalan UI macOS tidak tersedia.

Mengapa pemilihan cadangan tidak menggunakan label

Setiap pelengkapan membekalkan rentetan untuk medan teks, yang bindingnya merujuk kepada draft. Satu-satunya kod yang menetapkan appliedLabel atau menambah applyCount ialah tindakan butang Apply Label. Oleh itu, dalam contoh ini, mengemas kini draf tidak menyediakan laluan kepada tindakan akhir itu.

Medan itu juga mempunyai submitScope(). Apple mendokumenkan pengubah suai ini sebagai menghalang pencetus penghantaran daripada sampai kepada pengubah suai penghantaran yang lebih tinggi dalam hierarki paparan. Ini penting apabila komponen itu kemudiannya dibenamkan dalam borang lebih besar yang mempunyai onSubmit leluhur. Apple: submitScope(_:)

Fokus awal ditetapkan menggunakan @FocusState. Apple mendokumenkan bahawa menetapkan nilai terikatnya secara programatik akan memindahkan fokus ke paparan berkaitan. Fokus di sini memudahkan penyuntingan; ia tidak menentukan sama ada label digunakan. Apple: FocusState

Perkara yang sengaja dibenarkan oleh contoh ini

Senarai label menawarkan cadangan dan bukannya mengehadkan input yang sah. Menaip Square Tile masih membolehkan Apply Label. Jika antara muka anda hanya membenarkan label yang disenaraikan, ubah peraturan pengesahan dengan jelas—contohnya, aktifkan butang hanya apabila draf sepadan dengan entri katalog. Jangan gantikan draf yang tidak sepadan secara senyap dengan cadangan pertama.

Contoh ini juga menggantikan keseluruhan label. Jika draf mengandungi berbilang perkataan, memilih pelengkapan bermaksud memilih nilai medan yang telah siap. Penyunting token yang perlu mengekalkan teks di sekelilingnya memerlukan penggantian yang mengambil kira pemilihan.

Fahami maksud “tanpa menghantar”

Dokumentasi onSubmit(of:_:) SwiftUI menyatakan bahawa medan teks mencetuskan tindakan penghantarannya apabila pengguna menekan Return. Tindakan penghantaran boleh dilampirkan pada paparan individu atau hierarki yang lebih besar. Periksa kedua-dua lokasi apabila pemilihan autolengkap kelihatan mencetuskan tindakan app yang tidak dijangka. Apple: onSubmit(of:_:)

submitScope() menetapkan sempadan untuk pengendali leluhur. Ia tidak menjanjikan bahawa tiada peristiwa penghantaran dalaman berlaku, penyuntingan kekal aktif, atau setiap arahan papan kekunci digunakan. Begitu juga, pengubah suai pelengkapan Apple mendokumenkan penggantian teks, bukannya penindasan menyeluruh terhadap tingkah laku kekunci Return. Ingat had ini semasa memilih interaksi. Apple: submitScope(_:), Apple: textInputCompletion(_:)

Untuk komponen sedia ada, periksa laluan ini secara berasingan:

  1. Alih keluar sebarang tindakan aplikasi akhir daripada onSubmit medan jika Return tidak sepatutnya menggunakan nilainya.
  2. Letakkan submitScope() pada medan untuk menyekat tindakan penghantaran leluhur.
  3. Periksa pintasan papan kekunci butang secara berasingan, terutamanya tindakan lalai yang jelas.
  4. Periksa sebarang pemerhati perubahan atau penetap model yang menjalankan tindakan akhir setiap kali draf berubah.

Penutupan onSubmit setempat yang kosong bukan pengganti sempadan penghantaran yang didokumenkan. Apple secara khusus menyediakan submitScope() untuk menghentikan perambatan. Jika app patut menghantar apabila Return ditekan hanya ketika tiada pelengkapan aktif, dasar papan kekunci bersyarat itu memerlukan keadaan dan pengendalian arahan tambahan. Apple: onSubmit(of:_:)

Gunakan AppKit apabila Return mesti menerima dan menghentikan arahan

Untuk NSTextField yang dimiliki app, AppKit menyediakan control(_:textView:doCommandBy:). Argumen paparan teksnya ialah penyunting medan kawalan tersebut. Delegat boleh mengendalikan arahan terikat dan memulangkan true; memulangkan false bermakna arahan itu tidak dikendalikan oleh delegat tersebut. Ini ialah titik sambungan yang didokumenkan untuk menyesuaikan arahan dalam medan teks. Apple: control(_:textView:doCommandBy:)

Pengawal pelengkapan tersuai boleh menggunakan titik sambungan itu dengan urutan berikut:

  1. Jejaki sama ada senarai cadangan terbuka dan item yang diserlahkan.
  2. Apabila arahan Return tiba dengan item yang diserlahkan dan sah, masukkan item itu ke dalam draf.
  3. Tutup senarai cadangan dan segerakkan model draf app.
  4. Pulangkan true kerana pengawal mengendalikan arahan itu.
  5. Pulangkan false untuk arahan yang tidak dikendalikan oleh pengawal.

Urutan ini ialah reka bentuk pelaksanaan yang diperoleh daripada kontrak delegat, bukannya dasar autolengkap terbina dalam. Tentukan secara berasingan tindakan Return apabila senarai ditutup. Jika anda memiliki NSTextView kendiri pula, cantuman yang sepadan ialah textView(_:doCommandBy:); Apple menyatakan bahawa memulangkan true menghalang paparan teks daripada melaksanakan arahan itu sendiri. Apple: textView(_:doCommandBy:)

Untuk pelengkapan teks itu sendiri, NSTextView.insertCompletion(_:forPartialWordRange:movement:isFinal:) menyokong perubahan penggantian dan pemilihan. Dengan isFinal: false, akhiran yang dicadangkan dipilih; dengan isFinal: true, pemilihan diruntuhkan selepas perkataan yang dilengkapkan. Bendera itu menerangkan keadaan pelengkapan, bukannya penghantaran aplikasi. Argumen movement mengenal pasti cara navigasi atau pemilihan pelengkapan berlaku. Apple: insertCompletion(_:forPartialWordRange:movement:isFinal:)

Kekalkan julat teks yang betul

rangeForUserCompletion AppKit mengenal pasti perkataan separa dari permulaannya hingga titik sisipan. Gunakan julat yang sesuai dengan penggantian yang dimaksudkan dan kira semula selepas suntingan atau pergerakan karet. Julat yang pernah ditangkap tidak patut digunakan semula secara membuta tuli selepas teks berubah. Apple: rangeForUserCompletion

Apabila menjambatani julat teks, jangan gantikan kiraan aksara Swift dengan panjang NSString. Dokumentasi Swift menerangkan bahawa String mengira gugusan grafem lanjutan, manakala panjang NSString mengira unit kod UTF-16. Nilai-nilai ini boleh berbeza. Penggantian binding seluruh medan dalam contoh SwiftUI mengelakkan penukaran julat ini sepenuhnya. Swift: Strings and Characters)

Sahkan penyisipan dan penggunaan secara berasingan

Gunakan draf yang kelihatan, nilai yang digunakan dan pembilang untuk menyemak hasil pada setiap versi macOS yang disokong. Berikut ialah semakan yang dijangka untuk contoh ini, bukannya hasil ujian yang dilaporkan.

Tindakan Hasil yang boleh diperhatikan dan dijangka
Taip Bl Draf ialah Bl; label yang sepadan ditawarkan
Klik Blue Tile Draf menjadi Blue Tile; nilai yang digunakan dan kiraan kekal sama
Tekan Return semasa menyunting App tidak menggunakan draf
Klik Apply Label Nilai yang digunakan menyalin draf; kiraan bertambah sekali
Sunting draf selepas itu Nilai yang digunakan sebelum ini kekal kelihatan
Kosongkan draf Cadangan kosong dan Apply Label dinyahdayakan
Taip bl Tiada padanan berdasarkan peraturan peka huruf besar dan kecil dalam contoh ini

Uji juga pemilihan papan kekunci dalam senarai cadangan natif dan ulangi semakan selepas membenamkan medan dalam paparan induk sebenar. Apple menyatakan bahawa peristiwa seperti memindahkan tetingkap macOS boleh menutup antara muka cadangan natif, jadi draf patut kekal boleh digunakan tanpa senarai itu. Apple: textInputSuggestions(_:)

Jika sasaran penggunaan anda lebih awal daripada macOS 15, sediakan paparan natif dengan ketersediaan berpagar dan pilihan sandaran, atau gunakan butang cadangan milik app sepanjang masa. Butang sandaran boleh menetapkan labelnya terus kepada draf dan mengekalkan tindakan akhir pada Apply Label. Ini menyediakan pemisahan draf dan penggunaan yang sama, sementara navigasi papan kekunci dan pembentangan kekal sebagai tanggungjawab komponen anda.

Untuk tugasan pemilih label ini, sempadan yang berguna boleh diperhatikan: menerima pelengkapan mengubah draf yang boleh disunting, dan hanya tindakan akhir yang jelas mengubah label yang digunakan. Gunakan cadangan natif dan skop penghantaran SwiftUI untuk sempadan itu; beralih kepada pengendalian arahan AppKit apabila menggunakan kekunci Return itu sendiri ialah sebahagian daripada keperluan.

Sumber