Mac’teki bir öğe listesi için seçimi SwiftUI List’e bağlayın ve seçime duyarlı bağlam menüsü üzerinden bir birincil eylem sağlayın. Tek tıklama satırı seçer; çift tıklama açar. Seçim durumunu sunum durumundan ayrı tutun.

Bu eğitim, üç sentetik ve bellekte tutulan öğe kullanarak bu etkileşimi kendi macOS uygulamanızda uygular. Ayrıca özel SwiftUI yüzeyleri ve AppKit görünümleri için sınırları açıklayarak, aynı tıklama üzerinde birden fazla katmanın rekabet etmesine yol açmadan bir yaklaşım seçmenize yardımcı olur.

Seçilebilir kapsayıcının birincil eylemini kullanın

Apple, macOS’te seçilebilir kapsayıcılar için tek tıklamayla seçimi ve çift tıklamayla birincil eylemleri açıkça belgeliyor. Desteklenen SwiftUI giriş noktası contextMenu(forSelectionType:menu:primaryAction:) olup macOS 13’ten itibaren kullanılabilir. Bunu List veya Table gibi seçimi destekleyen bir kapsayıcıya ekleyin. Apple’ın seçime duyarlı bağlam menüsü belgeleri.

Birbirinden ayrı üç sorumluluk vardır: model her öğeyi tanımlar, liste bir bağlama aracılığıyla seçimi yönetir ve birincil eylem işleyiciniz açma işleminin ne anlama geldiğine karar verir. Bu örnekte açma işlemi bir öğe ayrıntıları sayfası gösterir. Bir öğeyi seçmek yalnızca vurgulanan satırı ve seçim etiketini değiştirir.

Uygulamanızı etkileşimin sahibi olan görünüme göre seçin:

Arayüzünüz Uygun başlangıç noktası Üstlenmeniz gereken sorumluluk
macOS 13 veya sonraki sürümlerde SwiftUI öğe listesi ya da tablosu Seçim bağlaması ve seçime duyarlı birincil eylem Eylem kimliklerini çözümlemek ve ilgili içeriği sunmak
Özel SwiftUI kutucuğu veya şekli Açıkça birleştirilmiş hareketler Seçim görünümü, etkinleştirme ilkesi ve diğer giriş yolları
SwiftUI içine gömülü AppKit tablosu NSTableView seçimi ve doubleAction Seçim ve etkinleştirmeyi SwiftUI durumuna aktarmak
Özel AppKit çizim yüzeyi Kendi fare olayı işleme mekanizması İsabet testi, öğe kimliği ve tıklama dizisi ilkesi

İlk yol burada istenen görevi doğrudan karşılar. Özel yollar, arayüzün gerçekten farklı bir yüzeye ihtiyaç duyduğu durumlarda kullanışlıdır; ancak daha fazla etkileşim davranışını sizin uygulamanızı gerektirir. Apple, temel alternatifleri hareket birleştirme, AppKit tablo çift tıklama eylemleri ve mouse-down işleme belgelerinde açıklıyor.

Tek seçimli eksiksiz bir örnek oluşturun

Xcode’da SwiftUI arayüzü olan bir macOS uygulama projesi oluşturun ve dağıtım hedefini macOS 13 veya sonraki bir sürüm olarak ayarlayın. Oluşturulan uygulama ve içerik görünümü bildirimlerini aşağıdaki tek dosyayla değiştirin. Hedefte yalnızca bir @main uygulama bildirimi bulundurun.

Program sabit tamsayı kimlikleri, üç renkli daire ve yerel durum kullanır. Hiçbir dosyayı okumaz ve harici kaynak açmaz. Araç çubuğu tarzındaki Open düğmesi ve bağlamsal Open komutu, çift tıklamayla etkinleştirmeyle aynı işlevi çağırır.

import SwiftUI

struct SampleItem: Identifiable {
    let id: Int
    let name: String
    let tint: Color
}

@main
struct ClickSelectionDemoApp: App {
    var body: some Scene {
        WindowGroup {
            ItemBrowser()
        }
    }
}

struct ItemBrowser: View {
    private let items = [
        SampleItem(id: 1, name: "Circle A", tint: .blue),
        SampleItem(id: 2, name: "Circle B", tint: .orange),
        SampleItem(id: 3, name: "Circle C", tint: .purple)
    ]

    @State private var selectedID: SampleItem.ID? = nil
    @State private var openedItem: SampleItem? = nil
    @State private var message = "Select an item."

    private var selectedItem: SampleItem? {
        items.first { $0.id == selectedID }
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            HStack {
                Text(selectedItem.map {
                    "Selected: \($0.name)"
                } ?? "No selection")
                Spacer()
                Button("Open") {
                    if let id = selectedID {
                        openItems([id])
                    }
                }
                .disabled(selectedItem == nil)
            }

            List(items, selection: $selectedID) { item in
                HStack(spacing: 10) {
                    Circle()
                        .fill(item.tint)
                        .frame(width: 18, height: 18)
                    Text(item.name)
                }
                .padding(.vertical, 4)
            }
            .contextMenu(forSelectionType: SampleItem.ID.self) { ids in
                if ids.count == 1 {
                    Button("Open") {
                        openItems(ids)
                    }
                }
            } primaryAction: { ids in
                openItems(ids)
            }

            Text(message)
                .font(.caption)
        }
        .padding()
        .frame(minWidth: 360, minHeight: 280)
        .sheet(item: $openedItem) { item in
            VStack(spacing: 16) {
                Circle()
                    .fill(item.tint)
                    .frame(width: 80, height: 80)
                Text(item.name)
                    .font(.title2)
                Button("Close") {
                    openedItem = nil
                }
            }
            .padding(32)
            .frame(minWidth: 260, minHeight: 220)
        }
    }

    private func openItems(_ ids: Set<SampleItem.ID>) {
        guard ids.count == 1, let id = ids.first else {
            message = "Choose exactly one item to open."
            return
        }
        guard let item = items.first(where: { $0.id == id }) else {
            message = "That item is no longer available."
            return
        }
        openedItem = item
        message = "Opened: \(item.name)"
    }
}

Bildirimler ve başvurular incelendi, ancak bu örnek burada derlenmedi veya çalıştırılmadı: kullanılabilir ortamda Swift araç zinciri ya da macOS grafik arayüzü yok. Xcode’da derleyin ve aşağıdaki etkileşim kontrollerini yapın.

Kimliği, seçimi ve açmayı birbirinden ayrı tutun

SampleItem, Swift’in Identifiable protokolüne uyar. Sabit id, görünüm güncellemeleri boyunca aynı öğeyi temsil eder. Apple bu protokolü kararlı kimlik etrafında tanımlar; görüntü adı ayrı bir özelliktir. Daha büyük bir uygulamada, öğenin adı veya görünümü değiştiğinde kimliğini koruyun. Apple’ın Identifiable belgeleri.

İsteğe bağlı selectedID tek seçimli bir liste oluşturur. Bunun yerine bir kimlik kümesi kullanmak çoklu seçimi etkinleştirir. SwiftUI’nin liste belgeleri bu iki bağlama biçimini ve kapsayıcının sağladığı seçim görünümünü açıklar. Apple’ın List belgeleri.

openedItem sunumu bağımsız olarak yönetir. Bunu bir öğeye ayarlamak sayfayı gösterir; nil olarak ayarlamak sayfayı kapatır. Bu, öğe güdümlü sayfa API’sinin belgelenmiş kullanımını izler. Bir satırı seçmek hiçbir zaman openedItem değerine atama yapmadığından, bu uygulamada seçim tek başına sayfayı açamaz. Apple’ın öğe güdümlü sayfa belgeleri.

Eyleme iletilen kimlikleri çözümleyin

Bağlam menüsü API’si kapsayıcının tanımlayıcı türünü alır ve eylemi bir kimlik kümesi alır. Bu nedenle örnekte, liste seçimiyle eşleşen SampleItem.ID.self kullanılır. Ayrı olarak okunan bir seçim değerini kullanmak yerine geri çağrı bağımsız değişkenini çözümler. Apple’ın bağlam menüsü parametresi belgeleri.

İşleyici özellikle tam olarak bir kimlik kabul eder. Boş bir küme bir ileti, çözümlenemeyen bir kimlik ise başka bir ileti üretir. Daha sonra çoklu seçimi etkinleştirirseniz, bu denetimi değiştirmeden önce açma ilkesini tanımlayın: bir grup ayrıntı görünümü sunun, ayrı pencereler açın veya tek seçim şartı koyun. Kümenin rastgele bir ilk üyesini almak, tanımlanmamış bir ilkeyi gizler.

İki dokunma işleyicisi neden farklı bir tasarım kararı gerektirir?

Özel bir kutucuk kendi hareketlerine ihtiyaç duyabilir. SwiftUI’nin onTapGesture(count:perform:) öğesi, gerekli tıklama veya dokunma sayısını kabul eder. Ancak belgeleri, bir görünüme iki bağımsız değiştirici eklemenin tek ve çift tıklama için birbirini dışlayan bir sözleşme oluşturduğunu belirtmez. Apple’ın dokunma eylemi belgeleri.

Önce çift tıklama sırasında seçimin ne anlama geldiğine karar verin. Sıradan bir öğe tarayıcısı için öğeyi açmadan önce seçmek kullanışlıdır ve kapsayıcı tabanlı çözümle tutarlıdır. Tek tıklama eyleminin yalnızca dizi hiçbir zaman çift tıklamaya dönüşmezse gerçekleştirilmesi gibi ayrı bir gereksinim, eşgüdüm gerektirir.

Özel bir yüzey için SwiftUI, exclusively(before:) sağlar: öncelik ilkine verilerek bileşen hareketlerinden yalnızca biri başarıya ulaşır. Aşağıdaki, liste örneğinin eksiksiz bir alternatifi değil, bir hareket ifadesi parçasıdır: Apple’ın birbirini dışlayan hareket belgeleri.

TapGesture(count: 2)
    .exclusively(before: TapGesture(count: 1))

Bu, çift tıklama tanımaya öncelik verir. Birleştirilmiş hareketi özel öğeye ekleyin ve sonucunu seçme ve açma ilkenize göre işleyin. TapGesture, tanımanın gerekli dokunma sayısına göre yapıldığını belgeler. Apple’ın TapGesture belgeleri.

Bu ilkenin zamanlamayla ilgili bir sonucu vardır: bir dizinin yalnızca tek tıklama olduğuna karar vermek, çift tıklama olmadığından emin olmayı gerektirir. Bu, etkileşim gereksiniminin bir sonucudur; SwiftUI için belgelenmiş sabit bir gecikme değildir. Tek tıklamanın hemen tamamlanacağını vaat ederken aynı zamanda çift tıklama dizisi sırasında gerçekleşmeyeceğini şart koşmayın.

Özel bir kutucukta tıklanabilir alanı da tanımlayın. contentShape(Rectangle()), dolgulu dikdörtgen bir alanın isabet testine katılmasını sağlayabilir; hareket önceliğini belirlemez. Kaçırılan tıklamaları incelerken bu iki konuyu ayrı tutun. Apple’ın içerik şekli belgeleri.

AppKit devredeyken olay sahipliğini koruyun

Satırların sahibi mevcut bir AppKit tablosuysa, o tablonun desteklenen etkinleştirme mekanizmasını kullanın. NSTableView.doubleAction, çift tıklamada hedefindeki bir seçiciye ileti gönderir. Apple ayrıca etkinleştirmenin nerede gerçekleştiğini, sütun başlıkları da dahil olmak üzere belirlemek için clickedRow ve clickedColumn öğelerini belgeliyor. Açmadan önce konumun bir öğeyi tanımladığını doğrulayın. Apple’ın doubleAction belgeleri.

Özel bir AppKit yüzeyi için mouseDown(with:), sol düğme basma olayını alır ve NSEvent.clickCount, mouse-down veya mouse-up olaylarıyla ilişkilendirilen tıklama sayısını bildirir. Bu özelliği ilgisiz bir olay üzerinde okumak belgelenmiş sözleşmenin dışındadır ve istisna oluşturabilir. Bu tür yorumlamayı uygun fare olayını işleyen görünümün içinde tutun. Apple’ın mouse-down belgeleri, Apple’ın tıklama sayısı belgeleri.

Sahip olduğunuz bu AppKit görünümünü SwiftUI içine gömerken NSViewRepresentable kullanın. Görünümü makeNSView içinde oluşturun, güncel yapılandırmayı updateNSView içinde eşitleyin ve hedef-eylem veya temsilci iletişiminin bir köprüye ihtiyaç duyduğu durumlarda bir koordinatör kullanın. Apple, oluşturma, güncelleme, eşgüdüm ve kaldırma işlemlerini bu tümleştirmenin parçaları olarak belgeliyor. Apple’ın NSViewRepresentable belgeleri.

Fare olaylarını alan bir kaplama, tıklamayı hangi görünümün işlediğini değiştirebilir. AppKit, mouse-down olaylarının alıcısını belirlemek için isabet testi kullanır; bu nedenle olayları yakalayan bir kaplama görünmez bir gözlemci olarak değerlendirilmemelidir. Bu görevde tanımayı, seçilebilir kapsayıcıda veya öğe etkileşiminin asıl sahibi olan özel yüzeyde tutun. Apple’ın isabet testi belgeleri.

Seçimi ve açmayı ayrı sonuçlar olarak doğrulayın

Örneği derledikten sonra şu adımları izleyin:

  1. Circle A öğesine bir kez tıklayın. Satırı seçili duruma gelmeli, seçim etiketi öğenin adını göstermeli ve hiçbir sayfa açılmamalıdır.
  2. Circle B öğesine çift tıklayın. Ayrıntı sayfasında Circle B göründüğünü doğrulayın.
  3. Sayfayı kapatın ve Circle C öğesine bir kez tıklayın. Başka bir sayfa açılmadan seçimin değiştiğini doğrulayın.
  4. Görünür Open düğmesini etkinleştirin. Aynı işleyici üzerinden seçili öğeyi açtığını doğrulayın.
  5. Bir satırın bağlamsal Open komutunu çağırın ve açtığı öğeyi doğrulayın.
  6. Kullanılmayan liste alanına tıklayın ve çift tıklayın. İlgisiz bir öğenin açılmadığını doğrulayın.
  7. Gömülü denetimler, sürükleme veya kaplamalar eklerseniz kontrolleri tam olarak bu bölgelerde tekrarlayın.

Seçim beklenmedik biçimde ayrıntıları açıyorsa sunum durumunun nerede atandığını inceleyin. Etkinleştirme yanlış öğeyi açıyorsa geri çağrı kimliklerini ve model aramasını inceleyin. Özel bir kutucuğun bir kısmında tıklamalar algılanmıyorsa hareket önceliğini değiştirmeden önce düzenini ve isabet testi şeklini inceleyin. Bu kontroller, üç alanı birden değiştirmek yerine durumu, eylem hedefini ve olay iletimini ayrı ayrı inceler.

Alışılmış bir Mac öğe listesi için uygulamayı seçim bağlaması ve kapsayıcının birincil eylemi etrafında kurun. Özel tanımayı yalnızca yüzey veya etkileşim gerektiriyorsa ekleyin ve her açma yolunun amaçlanan öğeyi açık bir işleyici üzerinden çözümlemesini sağlayın.

Kaynaklar