Lampirkan seretan induk dengan simultaneousGesture(_:including:) supaya ia boleh berfungsi bersama interaksi anak. Kekalkan tindakan klik dalam butang anak, tetapkan ambang pergerakan untuk seretan, dan tentukan secara jelas sama ada pemilihan boleh berlaku serentak dengan seretan.
Panduan ini membina panel boleh alih yang mengandungi tiga butang item sintetik. Tugas pembaca adalah khusus: klik item untuk memilihnya, manakala seret panel untuk mengubah kedudukannya dalam aplikasi Mac yang sama. Contoh ini membolehkan pemilihan dan pergerakan berlaku serentak; variasi pemegang berasingan menyediakan kawasan interaksi yang berbeza apabila itu lebih sesuai.
Tentukan kontrak interaksi sebelum mengubah keutamaan gerak isyarat
Apple mendokumentasikan simultaneousGesture(_:including:) sebagai penambahan gerak isyarat pada keutamaan yang sama dengan gerak isyarat sedia ada. Contohnya menunjukkan pengendali ketikan anak dan pengendali ketikan bekas yang mengelilinginya kedua-duanya bertindak balas terhadap satu klik. Inilah mekanisme penggabungan yang sesuai apabila induk perlu turut serta tanpa sengaja mengutamakan dirinya berbanding anak. Dokumentasi gerak isyarat serentak Apple
Walau bagaimanapun, pengecaman serentak tidak dengan sendirinya menyatakan peraturan “seretan mesti membatalkan setiap tindakan anak.” Contoh Apple juga tidak membuktikan bahawa setiap gaya butang dan setiap bekas mengurus butang dan seretan dengan cara yang sama. Anggap penyertaan gerak isyarat dan tingkah laku aplikasi sebagai keputusan yang berasingan.
Bagi panel ini, induk mengawal ofset panel dan item yang dipilih. Setiap anak mengawal butang yang meminta pemilihan melalui penutupan. Permintaan pemilihan tetap diterima walaupun interaksi penuding yang sama menggerakkan panel. Jika tingkah laku yang diperlukan melarang pengaktifan semasa pergerakan, gunakan variasi pemegang berasingan di bawah dan bukannya bergantung pada susunan panggilan balik yang tidak didokumenkan.
Gunakan Button untuk tindakan anak. Apple secara jelas mengesyorkan butang apabila interaksi memerlukan tingkah laku butang, termasuk tingkah laku kebolehcapaian standard. Bentuk tersuai boleh kekal sebagai label butang; ia tidak perlu dijadikan gerak isyarat ketikan tanpa kawalan. Dokumentasi Gestures Apple
Bina panel boleh alih lengkap dengan butang anak
Cipta aplikasi SwiftUI macOS dalam Xcode, tetapkan sasaran penggunaan kepada macOS 14 atau lebih baharu, dan gantikan paparan aplikasi serta kandungan yang dijana dengan satu fail berikut. Pastikan hanya ada satu titik kemasukan aplikasi dalam sasaran. Keperluan macOS 14 ini meliputi pengubah suai coordinateSpace(_:) moden yang digunakan di sini. Dokumentasi ruang koordinat Apple
Semua item dan keadaan wujud dalam ingatan. Contoh ini menggerakkan seluruh panel, termasuk butangnya, dalam pentas tetap. Ia tidak melaksanakan pemindahan item atau menggerakkan tetingkap aplikasi.
import SwiftUI
@main
struct GesturePanelApp: App {
var body: some Scene {
WindowGroup {
PanelDemo()
}
}
}
private struct DemoItem: Identifiable {
let id: Int
let title: String
let color: Color
}
private struct ItemTarget: View {
let item: DemoItem
let isSelected: Bool
let select: () -> Void
var body: some View {
Button(action: select) {
HStack(spacing: 10) {
Circle()
.fill(item.color)
.frame(width: 18, height: 18)
Text(item.title)
Spacer()
if isSelected {
Text("Selected")
.font(.caption)
}
}
.padding(12)
.frame(width: 220)
.background(
isSelected
? Color.accentColor.opacity(0.18)
: Color.gray.opacity(0.12)
)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
}
private struct PanelDemo: View {
private let items = [
DemoItem(id: 1, title: "Item A", color: .blue),
DemoItem(id: 2, title: "Item B", color: .orange),
DemoItem(id: 3, title: "Item C", color: .purple)
]
@State private var selectedID: Int? = nil
@State private var committedOffset: CGSize = .zero
@GestureState private var liveTranslation: CGSize = .zero
private var pan: some Gesture {
DragGesture(
minimumDistance: 8,
coordinateSpace: .named("stage")
)
.updating($liveTranslation) { value, translation, _ in
translation = value.translation
}
.onEnded { value in
committedOffset = CGSize(
width: committedOffset.width + value.translation.width,
height: committedOffset.height + value.translation.height
)
}
}
var body: some View {
VStack(spacing: 12) {
Text("Click an item or drag the panel")
ZStack {
Color.gray.opacity(0.06)
VStack(spacing: 10) {
Text("Drag panel")
.font(.headline)
.frame(width: 244, height: 32)
.contentShape(Rectangle())
ForEach(items) { item in
ItemTarget(
item: item,
isSelected: selectedID == item.id,
select: { selectedID = item.id }
)
}
}
.padding(16)
.background(Color.gray.opacity(0.15))
.contentShape(Rectangle())
.simultaneousGesture(pan, including: .all)
.offset(
x: committedOffset.width + liveTranslation.width,
y: committedOffset.height + liveTranslation.height
)
}
.frame(width: 640, height: 420)
.coordinateSpace(.named("stage"))
Button("Reset panel") {
committedOffset = .zero
}
}
.padding(20)
}
}
Program ini telah disemak berdasarkan dokumentasi API yang diperoleh, tetapi belum dikompil atau diuji secara interaktif di sini: persekitaran ini tidak mempunyai pengkompil Swift atau masa jalan UI macOS. Bina dalam Xcode dan lakukan pemeriksaan di bawah pada versi macOS yang anda sokong.
Mengapa seretan mempunyai ambang bukan sifar
DragGesture menyediakan jarak pergerakan minimum sebelum gerak isyarat berjaya. Nilai ilustrasi 8 memberi toleransi pergerakan tertentu kepada klik biasa sebelum induk mula menyeret. Ini ialah pilihan aplikasi, bukannya ambang yang ditetapkan Apple atau dasar lengkap untuk membezakan klik dengan seretan. Seretan dengan jarak sifar menghapuskan perbezaan khusus itu. Dokumentasi DragGesture Apple
Mengapa pergerakan sementara dan pergerakan yang dimuktamadkan diasingkan
Semasa pengecaman, updating(_:body:) menulis terjemahan semasa ke dalam liveTranslation. Apple mendokumentasikan bahawa keadaan gerak isyarat ini ditetapkan semula apabila gerak isyarat berakhir atau dibatalkan. Contoh ini memuktamadkan ofset hanya dalam onEnded, jadi pembatalan membuang pratonton yang belum dimuktamadkan dan bukannya meninggalkan terjemahan sementara yang lapuk. Dokumentasi updating Apple
Kedudukan yang dipaparkan ialah ofset yang dimuktamadkan ditambah terjemahan semasa. Apple mentakrifkan translation sebagai sesaran keseluruhan dari permulaan seretan, jadi menambahnya berulang kali pada ofset yang dimuktamadkan pada setiap perubahan akan mengira pergerakan secara berlebihan. Sebagai ilustrasi, ofset mendatar yang dimuktamadkan sebanyak 40 dan terjemahan semasa sebanyak 12 menghasilkan ofset paparan 52; apabila seretan selesai, 52 dimuktamadkan sekali. Dokumentasi translation Apple
Ruang koordinat bernama itu milik pentas tetap yang mengelilingi panel bergerak. Ini menjadikan pengiraan pergerakan merujuk kepada kawasan rujukan yang jelas. Butang anak tidak menyimpan salinan kedudukan panel masing-masing.
Mengapa pemilihan kekal pada induk
Induk menyimpan selectedID dan membekalkan status pemilihan semasa serta penutupan pemilihan kepada setiap anak. Ini selaras dengan panduan Apple supaya keadaan disimpan pada paparan tertinggi yang memerlukannya, kemudian nilai atau ikatan disalurkan ke bawah hierarki. Dokumentasi State Apple
Penutupan itu menangkap item semasa dan meminta kemas kini kepada keadaan induk. Swift menyokong penutupan yang menangkap nilai daripada konteks sekelilingnya; tiada peristiwa AppKit perlu disalin atau diteruskan untuk melaksanakan panggilan balik itu. The Swift Programming Language: Closures
Fahami kawasan sasaran dan topeng gerak isyarat
contentShape(Rectangle()) mentakrifkan bentuk untuk pengesanan sasaran. Dalam label butang, ia mengikuti ruang dalaman dan bingkai supaya kawasan label segi empat tepat yang dimaksudkan dinyatakan dengan jelas. Pada induk, ia mentakrifkan bentuk interaksi panel. Ia tidak menetapkan keutamaan pengecam atau mewajibkan dua gerak isyarat bekerjasama. Dokumentasi bentuk kandungan Apple
Topeng yang dibekalkan kepada pengubah suai gerak isyarat menentukan gerak isyarat yang kekal diaktifkan. Pilihan ini mudah disalah tafsir: subviews bermaksud gerak isyarat induk yang ditambah itu dilumpuhkan, bukannya secara automatik bekerjasama dengan paparan keturunan. Dokumentasi GestureMask Apple
| Topeng | Kesan yang didokumenkan | Kesesuaian di sini |
|---|---|---|
.all |
Mengaktifkan gerak isyarat yang ditambah dan gerak isyarat lain pada paparan serta subpaparannya | Titik permulaan untuk penyertaan bersama |
.gesture |
Mengaktifkan gerak isyarat yang ditambah dan melumpuhkan gerak isyarat dalam hierarki subpaparan | Bercanggah dengan pengekalan gerak isyarat anak |
.subviews |
Mengaktifkan gerak isyarat subpaparan dan melumpuhkan gerak isyarat yang ditambah | Melumpuhkan seretan induk |
.none |
Melumpuhkan gerak isyarat dalam hierarki subpaparan, termasuk gerak isyarat yang ditambah | Melumpuhkan interaksi yang dimaksudkan |
Elakkan daripada menganggap highPriorityGesture sebagai penyelesaian umum bagi panggilan balik seretan induk yang tiada. Tujuan yang didokumenkan ialah memberikan keutamaan lebih tinggi kepada gerak isyarat yang dilampirkan. Contoh Apple menunjukkan induk mengutamakan dirinya berbanding ketikan anak. Pilihnya apabila keutamaan itu memang tingkah laku yang dimaksudkan. Dokumentasi gerak isyarat keutamaan tinggi Apple
Gunakan pemegang seretan berasingan apabila pengaktifan perlu dibezakan
Bagi panel yang butangnya tidak patut berkongsi kawasan seretan, kekalkan keadaan dan pengiraan pergerakan yang sama tetapi lampirkan pan pada pengepala sahaja. Buang .simultaneousGesture(pan, including: .all) daripada panel induk.
Gantikan ungkapan pengepala sedia ada dengan fragmen ini dalam program lengkap di atas. Fragmen ini menggunakan sifat pan yang telah ditakrifkan; tiada prasyarat lain berubah.
Text("Drag panel")
.font(.headline)
.frame(width: 244, height: 32)
.contentShape(Rectangle())
.gesture(pan)
Kini pengepala dan butang item menempati kawasan yang berbeza, dan butang tidak lagi mempunyai seretan panel yang dilampirkan pada induk. Interaksi yang bermula pada item ialah interaksi butang; interaksi yang bermula pada pengepala ialah cara untuk menggerakkan panel. Reka bentuk ini berguna apabila penyertaan bersama boleh menjadikan hasilnya kabur.
Kekalkan butang natif apabila anak memulakan tindakan. Apple menerangkan Button sebagai tindakan bersama label dan membenarkan paparan label tersuai, seperti item berwarna sintetik yang digunakan di sini. Menggantikannya dengan pengendali penuding yang dibina sendiri akan memerlukan anda membina semula tingkah laku yang telah disediakan oleh kawalan itu. Dokumentasi Button Apple
Kekalkan pemilikan peristiwa AppKit di sempadan rangka kerja
Jika panel mengandungi paparan AppKit, diagnosis pengesanan sasarannya secara berasingan daripada penggabungan gerak isyarat SwiftUI. hitTest(_:) AppKit memulangkan keturunan sepadan yang paling dalam dan membantu tetingkap menentukan penerima peristiwa tekan tetikus. Titik inputnya dinyatakan dalam koordinat superpaparan paparan itu. Dokumentasi pengesanan sasaran AppKit Apple
Memulangkan induk daripada pengesanan sasaran mengubah sasaran awal; ia tidak sama dengan membenarkan anak dan induk mengecam secara serentak. Begitu juga, memulangkan nil daripada pengesanan sasaran tindihan mengeluarkan tindihan itu daripada pertimbangan—ia tidak menjadikan tindihan sebagai pemerhati pasif bagi keseluruhan interaksi anak.
AppKit mendokumentasikan bahawa pelaksanaan lalai mouseDown(with:) menghantar mesej kepada responden seterusnya. Itu ialah tingkah laku rantaian responden, bukannya janji bahawa setiap induk menerima salinan peristiwa secara berasingan selepas kawalan mengendalikannya. Hubungkan pengaktifan kawalan dan pergerakan panel melalui panggilan balik atau pengecam yang anda miliki secara jelas. Dokumentasi mouse-down Apple
Apabila anda memiliki dua pengecam gerak isyarat AppKit dan memerlukan pengecaman serentak, AppKit menyediakan gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:). Apple mendokumentasikan bahawa memulangkan true membenarkan pasangan itu mengecam bersama; memulangkan false tidak menjamin pengecualian jika wakil pengecam yang satu lagi membenarkannya. Hadkan dasar itu kepada pasangan pengecam yang dimaksudkan. Dokumentasi wakil pengecam Apple
Sahkan interaksi dengan matriks tingkah laku ringkas
Uji gaya butang, hierarki dan keluaran macOS yang tepat yang disokong oleh aplikasi anda. Pemeriksaan berikut memisahkan masalah pengecaman daripada aritmetik pergerakan dan menjelaskan sama ada permukaan bersama sepadan dengan kontrak yang dimaksudkan.
| Tindakan | Perkara yang perlu diperiksa |
|---|---|
| Klik setiap item tanpa menyeret | Keadaan pilihannya berubah; panel kekal di tempatnya |
| Seret pengepala melepasi ambang | Panel mengikuti penuding dan mengekalkan kedudukan akhirnya |
| Seret dari item dalam versi bersama | Pergerakan induk berfungsi; tentukan sama ada pengaktifan butang turut berlaku dan pastikan hal itu boleh diterima |
| Lakukan dua seretan berturut-turut | Seretan kedua bermula dari kedudukan pertama yang dimuktamadkan tanpa mengumpulkan setiap terjemahan perantaraan |
| Ganggu seretan yang sedang aktif | Terjemahan sementara dikosongkan; tiada pergerakan yang belum selesai dimuktamadkan |
| Cuba variasi pemegang | Item diaktifkan secara berasingan; pergerakan bermula dari pengepala |
| Aktifkan butang item melalui navigasi papan kekunci | Pemilihan berfungsi tanpa memerlukan seretan penuding |
Jika ruang panel kosong boleh diseret dengan betul tetapi kawasan item tidak boleh, periksa kawalan anak dan penggabungan gerak isyarat. Jika klik tidak berfungsi, periksa topeng gerak isyarat, keutamaan dan kawasan sasaran. Jika panel memecut atau melompat, periksa pengumpulan terjemahan dan ruang koordinat yang dipilih sebelum menambah pengecam lain.
Contoh ini sengaja tidak mengehadkan pergerakan, jadi panel boleh bergerak keluar dari pentas yang kelihatan; butang Reset panel memulihkan kedudukannya. Untuk ruang kerja terhad yang siap digunakan, tambahkan kekangan kedudukan yang jelas berdasarkan dimensi pentas dan panel. Kekalkan struktur pemilikan yang sama: anak meminta tindakan, induk mengawal pergerakan, dan kontrak interaksi menentukan sama ada kawasan pengecaman mereka bertindih.
Sumber
- simultaneousGesture(_:including:) — Apple
- Gestures — Apple
- coordinateSpace(_:) — Apple
- DragGesture — Apple
- updating(_:body:) — Apple
- translation — Apple
- State — Apple
- Closures — The Swift Programming Language, projek Swift
- contentShape(_:eoFill:) — Apple
- GestureMask — Apple
- highPriorityGesture(_:including:) — Apple
- Button — Apple
- hitTest(_:) — Apple
- mouseDown(with:) — Apple
- gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) — Apple