在 SwiftUI 中,如何使手势在容器视图之外处于非活动状态?

rli*_*ert 8 clipping gesture ios swiftui

我试图使视图仅在其剪切容器视图内可拖动和/或可缩放(否则它可能会遇到其他视图的手势并与之冲突),但到目前为止我尝试过的任何操作都无法阻止手势扩展到可见范围之外容器的边界。

这是我不想要的行为的简化演示......

当红色矩形部分超出绿色 VStack 区域(已剪切)时,它会响应超出绿色区域的拖动手势:
拖动不受容器限制

import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    
    @State var position: CGPoint = CGPoint(x: 100, y: 150)
    @State var lastPosition: CGPoint = CGPoint(x: 100, y: 150)
    
    var body: some View {
        
        let drag = DragGesture()
        .onChanged {
            self.position = CGPoint(x: $0.translation.width + self.lastPosition.x, y: $0.translation.height + self.lastPosition.y)
        }
        .onEnded {_ in
            self.lastPosition = self.position
        }
        
        return VStack {
            Rectangle().foregroundColor(.red)
                .frame(width: 150, height: 150)
                .position(self.position)
                .gesture(drag)
                .clipped()
        }
        .background(Color.green)
        .frame(width: 200, height: 300)
        
    }
}

PlaygroundPage.current.setLiveView(ContentView())
Run Code Online (Sandbox Code Playgroud)

您如何限制此手势仅在容器内起作用(上例中的绿色区域)?

更新:@Asperi 对上述问题的解决方案效果很好,但是当我在上面的容器旁边添加第二个可拖动容器时,我在第一个容器中出现一个“死区”,我无法在其中拖动(它似乎是如果没有剪裁,第二个方块将覆盖第一个方块)。问题仅发生在原来的/左侧,而不发生在新的一侧。我认为这与它具有更高的优先级有关,因为它是第二个绘制的。

这是新问题的说明:

2 容器产生拖动死区

这是更新后的代码:

struct ContentView: View {
    
    @State var position1: CGPoint = CGPoint(x: 100, y: 150)
    @State var lastPosition1: CGPoint = CGPoint(x: 100, y: 150)
    let dragArea1: CGRect = CGRect(x: 0, y: 0, width: 200, height: 300)
    
    @State var position2: CGPoint = CGPoint(x: 100, y: 150)
    @State var lastPosition2: CGPoint = CGPoint(x: 100, y: 150)
    let dragArea2: CGRect = CGRect(x: 0, y: 0, width: 200, height: 300)
    
    var body: some View {

        let drag1 = DragGesture(coordinateSpace: .named("dragArea1"))
        .onChanged {
            guard self.dragArea1.contains($0.startLocation) else { return }
            self.position1 = CGPoint(x: $0.translation.width + self.lastPosition1.x, y: $0.translation.height + self.lastPosition1.y)
        }
        .onEnded {_ in
            self.lastPosition1 = self.position1
        }
        
        let drag2 = DragGesture(coordinateSpace: .named("dragArea2"))
        .onChanged {
            guard self.dragArea2.contains($0.startLocation) else { return }
            self.position2 = CGPoint(x: $0.translation.width + self.lastPosition2.x, y: $0.translation.height + self.lastPosition2.y)
        }
        .onEnded {_ in
            self.lastPosition2 = self.position2
        }
        
        return HStack {
            VStack {
                Rectangle().foregroundColor(.red)
                    .frame(width: 150, height: 150)
                    .position(self.position1)
                    .gesture(drag1)
                    .clipped()
            }
            .background(Color.green)
            .frame(width: dragArea1.width, height: dragArea1.height)
            
            VStack {
                Rectangle().foregroundColor(.blue)
                .frame(width: 150, height: 150)
                .position(self.position2)
                .gesture(drag2)
                .clipped()
            }
            .background(Color.yellow)
            .frame(width: dragArea2.width, height: dragArea2.height)
        }
        
    }
}
Run Code Online (Sandbox Code Playgroud)

关于如何在任何容器之外保持禁用拖动(如已经实现的那样)的任何想法,但也允许在每个容器的整个边界内拖动,而不管其他容器发生什么情况?

Asp*_*eri 5

这是可能的解决方案。这个想法是在容器坐标空间中拥有拖动坐标,如果起始位置超出该指定区域,则忽略拖动。

使用 Xcode 11.4 / iOS 13.4 进行测试

演示

struct ContentView: View {

    @State var position: CGPoint = CGPoint(x: 100, y: 150)
    @State var lastPosition: CGPoint = CGPoint(x: 100, y: 150)

    var body: some View {
        let area = CGRect(x: 0, y: 0, width: 200, height: 300)

        let drag = DragGesture(coordinateSpace: .named("area"))
        .onChanged {
            guard area.contains($0.startLocation) else { return }
            self.position = CGPoint(x: $0.translation.width + self.lastPosition.x, y: $0.translation.height + self.lastPosition.y)
        }
        .onEnded {_ in
            self.lastPosition = self.position
        }

        return VStack {
            Rectangle().foregroundColor(.red)
                .frame(width: 150, height: 150)
                .position(self.position)
                .gesture(drag)
                .clipped()
        }
        .background(Color.green)
        .frame(width: area.size.width, height: area.size.height)
        .coordinateSpace(name: "area")

    }
}
Run Code Online (Sandbox Code Playgroud)