Agar NSTextView yang dibuat secara terprogram menerima ketikan, aktifkan pengeditan, lampirkan ke jendela Anda, lalu panggil makeFirstResponder(_:) pada jendela tersebut. Jendela itu juga harus dapat menjadi jendela utama. Berikut contoh lengkap dan urutan pemecahan masalah yang terarah.
Apa saja yang harus terpenuhi agar pengetikan berfungsi?
Panduan ini membahas satu tugas: membuat jendela aplikasi Mac kecil tempat seseorang dapat mengetik dan mengedit teks multiline. Panduan ini menggunakan AppKit secara langsung, lalu menjelaskan pilihan siklus hidup yang sesuai saat menyematkan editor ke SwiftUI.
Ada tiga kondisi terpisah yang penting. Tampilan teks harus mengizinkan pengeditan, jendela harus menerima peristiwa keyboard, dan tampilan teks harus menjadi responder pertama yang dimaksud di dalam jendela tersebut. Apple menyebut keyWindow aplikasi sebagai jendela yang saat ini menerima peristiwa keyboard; menampilkan jendela saja tidak menentukan tampilan mana yang harus mengedit teks. Apple: keyWindow
Mengatur isEditable = true mengizinkan pengeditan dan juga membuat teks dalam tampilan dapat dipilih. Apple mendokumentasikan bahwa tampilan teks secara default dapat diedit, tetapi pengaturan ini secara eksplisit memperjelas konfigurasi dalam contoh. Dapat diedit saja tidak menentukan tujuan keyboard. Apple: isEditable
Untuk menetapkan fokus, gunakan makeFirstResponder(textView) milik jendela yang menaunginya. Apple secara eksplisit menginstruksikan pengembang agar menggunakan metode ini, bukan memanggil becomeFirstResponder() milik tampilan teks secara langsung. Dengan begitu, jendela dapat mengoordinasikan perpindahan antarresponder. Apple: NSTextView
Buat jendela yang dapat diedit secara lengkap di Swift
Program mandiri berikut menargetkan macOS 14 atau versi lebih baru dan menggunakan toolchain Swift terkini dengan SDK macOS. Judul jendelanya adalah Local Editor, dan teks awalnya adalah Sample note. Teks tetap berada di memori; contoh ini tidak menerapkan penyimpanan.
Simpan program lengkap sebagai EditorDemo.swift. Gunakan sebagai executable mandiri, bukan dengan menambahkan titik masuk kedua ke aplikasi yang sudah ada. Swift hanya mengizinkan satu titik masuk program; tipe @main di bawah ini menyediakannya. The Swift Programming Language: Attributes
import AppKit
@MainActor
final class EditorDelegate: NSObject, NSApplicationDelegate {
private var editorWindow: NSWindow?
func applicationDidFinishLaunching(_ notification: Notification) {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 640, height: 420),
styleMask: [.titled, .closable, .miniaturizable, .resizable],
backing: .buffered,
defer: false
)
window.title = "Local Editor"
window.isReleasedWhenClosed = false
let scrollView = NSTextView.scrollableTextView()
guard let textView = scrollView.documentView as? NSTextView else {
fatalError("Expected an NSTextView document view")
}
textView.isEditable = true
textView.isSelectable = true
textView.isRichText = false
textView.allowsUndo = true
textView.font = NSFont.systemFont(ofSize: 16)
textView.textContainerInset = NSSize(width: 12, height: 12)
textView.string = "Sample note\n"
textView.setSelectedRange(
NSRange(location: (textView.string as NSString).length, length: 0)
)
scrollView.frame = NSRect(x: 0, y: 0, width: 640, height: 420)
scrollView.autoresizingMask = [.width, .height]
window.contentView = scrollView
window.initialFirstResponder = textView
editorWindow = window
let mainMenu = NSMenu()
let appMenuItem = NSMenuItem()
mainMenu.addItem(appMenuItem)
let appMenu = NSMenu()
appMenu.addItem(NSMenuItem(
title: "Quit Local Editor",
action: #selector(NSApplication.terminate(_:)),
keyEquivalent: "q"
))
appMenuItem.submenu = appMenu
NSApp.mainMenu = mainMenu
window.center()
window.makeKeyAndOrderFront(nil)
NSApp.activate()
guard textView.window === window,
textView.acceptsFirstResponder else {
print("Editor is not ready to receive focus")
return
}
let accepted = window.makeFirstResponder(textView)
print("Focus request returned:", accepted)
print("Editor is first responder:", window.firstResponder === textView)
print("Window is key:", window.isKeyWindow)
}
func applicationShouldTerminateAfterLastWindowClosed(
_ sender: NSApplication
) -> Bool {
true
}
}
@main
@MainActor
enum EditorDemo {
static func main() {
let application = NSApplication.shared
application.setActivationPolicy(.regular)
let delegate = EditorDelegate()
application.delegate = delegate
withExtendedLifetime(delegate) {
application.run()
}
}
}
Di Mac dengan Xcode atau Command Line Tools terpasang, kompilasi dan jalankan dari direktori yang berisi file tersebut:
xcrun swiftc -parse-as-library \
-target "$(uname -m)-apple-macos14.0" \
-framework AppKit EditorDemo.swift -o EditorDemo
./EditorDemo
Contoh ini telah ditinjau berdasarkan dokumentasi yang diperoleh, tetapi tidak dapat dikompilasi atau dijalankan di lingkungan riset ini, yang menggunakan Linux dan tidak memiliki kompiler Swift. Verifikasi perilaku jendela di macOS dengan pemeriksaan di bawah.
Mengapa pengaturan ini mendukung input keyboard
Metode praktis scrollableTextView() mengembalikan NSScrollView; kode mengambil tampilan teksnya melalui documentView. Menempatkan editor teks di dalam tampilan gulir memberi contoh kecil ini wadah sederhana untuk konten multiline. Factory ini tersedia mulai macOS 10.14. Apple: scrollableTextView()
Kode memasang wadah tersebut sebelum meminta fokus. Kode juga mengatur initialFirstResponder, yang didefinisikan Apple sebagai tampilan yang dipilih saat jendela pertama kali muncul. Pemanggilan makeFirstResponder(_:) setelahnya secara eksplisit meminta editor dan memberikan hasil untuk diperiksa. Fokus awal dan permintaan fokus berikutnya berlaku pada tahap siklus hidup jendela yang berbeda. Apple: initialFirstResponder, Apple: makeFirstResponder(_:)
Jendela menggunakan gaya berjudul agar dapat menjadi jendela utama. makeKeyAndOrderFront(nil) meminta agar jendela ditampilkan sekaligus menjadi jendela utama. Apple membedakannya dari orderFront(_:), yang mengubah urutan tanpa mengubah jendela utama. Apple: canBecomeKey, Apple: makeKeyAndOrderFront(_:)
Pemanggilan activate() menjadi alasan program khusus ini memerlukan macOS 14 atau versi lebih baru. Apple menjelaskan bahwa aktivasi adalah permintaan, bukan jaminan. Jika jendela muncul tetapi tidak menerima ketikan, aktifkan aplikasi dengan mengeklik jendelanya lalu periksa kembali. Jangan menganggap cetakan diagnostik langsung sebagai bukti bahwa aktivasi sudah selesai. Apple: activate()
Titik masuk dan delegasi ditandai dengan @MainActor, yang menurut definisi Apple memiliki executor yang setara dengan antrean dispatch utama. Program ini juga memulai loop peristiwa AppKit dengan run(), sehingga AppKit dapat mendistribusikan peristiwa pengguna setelah penyiapan selesai. Apple: MainActor, Apple: run()
Diagnosis editor yang terlihat tetapi tetap tidak menerima ketikan
Ikuti urutan berikut. Urutan ini memisahkan pemasangan, kelayakan jendela, pemilihan responder, dan kemampuan mengedit, sehingga setiap pengamatan menunjukkan tindakan berikutnya yang spesifik.
| Pemeriksaan | Arti hasilnya | Tindakan berikutnya |
|---|---|---|
textView.window === window |
Editor berada di jendela yang dimaksud. | Jika salah, selesaikan pemasangan hierarki tampilan sebelum meminta fokus. |
window.canBecomeKey |
Jendela memenuhi syarat untuk menjadi jendela utama. | Jika salah, periksa gayanya atau subclass kustomnya. |
window.isKeyWindow |
Jendela ini saat ini menjadi jendela utama di dalam aplikasi. | Aktifkan aplikasi dan pilih jendela yang dimaksud, lalu periksa kembali. |
textView.acceptsFirstResponder |
Editor menerima permintaan fokus. | Jika salah, periksa konfigurasi dan override subclass. |
window.firstResponder === textView |
Editor ini secara spesifik memiliki status responder pertama. | Jika salah, periksa perpindahan fokus dan responder saat ini. |
textView.isEditable |
Pengeditan oleh pengguna diaktifkan. | Atur menjadi true jika pengeditan memang diinginkan. |
Pemeriksaan responder sangat berguna karena makeFirstResponder(_:) memiliki detail halus yang terdokumentasi: metode ini dapat mengembalikan true ketika responder yang diminta menolak status responder pertama dan jendela menjadi responder pertama. Apple juga menyarankan untuk memeriksa acceptsFirstResponder sebelum membuat permintaan secara eksplisit. Karena itu, periksa nilai pengembaliannya sekaligus identitas firstResponder setelah pemanggilan. Apple: makeFirstResponder(_:)
Hasil false dapat berarti responder yang ada menolak untuk mengundurkan diri. Tinjau perilaku responder tersebut dan delegasi pengeditan yang terlibat. Sebagai contoh, Apple mendokumentasikan bahwa delegasi tampilan teks dapat menolak penghentian pengeditan melalui textShouldEndEditing(_:). Mengulangi permintaan fokus yang sama tanpa menyelesaikan penolakan tersebut tidak mengatasi penyebabnya. Apple: makeFirstResponder(_:), Apple: NSTextView
Untuk pemeriksaan manual dasar, ketik Blue square, tekan Return, ketik Green circle, lalu gunakan tombol panah dan Delete untuk mengedit teks. Ubah ukuran jendela dan lanjutkan mengetik. Ini adalah langkah verifikasi yang disarankan, bukan hasil pengujian yang dilaporkan. Tutup jendela setelah selesai; contoh ini kemudian berhenti.
Apa yang berubah untuk jendela tanpa bingkai?
Jendela tanpa bingkai yang terlihat tetap mungkin tidak dapat menjadi jendela utama. Apple mendokumentasikan bahwa NSWindow biasa tanpa gaya berjudul secara default tidak menjadi jendela utama. Kustomisasi yang didukung adalah dengan meng-override canBecomeKey di subclass NSWindow dan mengembalikan true saat jendela itu memang ditujukan untuk menerima input keyboard. Apple: canBecomeKey
Mulailah dengan contoh berjendela berjudul untuk memastikan entri teks berfungsi. Jika antarmuka nantinya memerlukan jendela tanpa bingkai, ubah implementasi jendelanya sambil mempertahankan pemeriksaan pemasangan, kemampuan mengedit, dan responder yang sama. Mengubah tampilan jendela tidak menggantikan persyaratan tersebut.
Bagaimana editor yang sama sebaiknya bekerja di dalam SwiftUI?
Gunakan NSViewRepresentable untuk mengintegrasikan tampilan teks AppKit ke dalam hierarki SwiftUI. Apple menetapkan pembuatan dan konfigurasi awal pada makeNSView(context:), pembaruan status yang berkelanjutan pada updateNSView(_:context:), serta koordinasi pesan delegasi atau target-action pada sebuah koordinator. Apple: NSViewRepresentable
Untuk editor ini, rancangan praktisnya adalah mengembalikan tampilan gulir sebagai tampilan yang direpresentasikan dan menyimpan referensi ke tampilan teks dokumennya di koordinator atau wadah kustom. Konfigurasikan kemampuan mengedit saat pembuatan. Teruskan perubahan teks pengguna melalui delegasi jika antarmuka SwiftUI di sekitarnya memerlukan binding.
Anggap pemasangan sebagai peristiwa terpisah dari pembuatan. Sebelum meminta fokus, periksa apakah window milik editor tidak nil. Subclass AppKit dapat menggunakan viewDidMoveToWindow() untuk mengamati perubahan hierarki; Apple mendokumentasikan bahwa window yang nil di sana berarti tampilan saat ini berada di luar jendela mana pun. Ini menyediakan hook siklus hidup terdokumentasi untuk menerapkan permintaan fokus yang memperhatikan status pemasangan. Apple: viewDidMoveToWindow()
Sebagai kebijakan implementasi, minta fokus awal satu kali untuk penyajian yang dimaksud, atau sebagai respons terhadap tindakan eksplisit seperti Focus Editor. Hindari memanggil makeFirstResponder(_:) tanpa syarat pada setiap pembaruan SwiftUI: kebijakan itu akan berulang kali memindahkan fokus kembali setelah seseorang memilih kontrol lain. Periksa jendela yang menaungi editor saat permintaan dibuat, alih-alih menganggap jendela yang tersimpan sebelumnya masih sesuai.
Hindari juga mereset string editor pada setiap pembaruan. Pada pembungkus berbasis binding, bandingkan status yang masuk dengan teks saat ini, lalu pertimbangkan teks bertanda dan seleksi sebelum mengganti konten. Contoh mandiri menginisialisasi string-nya satu kali; editor yang disinkronkan memerlukan kebijakan pembaruan yang eksplisit.
Biarkan SwiftUI mengatur ukuran tampilan akar yang direpresentasikan. Apple memperingatkan bahwa mengubah frame atau bounds tampilan tersebut secara langsung bertentangan dengan pengelolaan tata letak SwiftUI. Karena itu, pengaturan frame langsung dalam program AppKit mandiri bukan pola ukuran yang dapat diterapkan pada tampilan akar yang dikelola SwiftUI. Apple: NSViewRepresentable
Apakah menerima ketikan memerlukan susunan TextKit kustom?
Untuk tugas ini, gunakan perilaku pengeditan yang sudah ada di NSTextView. Apple menjelaskannya sebagai bagian depan sistem teks yang menggambar teks dan menangani seleksi, modifikasi, pengelolaan input, serta pengikatan tombol. Pengetikan biasa tidak memerlukan pembangunan ulang fasilitas tersebut. Apple: NSTextView
Pemilihan mesin TextKit merupakan keputusan terpisah dari fokus keyboard. Apple mendokumentasikan bahwa init(frame:) menggunakan NSLayoutManager secara default, sementara tampilan teks yang dirakit secara manual dapat menggunakan NSTextLayoutManager di macOS 12 atau versi lebih baru melalui inisialisator yang ditetapkan. Pilih mesin berdasarkan kebutuhan tata letak; beralih mesin bukan pengganti untuk menjadikan editor sebagai responder pertama. Apple: init(frame:textContainer:)
Satu diagnosis memerlukan kehati-hatian: membaca layoutManager belum tentu merupakan pemeriksaan pasif. Apple mendokumentasikan bahwa mengaksesnya pada tampilan teks atau wadah teks yang menggunakan sistem baru dapat memicu mode kompatibilitas dengan NSLayoutManager. Contoh ini menghindari pemeriksaan tersebut dan tidak membuat asumsi tentang mesin yang dipilih factory tampilan gulir. Apple: NSTextView
Hasil yang berfungsi itu konkret: jendela aplikasi yang dimaksud aktif dan menjadi jendela utama, tampilan teksnya yang dapat diedit adalah responder pertama, dan pengetikan mengubah teks yang ditampilkan. Pastikan perilaku tersebut pada jendela kecil terlebih dahulu, lalu pertahankan pemeriksaan yang sama saat menambahkan integrasi SwiftUI, tata letak kustom, atau kontrol tambahan.
Sumber
- NSTextView — Apple.
- isEditable — Apple.
- keyWindow — Apple.
- makeFirstResponder(_:) — Apple.
- initialFirstResponder — Apple.
- makeKeyAndOrderFront(_:) — Apple.
- canBecomeKey — Apple.
- scrollableTextView() — Apple.
- activate() — Apple.
- MainActor — Apple.
- run() — Apple.
- NSViewRepresentable — Apple.
- viewDidMoveToWindow() — Apple.
- init(frame:textContainer:) — Apple.
- Attributes — The Swift Project.