我一直在尝试在 SwiftUI 中在 2 个圆(第一个和第二个圆)之间画一条线的不同方法。
我的视图基本上是 10 x 5 网格中 50 个圆圈的网格。
发现了一些关于使用首选项键的其他线程和另一个建议的 GeometryReader,但将其应用到我的视图似乎没有帮助。我决定实现一种自定义方法来查找我视图中每个圆的 CGPoint 坐标,它可以工作,但不是我想要的方式。
我的目的是找到网格中每个圆的中心的CGPoint,这样我就可以均匀地绘制到每个圆的中心的线,但是我只是没有得到正确的计算,有人能帮我指出这里出了什么问题吗?
这是我到目前为止所拥有的,
calculateCirclePosition()
是我计算每个坐标的方式。
这是我的查看代码,供以下参考:
import SwiftUI
struct CircleGridView: View {
let rows = 10
let columns = 5
@State private var coordinates: [Int :CGPoint] = [:]
var body: some View {
ZStack {
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: columns)) {
ForEach(0..<rows * columns, id: \.self) { index in
Circle()
.foregroundColor(.blue)
.frame(width: 50, height: 50)
.onAppear {
DispatchQueue.main.async {
let circlePosition = calculateCirclePosition(for: index)
coordinates[index] = circlePosition
print("Index \(index): \(circlePosition)")
}
}
}
}
.padding(10)
if let startPoint = coordinates[0], let endPoint = coordinates[1] {
Path { path in
path.move(to: startPoint)
path.addLine(to: endPoint)
}.stroke(Color.red, lineWidth: 2)
}
}
}
func calculateCirclePosition(for index: Int) -> CGPoint {
let row = index / columns
let column = index % columns
let circleSize: CGFloat = 50
let circleSpacing: CGFloat = 10
let yOffset: CGFloat = 85
let xOffset: CGFloat = 15
let x = CGFloat(column) * (circleSize + circleSpacing) + circleSize / 2 + circleSpacing + xOffset
let y = CGFloat(row) * (circleSize + circleSpacing) + circleSize / 2 + circleSpacing + yOffset
return CGPoint(x: x, y: y)
}
}
您可以使用
GeometryReader
在 ZStack
的坐标空间(即绘制 Path
的坐标空间)中读取圆的框架。
不要使用
onAppear
更新字典来跟踪圆的中心,而是将字典放入 PreferenceKey
:
struct CirclePositionsKey: PreferenceKey {
static var defaultValue: [Int: CGPoint] { [:] }
static func reduce(value: inout [Int : CGPoint], nextValue: () -> [Int : CGPoint]) {
value.merge(nextValue(), uniquingKeysWith: { $1 })
}
}
每个圈子的偏好将是一个键值对。
reduce
实现将所有圆圈的偏好字典合并在一起,这样我们就可以在onPreferenceChange
中获得整个字典。
在视图中,您可以执行以下操作:
let rows = 10
let columns = 5
// use a state to track the start and end points of the line
@State private var line = Line()
var body: some View {
ZStack {
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: columns)) {
ForEach(0..<rows * columns, id: \.self) { index in
GeometryReader { geo in
let frame = geo.frame(in: .named("ZStack"))
// read the circle centre here
let center = CGPoint(x: frame.midX, y: frame.midY)
Circle()
.foregroundColor(.blue)
.preference(key: CirclePositionsKey.self, value: [
index: center
])
}
.frame(width: 50, height: 50)
}
}
.padding(10)
.onPreferenceChange(CirclePositionsKey.self) { value in
if let p1 = value[0], let p2 = value[1] {
// update the line's points in onPreferenceChange
line.start = p1
line.end = p2
}
}
Path { path in
path.move(to: line.start)
path.addLine(to: line.end)
}.stroke(Color.red, lineWidth: 2)
}
.coordinateSpace(.named("ZStack"))
}
struct Line {
var start: CGPoint = .zero
var end: CGPoint = .zero
}
也就是说,我会使用
Canvas
来绘制圆圈和线条。 Canvas
让您可以更好地控制在哪里绘制事物。
正如 Sweeper 所评论的,绘制网格的更简单方法可能是使用
Canvas
。
但是,如果您确实想使用
LazyVGrid
,那么可以简单地使用圆圈上的叠加来绘制线条。这样,定位是自动的,完全省去了计算。
GeometryReader
可用于查找每个网格单元的大小。struct CircleGridView: View {
let rows = 10
let columns = 5
let spacing: CGFloat = 10
var body: some View {
ZStack {
LazyVGrid(
columns: Array(
repeating: GridItem(.flexible(), spacing: spacing),
count: columns
),
spacing: spacing
) {
ForEach(0..<rows * columns, id: \.self) { index in
Circle()
.foregroundColor(.blue)
.frame(width: 50, height: 50)
.frame(maxWidth: .infinity)
.overlay {
GeometryReader { proxy in
ZStack {
if (index / columns) > 0 {
Rectangle()
.fill(.orange)
.frame(width: 2)
.offset(y: -(proxy.size.height / 2) - (spacing / 2))
}
if !index.isMultiple(of: columns) {
Rectangle()
.fill(.red)
.frame(height: 2)
.offset(x: -(proxy.size.width / 2) - (spacing / 2))
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
}
.padding(10)
}
}
}