如何检测 SwiftUI 视图何时被拖过
Posted
技术标签:
【中文标题】如何检测 SwiftUI 视图何时被拖过【英文标题】:How to detect when SwiftUI View is being dragged over 【发布时间】: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 数据似乎是一种反模式。
有什么建议吗?
【问题讨论】:
您觉得这些答案有帮助吗,还是需要更多帮助? 【参考方案1】:我用小卡片视图的网格替换了您示例的图像。 我们将尝试更改被拖动手势“交叉”的卡片的颜色。
我们可以使用PreferenceKey
来获取所有CardView
s 边界...
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")
)
【讨论】:
以上是关于如何检测 SwiftUI 视图何时被拖过的主要内容,如果未能解决你的问题,请参考以下文章
iOS - 拖放碰撞检测如何检测您选择的项目何时拖过另一个子视图?