Mac’te SwiftUI satırındaki boş alanı tıklanabilir yapmak için satır içeriğini kullanılabilir genişliğe genişletin ve dikdörtgen bir içerik şekli uygulayın. Eylem gerçekleştiren bir satır için her iki değiştiriciyi de bir Button etiketinin içine yerleştirin.
Bu öğreticide, satırlarında sentetik şekiller ve metin bulunan küçük bir macOS uygulaması oluşturuluyor. Bir şekle, etiketine veya yanındaki boş alana tıklandığında aynı satır eylemi çalışır. Satırın dışındaki alan bu eylemin hedefi olarak kalmaz.
Metin yanıt verirken boş alan neden yanıt vermiyor?
Düzen ve isabet testi farklı soruları yanıtlar. Bir çerçeve, görünümün kendisine önerilen alanı nasıl kapladığını belirler; içerik şekli ise isabet testi için kullanılan bölgeyi tanımlar. Apple bunları sırasıyla esnek çerçeve değiştiricisi ve içerik şekli değiştiricisi belgelerinde ayrı ayrı açıklar.
Örneğin, kapsayıcısı pencereyi doldurduğu için bir satır geniş görünebilir; ancak etkileşim yalnızca kısa bir metin görünümüne bağlı olabilir. Benzer şekilde, boş düzen alanı eklemek tüm satırın isabet testine katılması gerektiğini açıkça belirtmez. Uygulamadaki çözüm, önce satırın amaçlanan sınırlarını belirlemek, ardından bu bölgeye bir etkileşim şekli tanımlamaktır.
Etiketin, üst görünümü ek genişlik önerdiğinde genişlemesini sağlamak için .frame(maxWidth: .infinity, alignment: .leading) kullanın. Ardından bu genişletilmiş etikete .contentShape(.interaction, Rectangle()) uygulayın. Apple, interaction içerik şekli türünü özellikle isabet testi ve erişilebilirlik için tanımlar.
Bu, satırı sonsuz genişlikte yapmaz. Çerçeve hâlâ üst görünümün düzen önerisine katılır. Dar bir üst görünüm dar bir satır üretir; içerik şekli eksik düzen alanını sağlayamaz. Bu birleşim iki gereksinimi karşılar: yeterince geniş bir etiket ve açıkça tanımlanmış bir isabet testi bölgesi.
Tam bir satır düğmesi örneği oluşturun
Açıkça .interaction içerik şekli aşırı yüklemesi macOS 12’den itibaren kullanılabildiğinden, Xcode’da SwiftUI ve Swift kullanarak macOS 12 veya daha yeni bir dağıtım hedefiyle macOS uygulama projesi oluşturun. Oluşturulan uygulama bildirimini ve içerik görünümünü aşağıdaki tek dosyalı programla değiştirin; yalnızca bir @main uygulama bildirimi bırakın. Kullanılabilirlik bilgisi için Apple’ın içerik şekli aşırı yüklemesi belgelerine bakın.
Her satır bir eylem başlattığı için örnekte Button kullanılıyor. Apple, eylem gerçekleştiren arayüz öğeleri için düğmeleri önerir ve düğme etiketleri olarak rastgele görünümlerin kullanılmasını açıkça destekler. Apple’ın dokunma hareketi belgeleri de hareket tabanlı bir denetim işlevsel olarak bir düğmeyle eşdeğer olacaksa özelleştirilmiş bir düğme kullanılmasını önerir.
import SwiftUI
@main
struct ClickableRowsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct SampleItem: Identifiable {
let id: Int
let title: String
let color: Color
}
struct ContentView: View {
private let items = [
SampleItem(id: 1, title: "Blue square", color: .blue),
SampleItem(id: 2, title: "Orange square", color: .orange),
SampleItem(id: 3, title: "Purple square", color: .purple)
]
@State private var lastActivated = "None"
@State private var activationCount = 0
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Choose a shape")
.font(.headline)
ScrollView {
VStack(spacing: 8) {
ForEach(items) { item in
Button {
lastActivated = item.title
activationCount += 1
} label: {
HStack(spacing: 12) {
Rectangle()
.fill(item.color)
.frame(width: 24, height: 24)
Text(item.title)
}
.padding(.horizontal, 12)
.padding(.vertical, 10)
.frame(maxWidth: .infinity,
alignment: .leading)
.contentShape(.interaction, Rectangle())
}
.buttonStyle(.plain)
.background(Color.gray.opacity(0.12))
}
}
}
Text("Last activated: \(lastActivated)")
Text("Activations: \(activationCount)")
}
.padding(16)
.frame(minWidth: 360, minHeight: 280)
}
}
Tüm öğeler ve etkileşim durumu bellekte tutulur. Her öğenin sabit bir tamsayı kimliği vardır; bu, kararlı kimliği temsil eden Swift’in Identifiable protokolü ile uyumludur. Üst görünüm etkinleştirme durumunun sahibidir; bu, Apple’ın yerel State değerinin onu sahiplenmesi gereken görünümde özel tutulması yönündeki kılavuzunu izler.
Uygulamayı çalıştırın ve herhangi bir başlığın sağındaki boş alana tıklayın. Etkinleştirme sayısı bir kez artmalı ve durum satırın adını göstermelidir. Gri arka plan, bu kontrol sırasında amaçlanan satır alanını görünür kılar; açıkça belirtilen içerik şekli etkileşim bölgesini tanımlar.
Bu program, alınan API belgelerine göre incelendi ancak Swift araç zinciri burada bulunmadığından derlenmedi veya çalıştırılmadı. Entegrasyondan önce macOS hedefinizde işaretçi ve klavye davranışını doğrulayın.
Çerçeve, dolgu ve içerik şekli nereye yerleştirilmeli?
Bu örnekte sıra bilinçli olarak seçilmiştir: etiket içeriğini oluşturun, iç dolgusunu ekleyin, ortaya çıkan etiketi genişletin ve bu bölgenin üzerine etkileşim şeklini tanımlayın. Tüm bu işlemler düğmenin etiket kapanışında gerçekleşir. Apple, değiştirici sırasının önemli olduğunu belgeler.
Dolguyu bu şekillendirilmiş etiketin içine koymak, dolgulu bölgeyi amaçlanan hedefe dahil eder. Dolguyu düğmenin dışına koymak ise çevresinde düzen alanı oluşturur; bu alanın düğmenin etkileşimli etiketine ait olduğunu varsaymayın. Benzer şekilde, yalnızca dış kapsayıcıyı genişletmek, düğmenin içindeki içeriği genişletmekle aynı etiket geometrisini ifade etmez.
.buttonStyle(.plain) değiştiricisi örneğin görsel olarak sade kalmasını sağlar. Apple, düz düğme stilini boşta dekorasyonsuz olarak tanımlar; basılı, odaklanmış veya etkin durumlarda etkileri olabilir. Bu bir stil tercihidir; etiketin genişliğini ve içerik şeklini tanımlamanın yerini tutmaz.
macOS 12’den eski bir dağıtım hedefi için .contentShape(.interaction, Rectangle()) yerine .contentShape(Rectangle()) kullanın. Apple, eski aşırı yüklemeyi isabet testi şeklini tanımlayan bir seçenek olarak belgeler ve macOS 10.15’ten itibaren kullanılabildiğini belirtir. Bu değişiklik içerik şekli API gereksinimini değiştirir; uygulamanızın diğer API’lerini ayrıca kontrol edin.
Bir satırda düğme mi, liste seçimi mi, yoksa hareket mi kullanılmalı?
Etkileşimi, satıra tıklamanın ne anlama geldiğine göre seçin. Boş alanın yanıt vermesini sağlarken bu anlamı ve satırdaki denetimlerin sahipliğini koruyun.
| Kullanıcıya yönelik davranış | Uygun başlangıç noktası | Korunması gereken sınır |
|---|---|---|
| Satırın tamamı tek bir eylemi gerçekleştirir | Genişletilmiş ve şekillendirilmiş etiketli bir Button |
Satır için tek bir eylem sahibi tutun |
| Satır geçerli seçimi temsil eder | Seçim bağlaması olan bir List |
Seçim davranışını listenin yönetmesini sağlayın |
| Özel bir görünümün ayrı bir tıklama hareketine ihtiyacı vardır | onTapGesture içeren şekillendirilmiş bir görünüm |
Hareketi amaçlanan bölgeye bağlayın |
| Satırda birbirinden bağımsız birkaç denetim vardır | Ayrı kardeş denetimler | Her denetim için kendi hedefini belirleyin |
Liste seçimi için satır genelinde tıklama işleyicisi eklemeden önce kapsayıcının seçim modelini kullanın. Apple’ın List belgeleri, tekli seçim için isteğe bağlı bir öğe tanımlayıcısına, çoklu seçim içinse tanımlayıcı kümesine bağlamayı açıklar. Eylem gerçekleştiren bir düğme ile seçilen bir liste satırı farklı etkileşim gereksinimleridir.
Özel bir görünümde özellikle hareket tanımaya ihtiyacınız varsa hareket kullanmak uygundur. Hareketi onTapGesture ile bağlamadan önce aynı geometri sırasını uygulayın: dolgu ve çerçeve, ardından içerik şekli. Apple, count parametresini gerekli dokunma veya tıklama sayısı olarak belgeler. İçerik şekli hedef bölgeyi sağlar; tek başına bir eylem sağlamaz. Bkz. onTapGesture(count:perform:).
Satırda ayrıca ayrı bir düğme veya açma kapatma anahtarı varsa, bunu ana düğmenin etiketinin dışında tutun. Kullanışlı bir yapı, genişleyen bir ana düğme ile ayrı bir sondaki denetimi içeren yatay bir yığındır. Ana etiketin boş alanı kendi eylemine aittir; sondaki denetim kendi bölgesinin sahibidir. Etkileşimli denetimleri satırın tamamını kapsayan bir düğmenin içine yerleştirmekten kaçının.
Boş alanın hedeflenmesini düzeltmek için hareket önceliği değiştiricileri eklemeyin. Apple, highPriorityGesture değiştiricisini önceliği değiştiren, simultaneousGesture değiştiricisini ise hareketlerin birlikte işlenmesine olanak tanıyan bir seçenek olarak belgeler. Bu işlemler hareket ilişkilerini değiştirir ve alt öğelerin etkileşimlerini etkileyebilir; eksik satır geometrisini oluşturmaz.
Tıklamaları hâlâ algılamayan bir satırı teşhis edin
Satırınızın en küçük sürümünde aşağıdaki sırayı izleyin. Bu adımlar boyutlandırma sorununu hedef şekli sorunundan ve üst üste binen görünüm sorunundan ayırır.
- Etiketin gerçek sınırlarını kontrol edin. Etiketin çerçeve değiştiricisinden hemen sonra geçici olarak
.border(Color.orange)ekleyin. Kenarlık metnin yanında bitiyorsa üst görünümün genişlik önerisini ve sabit genişlik kısıtlamalarını inceleyin. - Değiştiricilerin yerleşimini kontrol edin. İçerik şeklinin genişletilmiş etiketi ve tıklanabilir olmasını istediğiniz dolgu dahil tüm bölgeyi kapsadığını doğrulayın.
- Etkileşim sahibini kontrol edin. Düğme satırında düğmenin eylemini kullanın. Sorunu incelerken gereksiz satır dokunma hareketlerini kaldırın.
- Üst üste binen dekorasyonu kontrol edin. Satırın üstündeki katmanları ve kardeş görünümleri inceleyin. Girdi almaması gereken dekorasyon için yalnızca o dekorasyonda
.allowsHitTesting(false)kullanmayı değerlendirin. - Diğer denetimleri tek tek geri ekleyin. Her denetimi geri ekledikten sonra kendi bölgesini, ardından kalan boş alanı test edin.
Apple, allowsHitTesting(_:) seçeneğini bir görünümün isabet testi işlemlerine katılıp katılmayacağını denetleyen bir özellik olarak belgeler. Bunu satırın tamamına uygulamak satırın katılımını kaldırır; amaçlanan davranış buysa yalnızca ilgili dekoratif görünümü hedefleyin.
Sonucu doğrulamak için birkaç farklı noktaya tıklayın. Tablo, ölçülmüş test sonuçlarını değil, sağlanan program için beklenen davranışı açıklar.
| Tıklama konumu | Beklenen sonuç |
|---|---|
| Renkli kare veya satır başlığı | O satırı bir kez etkinleştirin |
| Gri satırın içindeki sağ boş alan | O satırı bir kez etkinleştirin |
| Başlığın üstündeki veya altındaki iç dolgu | O satırı bir kez etkinleştirin |
| Satırlar arasındaki boşluk | Satır etkinleştirilmez |
| Dış dolgu veya satırların altındaki boş alan | Satır etkinleştirilmez |
Pencereyi yeniden boyutlandırın ve boş alanı tekrar kontrol edin. Ayrıca Mac’in klavye gezinme ayarlarıyla klavye odağını ve etkinleştirmeyi doğrulayın. Bu kontroller, son bileşenin yalnızca işaretçiye yanıt verip vermediğinin ötesinde, amaçlanan denetim gibi davranıp davranmadığını belirlemeye yardımcı olur.
AppKit isabet testi ne zaman önem kazanır?
Sıradan bir SwiftUI satırı için SwiftUI’nin düzen, içerik şekli ve denetim API’leriyle başlayın. Uygulamanız özel yerel görünümler veya olay hedeflemesini değiştiren gömülü bir görünüm içeriyorsa AppKit önem kazanır.
Apple, NSView.hitTest(_:) yöntemini bir noktayı içeren en derindeki alt görünümü bulan bir yöntem olarak belgeler. Pencere, fare aşağı olayını hangi görünümün alacağını belirlemek için öncelikle bu yöntemi kullanır. Girdi noktası, görünümün üst görünümünün koordinat sisteminde ifade edilir ve Apple doğrudan çağrılmasının nadiren gerekli olduğunu söyler.
Özel bir AppKit görünümünü yönetiyorsanız, sınırları ve isabet testi geçersiz kılmasını bu noktada inceleyin. SwiftUI içerik şekli, ayrı bir yerel görünümün olay sahipliğini atlama talimatı değildir. Düzeltmeyi tıklamayı almaktan sorumlu görünüm hiyerarşisi içinde yapın.
Bu öğreticideki satır için desteklenen yol basittir: düğme etiketinin amaçlanan satır genişliğini kaplamasını sağlayın, dikdörtgen etkileşim şeklini tanımlayın ve diğer denetimleri kendi bölgelerinde tutun. Böylece yararlı boş alan tıklanabilir olurken pencerenin geri kalanındaki etkileşimler korunur.
Kaynaklar
- contentShape(_:eoFill:) — Apple
- contentShape(::eoFill:) — Apple
- interaction — Apple
- frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:) — Apple
- Button — Apple
- plain — Apple
- padding(::) — Apple
- Identifiable — Apple
- State — Apple
- List — Apple
- onTapGesture(count:perform:) — Apple
- highPriorityGesture(_:including:) — Apple
- simultaneousGesture(_:including:) — Apple
- allowsHitTesting(_:) — Apple
- hitTest(_:) — Apple