Mac uygulamanızın sarılmış NSTextView’inde ekleme noktasını görünür kılmak için klavye odağını geri yükleyin ve geçerli seçimiyle scrollRangeToVisible(_:) çağrısını yapın. İmleç rengini ayrıca yapılandırın. İmleci ekranda tutmak ve yanıp sönmesini durdurmak farklı denetimler gerektirir.
Bu kılavuz, Ekleme noktasını göster düğmesi bulunan küçük bir yerel düzenleyici oluşturur. Belgede başka bir yere kaydırdıktan sonra düzenleme konumunuza dönmek gibi bilindik bir masaüstü görevini ele alır. Uygulama, geçerli seçimi korur ve normal metin girişini ve imleç çizimini AppKit’in yönetmesine izin verir.
Ekleme noktasının neden görünmediğini belirleyin
Gözle görülür belirtiden başlayın. Kaydırma, odak, seçim ve imleç animasyonu birbirine benzer görünebilir, ancak her biri farklı bir çözüm gerektirir.
| Gözlemlediğiniz durum | Denetlenecek şey | Uygun eylem |
|---|---|---|
| Düzenleme konumunuz belgenin görünür alanının dışında | Metin görünümünün geçerli seçimi | Bu aralığı görünür alana kaydırın |
| Başka bir denetime tıklayınca düzenleyicide yazma duruyor | Pencerenin ilk yanıtlayıcısı | İstendiğinde odağı açıkça geri verin |
| İmleci arka plandan ayırt etmek zor | İmleç ve arka plan renkleri | Metin görünümünün renk özelliklerini yapılandırın |
| Tek bir imleç yerine metin vurgulanıyor | Seçili aralığın sıfırdan büyük uzunlukta olup olmadığı | Kullanıcı taşınmasını istemediği sürece seçimi koruyun |
| Düzenleyici etkinken imleç kaybolup yeniden beliriyor | Normal imleç animasyonu | Yanıp sönmeyi kaydırmadan ayrı değerlendirin |
Apple, scrollRangeToVisible(_:) yöntemini, sağlanan aralığın ilk karakterleri görünür olana kadar çevreleyen kaydırma görünümünü kaydıran bir yöntem olarak belgeliyor. Bu yöntem aralığı ortalamayı veya uzun bir seçimin tamamını görüntülemeyi garanti etmez. Böylece glif dikdörtgenlerini kendiniz hesaplamadan ekleme konumunu görünür kılmak için uygundur. Apple’ın kaydırma belgeleri.
Odak, kapsayıcı pencereye aittir. Apple, çağıranlara metin görünümünün becomeFirstResponder() yöntemini doğrudan çağırmak yerine window.makeFirstResponder(textView) kullanmalarını açıkça söyler. Aşağıdaki uygulama bu yolu izler ve hangi nesnenin gerçekten ilk yanıtlayıcı olduğunu denetler. Apple’ın NSTextView belgeleri, ilk yanıtlayıcı geçişi belgeleri.
Göster komutuyla yerel bir düzenleyici oluşturun
Xcode’da SwiftUI ve Swift kullanarak bir macOS App projesi oluşturun. Bu örnekte dağıtım hedefini macOS 13 veya üzeri olarak ayarlayın. Oluşturulan uygulama giriş noktasını aşağıdaki ilk dosyayla değiştirin ve ikinci dosyayı aynı uygulama hedefine ekleyin. Bu dosyalar birlikte örnek uygulamanın tamamını oluşturur.
Örnek, sentetik metin kullanır ve düzenlemeleri AppKit görünümünün yaşam süresi boyunca bu görünümde tutar. Belge kaydetme uygulaması veya harici metin modeli içermez. Uygulamayı kapatmak düzenlemeleri siler. Kod, alınan belgelere göre incelendi; ancak kullanılabilir ortam Linux olduğundan ve AppKit SDK’sı bulunmadığından burada derlenmedi veya çalıştırılmadı.
SwiftUI penceresini oluşturun
Düğme bir istek numarasını artırır. Böylece her SwiftUI güncellemesinde düzenleyicinin odağını geri almak veya kaydırmak yerine sarmalayıcının işlemesi gereken belirli bir komut olur.
// CaretDemoApp.swift
import SwiftUI
@main
struct CaretDemoApp: App {
var body: some Scene {
WindowGroup("Local Editor") {
EditorScreen()
}
}
}
struct EditorScreen: View {
@State private var revealRequest = 0
private let sampleText = (1...80)
.map { "Sample row \($0): blue square, green circle." }
.joined(separator: "\n")
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Button("Reveal insertion point") {
revealRequest += 1
}
CaretEditor(
initialText: sampleText,
revealRequest: revealRequest
)
}
.padding()
.frame(minWidth: 480, minHeight: 320)
}
}
Kaydırılabilir AppKit düzenleyicisini sarın
NSViewRepresentable, AppKit görünümünü SwiftUI hiyerarşisinde yönetmek için Apple’ın sağladığı köprüdür. Burada temsil edilen görünüm bir NSScrollView, belge görünümüyse bir NSTextView’dir. Koordinatör, işlenen son isteği hatırlar. Apple, oluşturma sırasında kullanılan kaydırılabilir metin görünümü fabrikasını sağlar. NSViewRepresentable, scrollableTextView().
// CaretEditor.swift
import AppKit
import SwiftUI
@MainActor
struct CaretEditor: NSViewRepresentable {
let initialText: String
let revealRequest: Int
@MainActor
final class Coordinator {
var lastHandledRequest: Int
init(lastHandledRequest: Int) {
self.lastHandledRequest = lastHandledRequest
}
}
func makeCoordinator() -> Coordinator {
Coordinator(lastHandledRequest: revealRequest)
}
func makeNSView(context: Context) -> NSScrollView {
let scrollView = NSTextView.scrollableTextView()
scrollView.hasVerticalScroller = true
guard let textView = scrollView.documentView as? NSTextView else {
preconditionFailure("Expected an NSTextView document view")
}
textView.isRichText = false
textView.isEditable = true
textView.isSelectable = true
textView.allowsUndo = true
textView.drawsBackground = true
textView.backgroundColor = .textBackgroundColor
textView.textColor = .textColor
textView.insertionPointColor = .textColor
textView.font = .systemFont(ofSize: 15)
textView.textContainerInset = NSSize(width: 12, height: 12)
textView.string = initialText
let end = (initialText as NSString).length
textView.setSelectedRange(NSRange(location: end, length: 0))
return scrollView
}
func updateNSView(_ scrollView: NSScrollView, context: Context) {
guard revealRequest != context.coordinator.lastHandledRequest else {
return
}
context.coordinator.lastHandledRequest = revealRequest
guard let textView = scrollView.documentView as? NSTextView,
let window = textView.window,
textView.acceptsFirstResponder else {
return
}
guard window.makeFirstResponder(textView),
window.firstResponder === textView else {
return
}
textView.scrollRangeToVisible(textView.selectedRange())
}
}
Düzenleyici, örnek metninin sonunda sıfır uzunluklu bir seçimle başlar. Ekleme noktasını göster düğmesine basıldığında düzenleyici odağı alır ve bu konumu görünür kılar. Sonraki basışlarda güncel seçim kullanılır; böylece imleci başka bir yere taşımak, komutun göstereceği konumu değiştirir. Komut metni değiştirmez veya seçimi taşımaz.
Son konum, UTF-16 kod birimlerini sayan NSString.length değerini kullanır. Swift’in UTF-16 görünümü, dizinlenmiş NSString API’leriyle eşleşir; Swift’in String.count değeri ise karakterleri sayar. AppKit metin aralıkları oluştururken bu gösterimleri tutarlı kullanın. Mevcut bir selectedRange() değerini okumak, göster komutunun kendisi için bir ofset hesaplamayı önler. NSString length, Swift String.UTF16View, Swift String.count.
SwiftUI güncellemeleri sırasında düzenleme durumunu koruyun
Temel tasarım tercihi, oluşturma ile komutları birbirinden ayırmaktır. makeNSView düzenleyiciyi bir kez başlatır. updateNSView değişen istek numarasına yanıt verir ve belgenin içeriğini olduğu gibi bırakır. Bu, Apple’ın AppKit görünümlerini oluşturma ve güncelleme için açıkladığı temsil edilebilir yaşam döngüsünü izler. Temsil edilen görünümün yerleşimini SwiftUI de denetler; bu nedenle bu sarmalayıcı kaydırma görünümünün çerçevesini veya sınırlarını ayarlamaz. Apple’ın temsil edilebilir yaşam döngüsü ve yerleşim kılavuzu.
Bu örnekte her güncellemede textView.string = initialText atamaktan kaçının. Böyle yapmak kullanıcının geçerli belgesini başlangıçtaki örnek metinle değiştirir. Benzer şekilde, seçimi koşulsuz olarak belgenin sonuna ayarlamak, gösterme işleminin düzenleme konumunu taşımasına yol açar.
SwiftUI durumuyla desteklenen bir düzenleyici için bilinçli bir çift yönlü eşitleme ekleyin: kullanıcı düzenlemelerini koordinatör üzerinden iletin ve gelen model değişikliklerinin metni ne zaman değiştireceğini belirleyin. Bu değişiklikler boyunca geçerli seçimleri koruyun ve eşzamanlı değişikliklerin nasıl çözüleceğini tanımlayın. Bu, imleci görünür kılmaktan ayrı bir sorumluluktur; örnek, düzenlenebilir içeriğin tek bir sahibi olmasını kasıtlı olarak sağlar. Apple, temsilcilerin temsilci ve hedef-eylem etkileşimlerini SwiftUI’ye iletme mekanizması olduğunu belgeliyor. Koordinatör kılavuzu.
Odak denetimleri de önemlidir. Mevcut yanıtlayıcı odak bırakmayı reddederse makeFirstResponder(_:) başarısız olabilir. Apple ayrıca, istenen yanıtlayıcı odağı kabul etmezse başarılı bir dönüş değerinin pencereyi ilk yanıtlayıcı olarak bırakabileceğini belirtir. window.firstResponder === textView değerini denetlemek, kaydırmadan önce gerçek sonucu doğrular. İlk yanıtlayıcının dönüş değerleri ve davranışı.
Düzenleyici bir pencereye ekli değilse bu örnek isteği işleyip herhangi bir işlem yapmaz. Eklendikten sonra düğmeye yeniden basın. İlk odak özelliği için, ilgisiz güncellemeler sırasında odağı tekrar tekrar geri almak yerine eklenme durumunu dikkate alan açık bir istek tasarlayın.
İmleç çizimini değiştirmeden rengi yapılandırın
Örnek, hem metin hem de ekleme noktası için .textColor, düzenleyici arka planı içinse .textBackgroundColor atar. Bunlar metin ve arkasındaki alan için belgelenmiş anlamsal renklerdir. insertionPointColor, imleç rengini ayarlamak için belgelenmiş özelliktir. Uygulamanızın desteklediği her macOS sürümünde ve görünümde ortaya çıkan görünümü denetleyin. İmleç rengi, metin rengi, metin arka plan rengi.
Renk yapılandırması odağı istemez, belgeyi kaydırmaz veya yanıp sönmeyen bir görüntüleme modu seçmez. Eksik imleci incelerken bu sorumlulukları ayrı tutun. Örneğin rengi değiştirmek, kaydırma görünümünün görünür alanı dışında kalan bir konumu görünür kılmaz.
Örnek, imleç geometrisini hesaplamak için layoutManager özelliğine erişmekten de kaçınır. Apple, macOS 12 ve sonraki sürümlerde bu özelliğe bir metin görünümünde veya metin kapsayıcısında erişmenin, görünümü NSLayoutManager kullanan uyumluluk moduna geçirebileceğini belgeliyor. Yüksek düzeyli kaydırma API’sini çağırmak, seçimi görünür kılmak uğruna bu TextKit uyumluluk değişikliğine yol açmaktan kaçınır. TextKit uyumluluk davranışı.
Sürekli görünür bir imlecin sınırını anlayın
“Görünür tutmak” ile “hiç yanıp sönmemesini sağlamak” kastediliyorsa kaydırma komutu bu gereksinimi karşılamaz. Apple’ın güncel imleç belgeleri otomatik animasyonu sürekli görünür görüntüleme modundan ayrı ele alır.
macOS 14 ve sonraki sürümlerde NSTextInsertionIndicator, .automatic, .visible ve .hidden görüntüleme modlarını sağlar. Apple, animasyonları devre dışı bırakarak imleci sürekli göstermek için, örneğin sürükleme hedefini belirtirken, .visible modunu belgeliyor. Standart AppKit metin görünümleri sistem imleç arayüzünü otomatik olarak kullanır. Sistem metin imlecini kullanma kılavuzu, NSTextInsertionIndicator.
Belgelenen özel imleç yolu, kendi göstergenizi oluşturmanızı ve konumlandırmanızı gerektirir. Görüntüleme modu bu örneği denetler; Apple’ın kılavuzunda, NSTextView üzerinde dahili olarak yönetilen göstergenin .visible moduna geçirilmesini sağlayan bir ayar açıklanmaz. Özel yolu, imleç geometrisini ve etkin durumunu güncelleme sorumluluğu da dahil olmak üzere özel metin arayüzleri için mimari bir tercih olarak değerlendirin. Bu sarmalayıcıya doğrudan eklenebilecek bir değişiklik değildir.
Eski görünümlü iki API, eksik olan bu ayarı sağlamaz. shouldDrawInsertionPoint salt okunur olarak belgelenmiştir. drawInsertionPoint(in:color:turnedOn:) imleci çizer veya siler; Apple, çağıranların bunu doğrudan çağırmasına gerek olmaması gerektiğini belirtir. Bunların hiçbiri sürekli görünür olmayı sağlayan bir anahtar olarak belgelenmemiştir. Ekleme noktası çizim durumu, Ekleme noktası çizim yöntemi.
Bu makaledeki sıradan düzenleyici görevi için AppKit’in imleç animasyonunu koruyun ve geçerli konuma dönmenin güvenilir bir yolunu sunun. Özel imleç uygulaması kendi tasarımını ve NSTextInsertionIndicator kullanıldığında macOS 14 kullanılabilirlik sınırını gerektirir.
Uygulamanızdaki davranışı doğrulayın
Kaydırma, seçim, odak ve görünüm denetimlerini birbirinden ayırmak için örneği kullanın. Aşağıdakiler, bildirilen çalıştırma sonuçları değil, uygulamadan beklenen davranışlardır.
- Uygulamayı başlatıp
Ekleme noktasını gösterdüğmesine basın. Düzenleyici odağı almalı ve sondaki konumu görünür kılmalıdır. - İmleci taşımak için önceki bir satırın içine tıklayın. Başka bir metin konumuna tıklamadan kaydırın, ardından düğmeye basın. Önceki konumu görünür kılmalıdır.
- Birkaç sözcüğü seçip düğmeye basın. Seçim seçili kalmalıdır; komut seçimi tek bir imlece daraltmaz.
orange trianglegibi ayırt edici bir etiket yazıp düğmeye birkaç kez basın. Düzenlenen içerik olduğu gibi kalmalıdır.- Pencereyi yeniden boyutlandırıp gösterme işlemini tekrarlayın. Komut, eski bir ekran koordinatı yerine güncel aralığı kullanmaya devam etmelidir.
- Desteklenen macOS sürümlerinde hem açık hem koyu görünümü denetleyin. Metin, arka plan ve imleç renklerinin birlikte kullanılabilir olduğunu doğrulayın.
Kaydırma başarılı olduğu hâlde imleç aralıklı olarak görünmeye devam ediyorsa normal yanıp sönmeyi mi yoksa etkin bir metin seçimini mi gözlemlediğinizi denetleyin. Komut hiçbir şey yapmıyorsa önce pencereye eklenme ve ilk yanıtlayıcı denetimlerini inceleyin. Düzeltmeyi başarısız olan adıma bağlayın: metni koruyun, istendiğinde odağı geri verin ve geçerli aralığı görünür kılın.
Kaynaklar
- NSViewRepresentable — Apple
- NSTextView — Apple
- scrollableTextView() — Apple
- scrollRangeToVisible(_:) — Apple
- makeFirstResponder(_:) — Apple
- length — Apple
- String.UTF16View — Apple
- count — Apple
- insertionPointColor — Apple
- textColor — Apple
- textBackgroundColor — Apple
- Özel metin görünümlerinde sistem metin imlecini kullanma — Apple
- NSTextInsertionIndicator — Apple
- shouldDrawInsertionPoint — Apple
- drawInsertionPoint(in:color:turnedOn:) — Apple