【问题标题】:How to detect when SwiftUI View is being dragged over如何检测 SwiftUI 视图何时被拖过
【发布时间】:2021-08-14 17:18:29
【问题描述】:

我想在用户在我的视图上拖动手指时触发动画。

我可以做类似的事情

Image(…)
  .rotationEffect(…)
  .animation(self.isAnimating ? .spring : .default)
  .gesture(
    DragGesture(minimumDistance: 5, coordinateSpace: .global)
      .onChanged { value in
        self.isAnimating = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
          self.isAnimating = false
        }
      }
  )

但是,这仅捕获从图像开始的拖动事件。在其他地方开始然后在图像上移动的拖动事件将被忽略。

我还可以检测父视图中的拖动事件并计算哪些子视图正在被拖动——这很好。但是,我如何告诉子视图动画呢?更新它们的属性会导致重新渲染,这当然会取消动画。

通过模型传递这样的 ui 数据似乎是一种反模式。

有什么建议吗?

【问题讨论】:

  • 您觉得这些答案有帮助吗,还是需要更多帮助?

标签: ios animation swiftui gesture


【解决方案1】:

我用小卡片视图的网格替换了您示例的图像。 我们将尝试更改被拖动手势“交叉”的卡片的颜色。

我们可以使用PreferenceKey 来获取所有CardViews 边界...

struct CardPreferenceData: Equatable {
    let index: Int
    let bounds: CGRect
}

struct CardPreferenceKey: PreferenceKey {
    typealias Value = [CardPreferenceData]
    
    static var defaultValue: [CardPreferenceData] = []
    
    static func reduce(value: inout [CardPreferenceData], nextValue: () -> [CardPreferenceData]) {
        value.append(contentsOf: nextValue())
    }
}

这里:

struct CardView: View {
    let index: Int
    
    var body: some View {
        Text(index.description)
            .padding(10)
            .frame(width: 60)
            .overlay(RoundedRectangle(cornerRadius: 10).stroke())
            .background(
                GeometryReader { geometry in
                    Rectangle()
                        .fill(Color.clear)
                        .preference(key: CardPreferenceKey.self,
                                    value: [CardPreferenceData(index: self.index, bounds: geometry.frame(in: .named("GameSpace")))])
                }
            )
    }
}

现在我们可以在 ContentView 中收集这些卡片的所有首选项(边界和索引)并将它们存储在一个数组中:

.onPreferenceChange(CardPreferenceKey.self){ value in
            cardsData = value
        }

我们现在可以将这些 CardView 的位置(bounds)与拖动手势的位置进行比较。


struct ContentView: View {
    let columns = Array(repeating: GridItem(.fixed(60), spacing: 40), count: 3)
    @State private var selectedCardsIndices: [Int] = []
    @State private var cardsData: [CardPreferenceData] = []
    var body: some View {
        LazyVGrid(columns: columns, content: {
            ForEach((1...12), id: \.self) { index in
                CardView(index: index)
                    .foregroundColor(selectedCardsIndices.contains(index) ? .red : .blue)
            }
        })
        .onPreferenceChange(CardPreferenceKey.self){ value in
            cardsData = value
        }
        .gesture(
            DragGesture()
                .onChanged {drag in
                    if let data = cardsData.first(where: {$0.bounds.contains(drag.location)}) {
                        selectedCardsIndices.append(data.index)
                    }
                }
        )
        .coordinateSpace(name: "GameSpace")
    }
}

编辑:视频开头的小“滞后”不会出现在画布上。只在模拟器上。我没有在真机上测试过。

【讨论】:

  • 如何用一行来链接?
【解决方案2】:

这样怎么样?

我将拖拽点设置为两端的 +30/-30。

例如iPhone 12 宽度:844,端点:x:30 和 x:814




struct ContentView: View {
    
    @State private var offsetX: CGFloat = 0
    @State private var offsetY: CGFloat = 0
    
    var body: some View {
        VStack {
            Circle()
                .frame(width: 100, height: 100)
                .offset(x: offsetX, y: offsetY)
                .gesture(DragGesture(minimumDistance: 5, coordinateSpace: .global)
                            .onChanged { value in
                                offsetX = value.translation.width
                                offsetY = value.translation.height
                                
                                print("x: \(value.location.x)")
                                print("y: \(value.location.y)")
                                
                                if value.location.x > UIScreen.main.bounds.width - 30 || value.location.x < 30 {
                                    print("End")
                                }
                                
                                if value.location.y > UIScreen.main.bounds.height - 30 || value.location.y < 30 {
                                    print("End")
                                }
                            }
                )
        }
    }
}



【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 2020-07-25
    • 2021-05-22
    • 1970-01-01
    • 2019-09-08
    • 1970-01-01
    相关资源
    最近更新 更多