Kekalkan kedudukan bentuk yang telah disahkan tanpa perubahan semasa SwiftUI mempratonton seretan, kemudian sahkan anjakan akhir dalam onEnded. Ini mengekalkan kedudukan permulaan sehingga selesai tanpa bergantung pada pemasaan tetikus dilepaskan AppKit. Berikut ialah contoh lengkap untuk menggerakkan segi empat tepat dalam apl Mac anda sendiri.

Sempadan penyelesaian manakah yang patut mengawal keadaan permulaan?

Untuk interaksi menggerakkan bentuk, tugas khususnya ialah mengira setiap pratonton dan kedudukan akhir daripada kedudukan permulaan yang sama. Sempadan penyelesaian yang sesuai ialah apabila gerak isyarat bentuk itu berjaya diselesaikan. Apple menyatakan bahawa onEnded(_:) menyediakan nilai akhir gerak isyarat dan hanya berjalan apabila gerak isyarat berakhir dengan jayanya. Apple mengesyorkan @GestureState untuk keadaan sementara yang perlu ditetapkan semula tanpa mengira cara gerak isyarat berakhir. Dokumentasi Apple tentang onEnded(_:).

Berikan interaksi ini dua jenis keadaan: kedudukan model yang telah disahkan dan anjakan pratonton sementara. Semasa menyeret, paparkan jumlah kedua-duanya. Apabila berjaya selesai, gunakan anjakan akhir pada kedudukan yang telah disahkan sekali sahaja. Reka bentuk ini berasaskan perbezaan yang dibuat Apple antara kemas kini gerak isyarat sementara dengan kemas kini keadaan kekal. Menambahkan interaktiviti dengan gerak isyarat.

Maklumat Pemilik dalam contoh ini Jangka hayat
Pusat segi empat tepat yang telah disahkan Model @State peribadi Kekal selepas gerak isyarat selesai
Anjakan pratonton semasa @GestureState Ditetapkan semula apabila gerak isyarat menjadi tidak aktif
Anjakan akhir Argumen panggil balik onEnded Digunakan secara segerak untuk mengesahkan pergerakan
Peristiwa tetikus dilepaskan AppKit Pengendalian peristiwa AppKit Melaporkan pelepasan butang; tidak mentakrifkan penyelesaian gerak isyarat SwiftUI

Apple menerangkan @State sebagai storan yang diuruskan SwiftUI dan sumber kebenaran untuk nilai dalam hierarki paparan. @GestureState ditetapkan semula secara automatik apabila gerak isyaratnya menjadi tidak aktif. Dengan meletakkan garis dasar dalam yang pertama dan pratonton dalam yang kedua, setiap nilai mendapat jangka hayat yang diperlukan oleh interaksi ini. State, GestureState.

Bina contoh seretan segi empat tepat Mac yang lengkap

Cipta apl macOS SwiftUI dalam Xcode dengan sasaran penggunaan macOS 14 atau lebih baharu. Gantikan pengisytiharan apl dan paparan kandungan yang dijana dengan satu fail berikut, dan pastikan sasaran hanya mempunyai satu pengisytiharan @main. Contoh ini menggunakan satu segi empat tepat sintetik yang disimpan sepenuhnya dalam memori. API ruang koordinat bernama didokumenkan sebagai tersedia mulai macOS 14. Pemula gerak isyarat seret, Ruang koordinat bernama.

import SwiftUI

@main
struct GestureStartDemoApp: App {
    var body: some Scene {
        WindowGroup {
            ShapeCanvas()
        }
    }
}

private struct SampleItem {
    var center = CGPoint(x: 160, y: 140)
}

private struct ShapeCanvas: View {
    @State private var item = SampleItem()
    @GestureState private var previewTranslation = CGSize.zero

    private let canvasWidth: CGFloat = 600
    private let canvasHeight: CGFloat = 360
    private let shapeWidth: CGFloat = 80
    private let shapeHeight: CGFloat = 60

    private func projectedCenter(_ translation: CGSize) -> CGPoint {
        let proposedX = item.center.x + translation.width
        let proposedY = item.center.y + translation.height
        let halfWidth = shapeWidth / 2
        let halfHeight = shapeHeight / 2

        return CGPoint(
            x: min(max(proposedX, halfWidth),
                   canvasWidth - halfWidth),
            y: min(max(proposedY, halfHeight),
                   canvasHeight - halfHeight)
        )
    }

    private var moveGesture: some Gesture {
        DragGesture(
            minimumDistance: 3,
            coordinateSpace: .named("shapeCanvas")
        )
        .updating($previewTranslation) { value, translation, _ in
            translation = value.translation
        }
        .onEnded { value in
            let finalCenter = projectedCenter(value.translation)
            item.center = finalCenter
        }
    }

    var body: some View {
        let displayedCenter = projectedCenter(previewTranslation)

        VStack(alignment: .leading, spacing: 12) {
            Text("Drag the blue rectangle")
                .font(.headline)

            ZStack {
                Color.gray.opacity(0.12)

                RoundedRectangle(cornerRadius: 10)
                    .fill(Color.blue)
                    .frame(width: shapeWidth, height: shapeHeight)
                    .contentShape(Rectangle())
                    .gesture(moveGesture)
                    .position(
                        x: displayedCenter.x,
                        y: displayedCenter.y
                    )
            }
            .frame(width: canvasWidth, height: canvasHeight)
            .coordinateSpace(.named("shapeCanvas"))
            .clipped()

            Text(
                "Committed center: " +
                "\(Int(item.center.x)), \(Int(item.center.y))"
            )
            .monospacedDigit()
        }
        .padding(20)
    }
}

Invarian utamanya ialah item.center tidak pernah berubah dalam updating. Nilai itu kekal sebagai garis dasar model gerak isyarat sehingga onEnded mengira finalCenter. Pengiraan akhir membaca value.translation secara langsung, jadi sifat pratonton tidak perlu mengekalkan nilai terakhirnya semasa penyelesaian.

Gerak isyarat mengukur pergerakan dalam ruang koordinat bernama kanvas yang mengandungi bentuk itu. position(x:y:) segi empat tepat meletakkan pusatnya dalam ruang induk yang sama. Penggunaan ruang yang sepadan mengelakkan percampuran koordinat setempat bentuk yang bergerak dengan koordinat kanvas. Apple mendokumenkan kedua-dua pengukuran gerak isyarat dalam ruang bernama dan kedudukan berasaskan pusat. coordinateSpace(_:), position(x:y:).

Fungsi projectedCenter yang dikongsi juga memastikan segi empat tepat kekal di dalam kanvas. Pratonton dan pengesahan menggunakan sempadan yang sama: pusat mendatar dari 40 hingga 560, dan pusat menegak dari 30 hingga 330. Di tepi kanvas, pergerakan tetap berdasarkan anjakan keseluruhan dari permulaan seretan asal; segi empat tepat bergerak menjauhi tepi apabila kedudukan yang dicadangkan kembali ke dalam sempadan.

Pergerakan yang kurang daripada ambang pengecaman 3 mata yang dikonfigurasi tidak dianggap sebagai seretan yang dikenal pasti. Contoh ini sengaja menganggap permulaan sebagai permulaan interaksi menggerakkan bentuk yang telah dikenal pasti. Keperluan untuk menangkap keadaan model pada peristiwa fizikal tetikus ditekan ialah sempadan yang berbeza. Pemula gerak isyarat seret.

Mengapa kedudukan permulaan kekal selepas kemas kini akhir

Apple mentakrifkan DragGesture.Value.translation sebagai jumlah anjakan dari permulaan gerak isyarat hingga peristiwa semasa. Nilainya terkumpul, bukannya delta sejak panggil balik sebelumnya. Oleh itu, menambahkan setiap anjakan yang dilaporkan pada kedudukan model yang sudah dikemas kini akan mengira pergerakan secara berlebihan. DragGesture.Value.

Pertimbangkan input ilustrasi berikut, yang semuanya berada dalam sempadan kanvas:

Peringkat Jumlah anjakan yang dilaporkan Pusat yang telah disahkan Pusat yang dipaparkan
Sebelum menyeret (0, 0) (160, 140) (160, 140)
Kemas kini pertama (12, 5) (160, 140) (172, 145)
Kemas kini kedua (30, -10) (160, 140) (190, 130)
Berjaya selesai (45, 20) Menjadi (205, 160) Berakhir pada (205, 160)

Setiap pratonton menggunakan garis dasar yang sama. Penyelesaian menggunakan (45, 20) akhir sekali sahaja. Seretan berjaya seterusnya dengan anjakan (-15, 10) kemudian bermula dari (205, 160) dan mengesahkan (190, 170). Pengiraan ini diperiksa menggunakan pengiraan Python yang deterministik. Apl SwiftUI diteliti berdasarkan dokumentasi API yang diperoleh, tetapi tidak dikompil atau dijalankan dalam persekitaran ini.

Jangan gantikan pusat permulaan segi empat tepat dengan startLocation. Apple mentakrifkan startLocation sebagai lokasi peristiwa pertama seretan. Penuding boleh bermula berhampiran bucu segi empat tepat, jadi lokasi itu mungkin berbeza daripada pusat model bentuk. Begitu juga, predictedEndTranslation ialah ramalan berasaskan halaju; gunakan translation akhir untuk interaksi penempatan langsung ini. DragGesture.Value.

Reka bentuk ini tidak memerlukan pengunduran kedudukan yang telah disahkan apabila pembatalan berlaku kerana kemas kini pratonton tidak pernah menulis gantinya. Apple menyatakan bahawa keadaan gerak isyarat ditetapkan semula apabila gerak isyarat menjadi tidak aktif, manakala onEnded hanya berjalan selepas gerak isyarat berjaya. Oleh itu, anjakan sementara lenyap tanpa mengesahkan pergerakan yang dibatalkan. Kesimpulan ini berdasarkan tingkah laku yang didokumenkan itu; ia tidak bergantung pada andaian tentang turutan antara tetapan semula dengan panggil balik penyelesaian berjaya. GestureState, onEnded(_:).

Mengapa pemantau tetikus dilepaskan AppKit tidak patut mengosongkan garis dasar

Pemantau peristiwa setempat AppKit menerima peristiwa sebelum peristiwa itu dihantar kepada apl. Oleh itu, mengosongkan keadaan mula gerak isyarat yang dikongsi daripada peristiwa tetikus dilepaskan yang dipantau boleh mengalih keluar keadaan itu sebelum pengendalian seterusnya memproses peristiwa tersebut. Ini berpunca daripada sempadan penghantaran yang didokumenkan, bukannya jaminan turutan panggil balik SwiftUI yang didokumenkan. addLocalMonitorForEvents(matching:handler:).

Begitu juga, NSResponder.mouseUp(with:) melaporkan bahawa pengguna melepaskan butang tetikus kiri. Kontraknya tidak menyatakan bahawa setiap panggil balik gerak isyarat SwiftUI atau kemas kini paparan telah selesai. Kekalkan pemerhatian peristiwa mentah dan penyelesaian gerak isyarat yang dikenal pasti sebagai tanggungjawab berasingan. mouseUp(with:).

API yang disemak di sini tidak menetapkan makna panggil balik umum bahawa SwiftUI telah selesai memproses semua perkara yang berkaitan dengan peristiwa mentah. Blok pembersihan beratur atau pemasa singkat akan menambah andaian penjadualan tanpa menetapkan hubungan tersebut. Untuk tugas ini, cara yang disokong ialah melakukan pengiraan model akhir dalam onEnded, tempat nilai akhir gerak isyarat tersedia.

Jika interaksi perlu dilaksanakan dalam paparan AppKit tersuai, biarkan paparan itu mengurus jujukan input dan keadaan sesi, lalu integrasikannya melalui NSViewRepresentable. Apple menyediakan cangkuk penciptaan, kemas kini, penyelaras dan peleraian untuk integrasi tersebut. Kekalkan sesi paparan yang diwakilkan dalam paparan AppKit atau penyelarasnya, bukannya membinanya semula daripada setiap nilai representable. SwiftUI mengawal susun atur paparan yang diwakilkan, jadi jangan ubah bingkai atau sempadannya yang diuruskan secara berasingan. NSViewRepresentable.

Bilakah interaksi ini memerlukan petikan keadaan yang jelas?

Kedudukan yang telah disahkan dalam contoh ini memadai kerana tiada tindakan lain mengubahnya semasa seretan aktif. Untuk kanvas yang lebih besar, gunakan panduan keputusan ini sebelum memperkenalkan objek sesi berasingan:

Keperluan Kesan reka bentuk
Menggerakkan satu item yang kedudukannya kekal tanpa perubahan semasa pratonton Gunakan pendekatan garis dasar dan pratonton yang ditunjukkan
Menggerakkan beberapa item terpilih bersama-sama Kekalkan pengecam stabil dan kedudukan permulaan mereka sebagai satu petikan interaksi
Membenarkan arahan lain mengubah kedudukan semasa seretan Tentukan sama ada perubahan itu disekat, seretan dibatalkan, atau diselesaikan berdasarkan semakan model
Menangkap keadaan pada tetikus ditekan secara fizikal sebelum pengecaman Gunakan sempadan input AppKit yang dimiliki secara jelas; kemas kini gerak isyarat pertama tidak setara
Mengekalkan sesi berasingan selepas selesai Tetapkan pemilik dan dasar penamatannya secara jelas dan jangan bergantung pada keadaan gerak isyarat sementara

Petikan mesti mengekalkan nilai. Struktur Swift mempunyai semantik nilai, manakala pemberian nilai kepada tika kelas menyalin rujukannya. Oleh itu, tatasusunan rujukan kepada objek item boleh ubah tidak membekukan kedudukannya dengan sendirinya. Untuk pergerakan berbilang item, tangkap pengecam dan nilai koordinat yang diperlukan bagi pengiraan akhir. Struktur dan Kelas — Bahasa Pengaturcaraan Swift.

Jika anda memperkenalkan sesi berterusan, takrifkan penyelesaian berjaya, pembatalan dan penyingkiran paparan secara jelas. Mengosongkannya hanya dalam onEnded tidak menangani pembatalan kerana panggil balik itu hanya berlaku selepas kejayaan. Jangan gunakan semula transaksi tetapan semula keadaan gerak isyarat sebagai pemberitahuan penyelesaian sejagat: Apple menyatakan bahawa penutupan tetapan semulanya menyediakan transaksi untuk menetapkan semula keadaan gerak isyarat. onEnded(_:), init(wrappedValue:reset:).

Untuk tugas biasa menggerakkan segi empat tepat, kekalkan garis dasar dengan membiarkan model yang telah disahkan tidak berubah semasa pratonton. Kira kedudukan akhir daripada nilai gerak isyarat yang berjaya diselesaikan, sahkannya sekali, dan biarkan keadaan gerak isyarat sementara ditetapkan semula. Dengan cara ini, kedudukan permulaan mempunyai pemilik dan jangka hayat yang jelas sepanjang pengiraan yang memerlukannya.

Sumber