Untuk memastikan medan numerik kekal boleh diedit selepas nilai pilihannya menjadi nil, simpan teks yang boleh diedit secara berasingan daripada nombor. Mula-mula periksa penghuraian, keadaan dilumpuhkan dan fokus; kemudian gunakan draf berasaskan rentetan apabila app anda memerlukan tingkah laku pemulihan yang jelas.

Panduan ini merangkumi medan numerik dalam app macOS yang anda bangunkan: memasukkan Tile count sintetik, memadamkannya dan memasukkan nombor lain. Matlamatnya adalah untuk mengekalkan nilai kosong yang bermakna sambil membolehkan pengeditan seterusnya.

Mengapakah nombor pilihan itu menjadi nil?

Apple menyediakan pemula SwiftUI khusus untuk nilai pilihan: TextField(value:format:prompt:label:). Tingkah laku yang didokumenkannya ialah menetapkan binding kepada nil apabila gaya format tidak dapat menghuraikan input. Oleh itu, penukaran yang gagal ialah keadaan API ini yang dijangka. Apple tidak menyifatkan keadaan itu sebagai melumpuhkan medan. Pemula gaya format pilihan ini memerlukan macOS 12 atau lebih baharu. Pemula TextField nilai pilihan Apple

Periksa pemula yang sebenarnya digunakan oleh app anda. Pemula lama formatter: mempunyai tingkah laku kegagalan yang didokumenkan secara berbeza: jika penukaran gagal, nilai terikat tidak berubah. Menggantikan formatter: dengan format: mengubah kontrak itu, jadi anggapnya sebagai perubahan tingkah laku yang disengajakan, bukannya pembaikan sejagat. Pemula TextField berasaskan formatter Apple

Isu reka bentuk asasnya ialah nombor pilihan tidak dapat mewakili setiap keadaan pengeditan. Medan kosong, tanda tolak yang baru ditaip sebahagian dan rentetan tidak sah semuanya mungkin tidak mempunyai integer yang boleh digunakan, tetapi teks drafnya berbeza. Sebab itulah draf berasingan berguna: medan boleh mengekalkan tepat apa yang sedang diedit oleh pengguna sementara app menentukan bila nilai numerik patut diterima.

Diagnosis kegagalan sebelum mengubah medan

Ikuti urutan ini dalam tetingkap app anda sendiri. Langkah ini membezakan masalah penukaran daripada masalah interaksi tanpa mengandaikan kecacatan pelaksanaan macOS tertentu.

  1. Periksa sama ada teks benar-benar berubah. Klik medan, pilih kandungannya dan taip 24. Jika aksara berubah tetapi model numerik tidak, periksa penukaran dan pengendalian komit.
  2. Periksa keadaan interaksi. Cari keadaan seperti .disabled(tileCount == nil) pada medan dan bekasnya. Keadaan itu menghalang pengeditan yang diperlukan untuk pulih daripada nilai kosong.
  3. Periksa sama ada medan masih wujud. Jika paparan anda mencipta editor hanya dalam cabang if let, menetapkan nilai kepada nil boleh mengalih keluar editor. Pastikan draf dan editor tersedia secara berasingan daripada nombor pilihan.
  4. Periksa fokus secara berasingan. Jika medan menerima klik dan kemudian taipan berfungsi, pulihkan fokus melalui API awam yang sesuai.
  5. Kecilkan contoh. Buat sementara waktu, buang transformasi binding tersuai dan penyegerakan model. Hasilkan semula peralihan daripada 12 kepada kosong kepada 24 dalam paparan kecil.

Induk yang dilumpuhkan memberi kesan: Apple mendokumenkan bahawa .disabled(true) pada bekas mengatasi .disabled(false) pada anak. Betulkan keadaan induk itu dan bukannya berulang kali mendayakan medan sahaja. Dokumentasi disabled(_:) Apple

Periksa juga setter binding tersuai. Setter yang keluar awal setiap kali nombor sedia ada ialah nil tidak dapat menerima nombor gantian. Begitu juga, memetakan setiap penukaran yang gagal kepada 0 menghilangkan perbezaan antara nilai yang tiada dengan sifar sebenar. Itu ialah keputusan logik app; perubahan fokus tidak dapat membaikinya.

Kekalkan draf yang boleh diedit dan komitkan nombor secara jelas

Medan berasaskan rentetan memberi anda kawalan terhadap tiga keadaan berasingan: teks semasa, nombor pilihan yang diterima dan mesej pengesahan. Apple mendokumenkan bahawa TextField yang terikat pada rentetan mengemas kini bindingnya secara berterusan semasa pengeditan. Ini menjadikannya tempat yang sesuai untuk mengekalkan input yang belum selesai. Gambaran keseluruhan TextField Apple

Contoh lengkap di bawah menggunakan dasar ini: input kosong bermaksud nil; integer asas 10 yang sah menggantikan nombor yang diterima; input tidak sah kekal kelihatan dan nombor yang diterima tidak berubah. Butang Clear mengosongkan kedua-dua keadaan dan meminta fokus supaya nombor lain boleh dimasukkan.

Cipta projek App macOS SwiftUI dalam Xcode dengan sasaran penggunaan macOS 12 atau lebih baharu. Gantikan pengisytiharan app dan paparan kandungan yang dijana dengan satu fail ini, dan kekalkan hanya satu pengisytiharan @main dalam sasaran. Contoh ini hanya menggunakan tetingkapnya sendiri dan label sintetik. Swift dan SDK macOS tidak tersedia dalam persekitaran penyelidikan, jadi program ini telah diperiksa tetapi tidak dikompil atau dijalankan.

import SwiftUI

@main
struct NumericEditingDemoApp: App {
    var body: some Scene {
        WindowGroup {
            NumericEditingView()
        }
    }
}

struct NumericEditingView: View {
    @State private var tileCount: Int? = 12
    @State private var draft = "12"
    @State private var message: String? = nil
    @FocusState private var fieldIsFocused: Bool

    private var acceptedDescription: String {
        if let value = tileCount {
            return String(value)
        }
        return "None"
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            TextField("Tile count", text: $draft)
                .textFieldStyle(.roundedBorder)
                .focused($fieldIsFocused)
                .onSubmit {
                    applyDraft()
                }

            HStack {
                Button("Apply") {
                    applyDraft()
                }

                Button("Clear") {
                    tileCount = nil
                    draft = ""
                    message = nil
                    fieldIsFocused = true
                }

                Button("Edit") {
                    fieldIsFocused = true
                }
            }

            Text("Accepted count: \(acceptedDescription)")

            if let message = message {
                Text(message)
                    .foregroundColor(.secondary)
            }
        }
        .padding(20)
        .frame(width: 380)
    }

    private func applyDraft() {
        if draft.isEmpty {
            tileCount = nil
            message = nil
            return
        }

        guard let parsed = Int(draft, radix: 10) else {
            message = "Enter a whole number without spaces, or clear the field."
            fieldIsFocused = true
            return
        }

        tileCount = parsed
        draft = String(parsed)
        message = nil
    }
}

Medan sentiasa terikat pada draft, jadi menetapkan tileCount kepada nil tidak mengalih keluar keadaan teks yang boleh diedit. Penukaran hanya berlaku apabila pengguna mengklik Apply atau menekan Return. Selepas penghantaran yang tidak sah, mesej kekal sehingga penghantaran seterusnya atau Clear; draf kekal tersedia untuk pembetulan.

guard menghalang penukaran yang gagal daripada menggantikan nombor yang diterima. Penghantaran yang berjaya menormalkan ejaan melalui String(parsed): contohnya, memasukkan +024 menghasilkan 24. Ini ialah dasar contoh yang jelas. Buang penetapan normalisasi itu jika anda mahu mengekalkan ejaan yang dihantar.

Pilih tatabahasa numerik dengan sengaja

Contoh ini menerima integer bertanda tanpa pemisah kumpulan dalam asas 10. Ruang, pecahan perpuluhan dan pemisah kumpulan ditolak. Swift mendokumenkan bahawa Int(_:radix:) mengembalikan nil untuk input yang tidak sah atau nilai yang tidak dapat diwakili oleh jenis integer, jadi nombor terlalu besar yang ditampal juga melalui laluan pengesahan. Pemula rentetan Int Swift

Untuk medan yang memerlukan tatatanda numerik setempat, kekalkan struktur draf-dan-komit yang sama tetapi gantikan penukaran dengan strategi penghuraian Foundation yang sesuai. ParseableFormatStyle mendedahkan parseStrategy, dan kaedah parse(_:) pada strategi itu melaksanakan penukaran dan boleh melontarkan ralat. Kendalikan kegagalan sambil mengekalkan draf. Pilih konvensyen pemformatan dan penghuraian yang sepadan, kemudian uji setempat yang dimaksudkan. Dokumentasi ParseableFormatStyle Apple, Dokumentasi ParseStrategy Apple

Jika bahagian lain app anda boleh menggantikan nombor yang diterima, tentukan kesan kemas kini itu terhadap draf yang belum selesai. Contohnya, tindakan Load value yang disengajakan boleh mengemas kini kedua-dua keadaan. Elakkan daripada terus membina semula draf daripada nombor yang diterima semasa pengeditan: hal itu akan membuang teks belum selesai yang dikekalkan oleh reka bentuk ini.

Pulihkan fokus melalui SwiftUI atau AppKit

Dalam SwiftUI, lampirkan .focused($fieldIsFocused) pada editor dan tetapkan fieldIsFocused = true daripada tindakan setempat, seperti dalam contoh. Apple mendokumenkan bahawa penetapan nilai keadaan fokus akan mengalihkan fokus kepada paparan yang berkaitan. Berikan binding fokus yang berbeza kepada medan berasingan; menetapkan nilai fokus yang sama kepada berbilang medan menimbulkan kekaburan. Dokumentasi FocusState Apple

Fokus mengawal tempat pengeditan berlaku. Fokus tidak mengubah penghurai, mendayakan semula induk yang dilumpuhkan atau mencipta semula editor yang dialih keluar oleh logik paparan bersyarat. Selesaikan keadaan tersebut sebelum menambah pemulihan fokus.

Untuk medan AppKit yang dimiliki oleh app anda, periksa NSTextField.isEditable. Apple mentakrifkan sifat ini sebagai kawalan terhadap sama ada pengguna boleh mengedit nilai medan. Pulihkannya kepada true jika kod anda sendiri tersilap menetapkannya kepada false, sambil mengekalkan sebarang sekatan app yang disengajakan. Dokumentasi NSTextField.isEditable Apple

Apabila mengalihkan fokus AppKit secara jelas, periksa acceptsFirstResponder dan panggil makeFirstResponder(_:) milik tetingkap yang menempatkannya. Keputusannya perlu diteliti dengan cermat: false boleh bermaksud responder semasa enggan melepaskan fokus, manakala true juga boleh berlaku apabila responder yang dicadangkan enggan dan tetingkap menjadi responder pertama. Sahkan keadaan pengeditan yang terhasil. Untuk memilih seluruh medan sebagai gantiannya, selectText(_:) ialah operasi yang didokumenkan; ia tidak memberi kesan sebelum medan menjadi sebahagian daripada hierarki paparan tetingkap. Dokumentasi makeFirstResponder(_:) Apple, Dokumentasi selectText(_:) Apple

Hormati kitar hayat editor medan

AppKit currentEditor() mengembalikan editor medan semasa bagi kawalan itu, atau nil jika tiada. Apple menerangkan editor itu sebagai NSTextView yang dikongsi antara kawalan dalam tetingkap dan dicipta apabila diperlukan. Oleh itu, editor nil di luar sesi pengeditan aktif tidak membuktikan bahawa kemasukan numerik rosak. Dokumentasi currentEditor() Apple

Pada peringkat sistem teks, editor medan menganggap Tab, Shift-Tab dan Return sebagai isyarat untuk menamatkan pengeditan dan mungkin menukar responder pertama. Jangan ubah isFieldEditor sebagai pembaikan penukaran numerik: sifat itu mengawal tingkah laku pengeditan, bukannya penghuraian nombor pilihan. Hadkan pemeriksaan AppKit dan TextKit ini kepada kawalan yang dimiliki oleh app anda. Dokumentasi NSTextView.isFieldEditor Apple

Sahkan bahawa pemadaman dan kemasukan semula kedua-duanya berfungsi

Jalankan urutan manual ini terhadap contoh lengkap pada setiap versi macOS yang disokong oleh app anda. Ini ialah hasil yang dijangka berdasarkan logik contoh, bukannya pelaksanaan ujian yang dilaporkan.

Tindakan Nilai diterima yang dijangka Tingkah laku pengeditan yang dijangka
Lancarkan 12 Draf mengandungi 12
Klik Clear nil, dipaparkan sebagai None Draf kosong; fokus diminta
Taip 24, kemudian klik Apply 24 Draf mengandungi 24
Gantikan dengan -, kemudian tekan Return Nilai yang diterima sebelum ini Draf kekal -; mesej muncul
Gantikan dengan -8, kemudian tekan Return -8 Mesej dikosongkan
Masukkan 0, kemudian klik Apply 0 Sifar kekal berbeza daripada ketiadaan
Masukkan 2.5, kemudian klik Apply Nilai yang diterima sebelum ini Teks pecahan kekal untuk dibetulkan
Kosongkan medan, kemudian klik Apply nil Medan kekal tersedia untuk pengeditan seterusnya

Uji juga dengan menampal integer yang lebih besar daripada Int.max, mengklik di tempat lain kemudian kembali, dan memadam medan berulang kali. Dalam app bersepadu, sertakan sebarang tindakan yang mengubah model di tempat lain. Pemeriksaan ini menguji kegagalan penukaran, pemulihan fokus dan penyegerakan draf secara berasingan.

Keadaan pemulihan yang berguna adalah jelas: selepas nombor yang diterima menjadi nil, editor kekal wujud dan interaktif, dan penghantaran sah yang seterusnya memberikan nombor baharu. Dengan mengekalkan draf secara berasingan, tingkah laku itu dinyatakan dengan jelas tanpa bergantung pada perubahan tidak berdokumen kepada kawalan sokongan SwiftUI.

Sumber