Programatik olarak oluşturulan bir NSTextView’in yazı yazmayı kabul etmesi için düzenlemeyi etkinleştirin, görünümü pencerenize ekleyin ve pencerenin makeFirstResponder(_:) yöntemini çağırın. Pencerenin key window olabilmesi de gerekir. Aşağıda eksiksiz bir örnek ve odaklı bir sorun giderme sırası yer alıyor.
Yazı yazmanın çalışması için neler doğru olmalı?
Bu kılavuz tek bir görevi ele alıyor: bir kişinin çok satırlı metin yazıp düzenleyebileceği küçük bir Mac uygulama penceresi oluşturmak. Önce AppKit doğrudan kullanılıyor, ardından düzenleyici SwiftUI içine gömülürken yaşam döngüsüyle ilgili seçimler açıklanıyor.
Birbirinden ayrı üç koşul önemlidir. Metin görünümü düzenlemeye izin vermeli, pencere klavye olaylarını almalı ve metin görünümü o pencere içinde hedeflenen first responder olmalıdır. Apple, uygulamanın keyWindow öğesini klavye olaylarını o anda alan pencere olarak tanımlar; bir pencereyi yalnızca görüntülemek, metni hangi görünümün düzenleyeceğini belirlemez. Apple: keyWindow
isEditable = true ayarı düzenlemeye izin verir ve metin görünümünün seçilebilir olmasını da sağlar. Apple, metin görünümlerinin varsayılan olarak düzenlenebilir olduğunu belgeliyor; ancak bunu açıkça ayarlamak örneğin yapılandırmasını anlaşılır kılar. Düzenlenebilir olması, klavye hedefini tek başına belirlemez. Apple: isEditable
Odak için kapsayıcı pencerenin makeFirstResponder(textView) yöntemini kullanın. Apple, geliştiricilere metin görünümünün becomeFirstResponder() yöntemini doğrudan çağırmak yerine bu yöntemi kullanmalarını açıkça söylüyor. Böylece pencere, responder’lar arasındaki geçişi yönetebilir. Apple: NSTextView
Swift ile düzenlenebilir eksiksiz bir pencere oluşturma
Aşağıdaki bağımsız program macOS 14 veya üzerini hedefler ve güncel bir Swift araç zinciriyle macOS SDK’sını kullanır. Örnek pencerenin başlığı Local Editor, başlangıç metni ise Sample note şeklindedir. Metin bellekte tutulur; bu örnek kaydetme işlevi içermez.
Eksiksiz programı EditorDemo.swift adıyla kaydedin. Mevcut bir uygulamaya ikinci bir giriş noktası olarak eklemek yerine bağımsız bir yürütülebilir dosya olarak kullanın. Swift yalnızca bir program giriş noktasına izin verir; aşağıdaki @main türü bu noktayı sağlar. 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()
}
}
}
Xcode veya Command Line Tools yüklü bir Mac’te dosyanın bulunduğu dizinden derleyip çalıştırın:
xcrun swiftc -parse-as-library \
-target "$(uname -m)-apple-macos14.0" \
-framework AppKit EditorDemo.swift -o EditorDemo
./EditorDemo
Örnek, alınan belgelere göre incelendi; ancak Linux üzerinde çalışan ve Swift derleyicisi bulunmayan bu araştırma ortamında derlenemedi veya çalıştırılamadı. Aşağıdaki kontrollerle pencerenin davranışını macOS’te doğrulayın.
Bu kurulum klavye girdisi sorununu neden çözüyor?
Kolaylık yöntemi scrollableTextView() bir NSScrollView döndürür; kod, metin görünümünü documentView aracılığıyla alır. Metin düzenleyiciyi bir kaydırma görünümünün içinde tutmak, bu küçük örnekte çok satırlı içerik için kullanışlı bir kapsayıcı sağlar. Bu fabrika yöntemi macOS 10.14 ve üzeri sürümlerde kullanılabilir. Apple: scrollableTextView()
Kod, odağı istemeden önce bu kapsayıcıyı pencereye ekler. Ayrıca Apple’ın pencere ilk göründüğünde seçilen görünüm olarak tanımladığı initialFirstResponder özelliğini ayarlar. Sonraki makeFirstResponder(_:) çağrısı düzenleyiciyi açıkça ister ve incelenebilecek bir sonuç verir. Başlangıç odağı ile daha sonra yapılan odak isteği, pencerenin yaşam döngüsünün farklı aşamalarında kullanılır. Apple: initialFirstResponder, Apple: makeFirstResponder(_:)
Pencere, key window olabilmesi için başlık çubuğu stilini kullanır. makeKeyAndOrderFront(nil) hem pencerenin gösterilmesini hem de key olmasını ister. Apple bunu, sıralamayı değiştirip key window’u değiştirmeyen orderFront(_:) yönteminden ayırır. Apple: canBecomeKey, Apple: makeKeyAndOrderFront(_:)
activate() çağrısı, bu özel programın neden macOS 14 veya üzerini gerektirdiğini açıklar. Apple, uygulamanın etkinleştirilmesini bir garanti değil, istek olarak tanımlar. Pencere göründüğü hâlde yazı yazılamıyorsa pencereye tıklayarak uygulamayı etkinleştirin ve yeniden deneyin. Hemen yazdırılan bir tanılama mesajını, etkinleştirmenin tamamlandığına dair bir garanti olarak yorumlamayın. Apple: activate()
Giriş noktası ve temsilci @MainActor ile işaretlenmiştir; Apple bu aktörün yürütücüsünü ana dispatch kuyruğuna eşdeğer olarak tanımlar. Kurulum tamamlandıktan sonra AppKit’in kullanıcı olaylarını gönderebilmesi için program ayrıca run() ile AppKit’in olay döngüsünü başlatır. Apple: MainActor, Apple: run()
Görünen bir düzenleyici yazı yazmayı hâlâ kabul etmiyorsa sorunu giderme
Aşağıdaki sırayı izleyin. Görünümün pencereye eklenmesini, pencerenin uygunluğunu, responder seçimini ve düzenlenebilirliği ayrı ayrı kontrol eder; böylece her gözlem bir sonraki adımı belirler.
| Kontrol | Sonucun anlamı | Sonraki adım |
|---|---|---|
textView.window === window |
Düzenleyici hedeflenen pencereye aittir. | Yanlışsa, odak istemeden önce görünüm hiyerarşisini tamamlayın. |
window.canBecomeKey |
Pencere key olabilecek durumdadır. | Yanlışsa, pencere stilini veya özel alt sınıfını inceleyin. |
window.isKeyWindow |
Uygulama içinde şu anda bu pencere key durumundadır. | Uygulamayı etkinleştirip hedeflenen pencereyi seçin, ardından yeniden kontrol edin. |
textView.acceptsFirstResponder |
Düzenleyici odak isteğini kabul eder. | Yanlışsa, yapılandırmayı ve alt sınıf geçersiz kılmalarını inceleyin. |
window.firstResponder === textView |
Tam olarak bu düzenleyici first responder durumundadır. | Yanlışsa, odak geçişini ve mevcut responder’ı inceleyin. |
textView.isEditable |
Kullanıcı düzenlemeleri etkinleştirilmiştir. | Düzenleme isteniyorsa true olarak ayarlayın. |
Responder kontrolü özellikle yararlıdır; çünkü makeFirstResponder(_:) belgelerde açıklanan ince bir davranışa sahiptir: istenen responder first responder olmayı reddeder ve pencerenin kendisi first responder olursa true döndürebilir. Apple ayrıca isteği açıkça yapmadan önce acceptsFirstResponder değerinin kontrol edilmesini söyler. Bu nedenle hem dönüş değerini hem de sonuçta oluşan firstResponder kimliğini inceleyin. Apple: makeFirstResponder(_:)
false sonucu, mevcut first responder’ın odağı bırakmayı reddettiği anlamına gelebilir. Bu responder’ın davranışını ve düzenlemeyle ilgili tüm temsilcileri inceleyin. Örneğin Apple, metin görünümünün temsilcisinin textShouldEndEditing(_:) aracılığıyla düzenlemenin bitmesini reddedebileceğini belgeliyor. Reddin nedenini çözmeden aynı odak isteğini tekrarlamak sorunu gidermez. Apple: makeFirstResponder(_:), Apple: NSTextView
Temel bir elle kontrol için Blue square yazın, Return tuşuna basın, Green circle yazın, ardından metni düzenlemek için ok tuşlarıyla Delete tuşunu kullanın. Pencereyi yeniden boyutlandırıp yazmaya devam edin. Bunlar önerilen doğrulama adımlarıdır; test sonucu olarak raporlanmamıştır. Bitirdiğinizde pencereyi kapatın; örnek program sonlanır.
Kenarlıksız pencere kullanılırsa ne değişir?
Görünen kenarlıksız bir pencere yine de key olamayabilir. Apple, başlık çubuğu stili olmayan sıradan bir NSWindow’un varsayılan olarak key olmadığını belgeliyor. Desteklenen özelleştirme, bu pencerenin klavye girdisi alması gerektiğinde NSWindow alt sınıfında canBecomeKey yöntemini geçersiz kılıp true döndürmektir. Apple: canBecomeKey
Önce başlık çubuklu örnekle yazı girişinin çalıştığını doğrulayın. Arayüz daha sonra kenarlıksız pencere gerektirirse pencere uygulamasını değiştirirken görünümün eklenmesi, düzenlenebilirlik ve responder kontrollerini koruyun. Pencerenin görünümünü değiştirmek bu gereklilikleri ortadan kaldırmaz.
Aynı düzenleyici SwiftUI içinde nasıl çalışmalı?
AppKit metin görünümünü SwiftUI hiyerarşisine entegre etmek için NSViewRepresentable kullanın. Apple, oluşturma ve başlangıç yapılandırmasını makeNSView(context:) öğesine, devam eden durum güncellemelerini updateNSView(_:context:) öğesine, temsilci veya target-action mesajlarıyla koordinasyonu ise bir koordinatöre verir. Apple: NSViewRepresentable
Bu düzenleyici için pratik bir tasarım, temsil edilen görünüm olarak kaydırma görünümünü döndürmek ve belge metin görünümüne ilişkin başvuruyu koordinatörde veya özel bir kapsayıcıda tutmaktır. Oluşturma sırasında düzenlenebilirliği yapılandırın. Çevresindeki SwiftUI arayüzünün bir bağlamaya ihtiyaç duyması hâlinde kullanıcı metin değişikliklerini temsilci üzerinden iletin.
Görünümün pencereye eklenmesini, oluşturulmasından ayrı bir olay olarak ele alın. Odağı istemeden önce düzenleyicinin window özelliğinin nil olmadığını kontrol edin. Bir AppKit alt sınıfı, hiyerarşi değişikliklerini gözlemlemek için viewDidMoveToWindow() yöntemini kullanabilir; Apple, buradaki nil window değerinin görünümün şu anda hiçbir pencerede bulunmadığı anlamına geldiğini belgeliyor. Bu, pencereye eklenme durumunu dikkate alan odak isteği uygulamak için belgelenmiş bir yaşam döngüsü kancası sağlar. Apple: viewDidMoveToWindow()
Uygulama politikası olarak, ilk odağı hedeflenen sunum için bir kez isteyin veya Focus Editor gibi açık bir eyleme yanıt olarak isteyin. Her SwiftUI güncellemesinde koşulsuz olarak makeFirstResponder(_:) çağırmaktan kaçının: bu politika, kişi başka bir denetimi seçtikten sonra odağı tekrar düzenleyiciye taşır. Önceden kaydedilmiş bir pencerenin hâlâ uygun olduğunu varsaymak yerine istek anında kapsayıcı pencereyi kontrol edin.
Her güncellemede düzenleyicinin metnini de sıfırlamayın. Bağlama dayalı bir sarmalayıcıda gelen durumu mevcut metinle karşılaştırın ve içeriği değiştirmeden önce işaretlenmiş metni ve seçimi hesaba katın. Bağımsız örnek metin dizgesini bir kez başlatır; eşitlenen bir düzenleyici için açık bir güncelleme politikası gerekir.
Temsil edilen kök görünümün boyutunu SwiftUI’ın belirlemesine izin verin. Apple, bu görünümün frame veya bounds özelliklerini doğrudan değiştirmenin SwiftUI’ın yerleşim yönetimiyle çeliştiği konusunda uyarıyor. Bu nedenle bağımsız AppKit programındaki doğrudan çerçeve kurulumu, SwiftUI tarafından yönetilen bir kök görünüme kopyalanacak bir boyutlandırma kalıbı değildir. Apple: NSViewRepresentable
Yazı yazmayı kabul etmek için özel bir TextKit yığını gerekir mi?
Bu görev için NSTextView’in mevcut düzenleme davranışını kullanın. Apple, onu metni çizen ve seçim, değiştirme, girdi yönetimi ile tuş bağlarını işleyen metin sistemi ön ucu olarak tanımlar. Sıradan yazı yazma işlemi, bu olanakları yeniden oluşturmayı gerektirmez. Apple: NSTextView
TextKit altyapısı seçimi, klavye odağından ayrı bir karardır. Apple, init(frame:) yönteminin varsayılan olarak NSLayoutManager kullandığını belgeliyor; elle oluşturulmuş bir metin görünümü ise macOS 12 veya üzerindeki belirlenmiş başlatıcı aracılığıyla NSTextLayoutManager kullanabilir. Gereksinimlerinize göre bir altyapı seçin; altyapıyı değiştirmek, düzenleyiciyi first responder yapmanın yerini tutmaz. Apple: init(frame:textContainer:)
Bir tanılama adımında dikkatli olmak gerekir: layoutManager değerini okumak mutlaka pasif bir inceleme değildir. Apple, yeni sistemi kullanan bir metin görünümünde veya metin kapsayıcısında bu özelliğe erişmenin NSLayoutManager kullanan uyumluluk kipini tetikleyebileceğini belgeliyor. Örnek bu özelliği sorgulamaz ve kaydırılabilir kolaylık fabrikasının hangi altyapıyı seçtiğine dair bir varsayımda bulunmaz. Apple: NSTextView
Çalışır durumda olduğu somut olarak şunlarla anlaşılır: hedeflenen uygulama penceresi etkin ve key durumundadır, düzenlenebilir metin görünümü first responder’dır ve yazılanlar görüntülenen metni değiştirir. Önce bu davranışı küçük pencerede doğrulayın; ardından SwiftUI entegrasyonu, özel yerleşim veya ek denetimler eklerken aynı kontrolleri koruyun.
Kaynaklar
- 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.