在 SwiftUI 中可以同时使用 .onDrag 和 .gesture(DragGesture()) 吗?

问题描述 投票:0回答:1

我有一个项目视图,上面有一个

.simultaneousGesture(DragGesture())
修饰符,用于处理在画布周围拖动项目并更新其 x 和 y 位置。我还想添加一个
.onDrag
修饰符来处理不同视图之间的拖放项目。但这两者互相干扰。

我尝试在手势之前和之后放置

.onDrag
,但这两个位置都没有解决我的问题。 如果
.onDrag
放置在
.simultaneousGesture
之前,则
.onDrag
事件会按预期触发,
.simultaneousGesture
也会触发,但仅限于前几次更新。如果放在后面,则仅识别
.simultaneousGesture

下面是视图的代码,下面我附加了拖动 FileView 后控制台输出的屏幕截图。

import Foundation
import SwiftUI
    
struct FileView: View {
    @ObservedObject var form: Forms.File

    @State private var firstClick: Bool = false
    @State private var mousedown: Bool = false
    @State private var initialDragOffset = CGSize.zero
    @State private var hovering: Bool = false
    @ObservedObject private var windowSize = WindowSize.shared
    
    var body: some View {
        HStack {
            Image(systemName: "doc.fill")
                .resizable()
                .scaledToFit()
                .frame(width: 17, height: 17)
                .aspectRatio(contentMode: .fit)
                .scaleEffect(x: 1, y: mousedown ? 0.8 : 1)
                .foregroundColor(form.isSelected ? Color.red : Color.black)
                .frame(width: 15, height: 15)
            Text(form.name)
        }
        .frame(height: 30)
        .onHover { hovering in
            self.hovering = hovering
        }
        .padding(.leading, 8)
        .padding(.trailing, 8)
        .overlay(
            RoundedRectangle(cornerRadius: 8)
                .stroke(form.isSelected ? Color.red : Color.clear, lineWidth: 1)
        )
        .position(
            x: windowSize.width * form.canvasPosition.x,
            y: windowSize.height * form.canvasPosition.y
        )
        .onDrag {
            print("DRAGGING")
            return NSItemProvider(object: form.path as NSString)
        }
        .simultaneousGesture(
            DragGesture(minimumDistance: 0.10)
            .onChanged { gesture in
                if initialDragOffset == .zero {
                    let initialX = windowSize.width * form.canvasPosition.x
                    let initialY = windowSize.height * form.canvasPosition.y
                    initialDragOffset = CGSize(width: gesture.location.x - initialX,
                                               height: gesture.location.y - initialY)
                }

                let newX = (gesture.location.x - initialDragOffset.width) / windowSize.width
                let newY = (gesture.location.y - initialDragOffset.height) / windowSize.height
                form.updatedAt = Int(Date().timeIntervalSince1970)
                form.canvasPosition.x = newX
                form.canvasPosition.y = newY
                print(newX, newY)
            }
            .onEnded { _ in
                initialDragOffset = .zero
            }
        )
        .onLongPressGesture(minimumDuration: 0.01, pressing: { isPressing in
            if isPressing {
                withAnimation(.easeInOut(duration: 0.1)) {
                    mousedown = true
                }
            } else {
                if firstClick {
                    form.open()
                } else {
                    firstClick = true
                    
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                        firstClick = false
                    }
                }
                
                if(form.isSelected) {
                    mousedown = false
                } else {
                    withAnimation(Animation.interpolatingSpring(mass: 2.0, stiffness: 80, damping: 8, initialVelocity: 0).speed(6)) {
                        mousedown = false
                        form.isSelected.toggle()
                    }
                }
            }
        }){}
    }
}

正如您在输出中看到的,

onChanged
.onDrag
事件触发之前打印一次,之后打印一次,但当我继续拖动时,不会再打印一次。有什么方法可以让这两个修饰符协调工作吗?

控制台输出

注意:我尝试过,但我不知道如何嵌入图像而不是仅显示链接:(

ios swift swiftui gesture drag
1个回答
0
投票

组合 .onDrag 和 .simultaneousGesture 确实会引起冲突,因为这两种手势可能会干扰彼此的状态和识别。管理此问题的一种方法是创建一个自定义手势,该手势结合了拖放功能和位置更新逻辑的行为。以下是实现这一目标的方法:

自定义手势识别器:将拖放逻辑合并到单个手势识别器中。 状态管理:管理自定义手势中的状态,以确保两种功能可以共存。 以下是修改 FileView 来实现此目的的方法:

迅速 复制代码 进口基金会 导入 SwiftUI

结构FileView:视图{ @ObservedObject var 形式:Forms.File

@State private var firstClick: Bool = false
@State private var mousedown: Bool = false
@State private var initialDragOffset = CGSize.zero
@State private var hovering: Bool = false
@ObservedObject private var windowSize = WindowSize.shared

var body: some View {
    HStack {
        Image(systemName: "doc.fill")
            .resizable()
            .scaledToFit()
            .frame(width: 17, height: 17)
            .aspectRatio(contentMode: .fit)
            .scaleEffect(x: 1, y: mousedown ? 0.8 : 1)
            .foregroundColor(form.isSelected ? Color.red : Color.black)
            .frame(width: 15, height: 15)
        Text(form.name)
    }
    .frame(height: 30)
    .onHover { hovering in
        self.hovering = hovering
    }
    .padding(.leading, 8)
    .padding(.trailing, 8)
    .overlay(
        RoundedRectangle(cornerRadius: 8)
            .stroke(form.isSelected ? Color.red : Color.clear, lineWidth: 1)
    )
    .position(
        x: windowSize.width * form.canvasPosition.x,
        y: windowSize.height * form.canvasPosition.y
    )
    .gesture(
        DragGesture(minimumDistance: 0.10)
            .onChanged { gesture in
                if initialDragOffset == .zero {
                    let initialX = windowSize.width * form.canvasPosition.x
                    let initialY = windowSize.height * form.canvasPosition.y
                    initialDragOffset = CGSize(width: gesture.location.x - initialX,
                                               height: gesture.location.y - initialY)
                }

                let newX = (gesture.location.x - initialDragOffset.width) / windowSize.width
                let newY = (gesture.location.y - initialDragOffset.height) / windowSize.height
                form.updatedAt = Int(Date().timeIntervalSince1970)
                form.canvasPosition.x = newX
                form.canvasPosition.y = newY
                print(newX, newY)
            }
            .onEnded { _ in
                initialDragOffset = .zero
            }
    )
    .onDrag {
        print("DRAGGING")
        return NSItemProvider(object: form.path as NSString)
    }
    .onLongPressGesture(minimumDuration: 0.01, pressing: { isPressing in
        if isPressing {
            withAnimation(.easeInOut(duration: 0.1)) {
                mousedown = true
            }
        } else {
            if firstClick {
                form.open()
            } else {
                firstClick = true

                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                    firstClick = false
                }
            }

            if(form.isSelected) {
                mousedown = false
            } else {
                withAnimation(Animation.interpolatingSpring(mass: 2.0, stiffness: 80, damping: 8, initialVelocity: 0).speed(6)) {
                    mousedown = false
                    form.isSelected.toggle()
                }
            }
        }
    }){}
}

} 解释 自定义手势:使用单个 DragGesture 并管理 .onChanged 和 .onEnded 闭包内的状态来处理拖动和位置更新。 状态管理:跟踪初始拖动偏移以正确计算新位置。 此设置确保 DragGesture 可以连续处理位置更新,并且 .onDrag 修改器可以处理拖放功能而不会相互干扰。 .onDrag 功能仅在拖动手势结束时触发,确保两个手势协调工作。

© www.soinside.com 2019 - 2024. All rights reserved.