我有一个定义点的数组,我使用这些点在每个位置绘制一颗星。星形视图应该简单地报告拖动手势以更新每个点的位置。目前,代码部分有效:拖动量不等于星星的位移。
我的目标是让星形视图尽可能简洁;我只想使用此视图读取拖动量。我希望在 ForEach 循环中更新点的位置。如何在 ForEach 中将 DragGesture 值正确转换为 CGPoint 以实现此目的?
import SwiftUI
struct ContentView: View {
@State private var points: [CustomPoint] = [CustomPoint(x: 100, y: 100), CustomPoint(x: 200, y: 200), CustomPoint(x: 300, y: 100)]
var body: some View {
GeometryReader { proxy in
Color.blue
ForEach($points) { $point in
DrawStarView(customPoint: point) { value in
point.cgPointValue = CGPoint(x: point.cgPointValue.x + value.width, y: point.cgPointValue.y + value.height)
}
}
}
.padding()
}
}
struct CustomPoint: Identifiable {
init(x: CGFloat, y: CGFloat) {
self.cgPointValue = CGPoint(x: x, y: y)
}
let id: UUID = UUID()
var cgPointValue: CGPoint
}
struct DrawStarView: View {
init(customPoint: CustomPoint, action: @escaping (CGSize) -> Void) {
self.customPoint = customPoint
self.action = action
}
let customPoint: CustomPoint
let action: (CGSize) -> Void
var body: some View {
return createStarPath(at: customPoint.cgPointValue)
.fill(Color.white)
.shadow(radius: 5)
.gesture(dragGesture)
}
func createStarPath(at point: CGPoint, size: CGSize = CGSize(width: 100, height: 100)) -> Path {
let outerRadius = size.width / 2
let innerRadius = outerRadius / 2.5
let center = point
let pointsOnStar = 5
let angleIncrement = .pi * 2 / CGFloat(pointsOnStar * 2)
var path = Path()
for i in 0..<pointsOnStar * 2 {
let angle = angleIncrement * CGFloat(i) - .pi / 2
let radius = (i % 2 == 0) ? outerRadius : innerRadius
let x = center.x + radius * cos(angle)
let y = center.y + radius * sin(angle)
if i == 0 {
path.move(to: CGPoint(x: x, y: y))
} else {
path.addLine(to: CGPoint(x: x, y: y))
}
}
path.closeSubpath()
return path
}
private var dragGesture: some Gesture {
DragGesture(minimumDistance: 0)
.onChanged { value in
action(value.translation)
}
.onEnded { value in
action(value.translation)
}
}
}
translation
是手势开始以来的总onChange
平移量。它不是本次调用和下次调用之间的位置差异onChange
。如果差值正是您想要的,您应该
translation
使用单独的跟踪前一个@State
,然后从当前的 中减去该值translation
,然后将其传递给action
。正如评论中所提到的,很难理解 OP 方法背后的逻辑,因为据我所知,它没有提供额外的优势,也没有以任何方式简化事情。
由于 @Sweeper 提供了修复拖动手势的解决方案(结合了下面示例中的一些小改动),下面是如何以更 SwiftUI 的方式实现相同的功能,并且更符合我认为 @Sweeper 在之前的评论中所建议的方式。
一些补充说明:
星形视图(原始代码中的 DrawStarView)不需要操作闭包即可使一切正常工作,因为它已经接收了点作为参数。它使一切变得不必要地复杂。
你代码中的func
createStarPath
基本上是一个星形的代码,所以应该把它变成一个形状以使其可重复使用,而不是仅存在于视图内部DrawStarView
。各种初始化程序和动作闭包的整体复杂性根本没有必要——与下面的代码相比。
没有使用
GeometryReader
,因此用 代替,以ZStack
使布局和逻辑更加直观。添加了一个按钮,将(更新后的)星星的位置打印到控制台,以表明没有维护最终点的位置,因为由于传递给的绑定,点在数组中直接更新
StarView
。为方便起见,添加了可选的双击星星来重置其位置。
请注意,可以使用修改器控制星星的大小
.frame
,这在以前是不可能的。如果需要,可以添加其他参数StarView
来动态控制星星的大小。更新:我继续添加了一个 size 属性CustomPoint
,现在可以设置星星的大小了。完整代码如下:
尝试一下,然后让我知道它是否有意义或者是否仍然会给您的用例带来困难。