我正在 Rectangle() 中构建一个简单的拖动手势动画,在手势结束时,将形状粘贴到组中最近的项目中。
检查下图中的示例。
我通过以下方法取得了不错的结果:
手动计算黑色矩形的x绝对位置:
@State private var xPos : [Int] = [40,110,180,250,320]
使用绝对位置属性将矩形放置在屏幕中:
Rectangle()
.position(x: CGFloat(xPos[i]), y: 0)
最后,在蓝色矩形中,添加手势属性,在 .onEnded 上使用一个奇特的函数来查找最接近的元素,并相应地应用 x 位置。
Rectangle()
.fill(.blue)
.position(location)
.gesture(
DragGesture()
.onChanged { gesture in
location.x = gesture.location.x
}
.onEnded { value in
withAnimation(.spring()) {
var goal = Int(value.location.x)
var closest = xPos[0]
var index = 0
for item in xPos {
var distanceToActual = abs(closest - goal)
if (xPos.count - 1 > index) { // prevent out of the bound
var distanceToNext = abs(xPos[index + 1] - goal)
if(distanceToNext < distanceToActual) {
closest = xPos[index + 1]
}
}
index += 1
}
location.x = CGFloat(closest)
}
}
我的问题是,有没有更优雅的方法?手动计算黑色矩形的位置是非常烦人的...有什么方法可以将黑色矩形放置在 HStack 中而不是手动定位吗?
答案中描述的技术是否可以检测当前哪个视图落在 DragGesture 的位置下?可用于检测哪个方块最接近拖动位置(这是我的答案)。
matchedGeometryEffect
然后提供了一种方便的方法将蓝色矩形的位置与识别的正方形相匹配。像这样:
@State private var dragLocation = CGPoint.zero
@State private var indexForDragLocation = 0
@Namespace private var ns
let spacing: CGFloat = 50
var body: some View {
HStack(spacing: spacing) {
ForEach(0...4, id: \.self) { index in
Color(white: 0.2)
.frame(width: 20, height: 20)
.matchedGeometryEffect(id: index, in: ns, isSource: index == indexForDragLocation)
.background {
GeometryReader { proxy in
let width = proxy.size.width
let midX = proxy.frame(in: .global).midX
let dx = abs(midX - dragLocation.x)
let isClosest = dx < (width + spacing) / 2
Color.clear
.onChange(of: isClosest) { oldVal, newVal in
if isClosest {
indexForDragLocation = index
}
}
}
}
}
}
.background {
RoundedRectangle(cornerRadius: 4)
.fill(.blue)
.frame(width: 40, height: 80)
.matchedGeometryEffect(
id: indexForDragLocation,
in: ns,
properties: .position,
isSource: false
)
.animation(.spring, value: indexForDragLocation)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(UIColor.systemBackground))
.gesture(
DragGesture(coordinateSpace: .global)
.onChanged { val in
dragLocation = val.location
}
)
}