Swift UIBezierPath不同坐标起点
Swift UIBezierPath Different Coordinate Starting Point
我找到了一个 UIBezierPath 箭头 。我的目标是向函数 "addArrows" 传递玩家数组列表并在它们之间绘制箭头。这段代码很好用……除非我调用 addArrows(gameScene: scene, from: [1, 3], to: [1, 3])
或 addArrows(gameScene: scene, from: [2, 3], to: [2, 3])
。这应该在玩家 1 和 3(第一次调用)以及玩家 2 和 3(第二次调用)之间创建箭头。但是,它在玩家 2 和 3(第一次通话)和玩家 1 和 3(第二次通话)之间创建箭头。我已将 print 语句添加到 "addArrows" 函数,它会正确返回玩家的位置(位于我的常量结构中)。如果输入 [1, 2, 3] 和 [1, 2, 3] 或 [1, 2] 和 [1, 2] 的数组,它就会工作。这里究竟发生了什么?
struct K {
static let stackPositions = [
// Player 1
1: CGPoint(x: (_screenW / 2), y: cardHeightConstant + cardHeight + 60),
// Player 2
2: CGPoint(x: (_screenW / 2), y: _screenH - cardHeightConstant - cardHeight - 60),
// Player 3
3: CGPoint(x: 30, y: _screenH / 2),
// Player 4
4: CGPoint(x: _screenW - 30, y: _screenH / 2)
]
}
//
extension UIBezierPath {
func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
self.move(to: start)
self.addLine(to: end)
let startEndAngle = atan((end.y - start.y) / (end.x - start.x)) + ((end.x - start.x) < 0 ? CGFloat(Double.pi) : 0)
let arrowLine1 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
let arrowLine2 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))
self.addLine(to: arrowLine1)
self.move(to: end)
self.addLine(to: arrowLine2)
}
}
func addArrows(gameScene: GameScene, from: [Int], to: [Int]) {
for fromPlayerNumber in from {
for toPlayerNumber in to {
guard let fromPos = K.stackPositions[fromPlayerNumber] else { return }
guard let toPos = K.stackPositions[toPlayerNumber] else { return }
if fromPos == toPos { continue }
print(fromPos.y)
print(toPos.y)
let arrow = UIBezierPath()
arrow.addArrow(start: CGPoint(x: fromPos.x, y: fromPos.y),
end: CGPoint(x: toPos.x, y: toPos.y),
pointerLineLength: 7,
arrowAngle: CGFloat(Double.pi / 4))
let arrowLayer = CAShapeLayer()
arrowLayer.strokeColor = UIColor.black.cgColor
arrowLayer.lineWidth = 1.0
arrowLayer.path = arrow.cgPath
arrowLayer.fillColor = UIColor.clear.cgColor
arrowLayer.lineJoin = CAShapeLayerLineJoin.round
arrowLayer.lineCap = CAShapeLayerLineCap.round
gameScene.view?.layer.addSublayer(arrowLayer)
}
}
}
编辑
我已将 "From" 位置强制设为 CGPoint(x: 0, y: 0)
。这是从屏幕左上角开始的箭头,而不是从屏幕左下角开始的所有其他参考。因此,看起来箭头使用的锚点与我场景中的其他对象不同。我尝试设置 arrowLayer.anchorPoint = CGPoint(x: 0, y: 0)
,但没有用...
在这里,坐标系参考屏幕的左上角而不是左下角。为了解决这个问题,我需要做的就是 "invert" 开始和结束 y 点的 y 坐标。修正如下:
func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
// Adjust Y Directions Here! //
let adjStart = CGPoint(x: start.x, y: K._screenH - start.y)
let adjEnd = CGPoint(x: end.x, y: K._screenH - end.y)
self.move(to: adjStart)
self.addLine(to: adjEnd)
let startEndAngle = atan((adjEnd.y - adjStart.y) / (adjEnd.x - adjStart.x)) + ((adjEnd.x - adjStart.x) < 0 ? CGFloat(Double.pi) : 0)
let arrowLine1 = CGPoint(x: adjEnd.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle),
y: adjEnd.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
let arrowLine2 = CGPoint(x: adjEnd.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle),
y: adjEnd.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))
self.addLine(to: arrowLine1)
self.move(to: adjEnd)
self.addLine(to: arrowLine2)
}
我找到了一个 UIBezierPath 箭头 addArrows(gameScene: scene, from: [1, 3], to: [1, 3])
或 addArrows(gameScene: scene, from: [2, 3], to: [2, 3])
。这应该在玩家 1 和 3(第一次调用)以及玩家 2 和 3(第二次调用)之间创建箭头。但是,它在玩家 2 和 3(第一次通话)和玩家 1 和 3(第二次通话)之间创建箭头。我已将 print 语句添加到 "addArrows" 函数,它会正确返回玩家的位置(位于我的常量结构中)。如果输入 [1, 2, 3] 和 [1, 2, 3] 或 [1, 2] 和 [1, 2] 的数组,它就会工作。这里究竟发生了什么?
struct K {
static let stackPositions = [
// Player 1
1: CGPoint(x: (_screenW / 2), y: cardHeightConstant + cardHeight + 60),
// Player 2
2: CGPoint(x: (_screenW / 2), y: _screenH - cardHeightConstant - cardHeight - 60),
// Player 3
3: CGPoint(x: 30, y: _screenH / 2),
// Player 4
4: CGPoint(x: _screenW - 30, y: _screenH / 2)
]
}
//
extension UIBezierPath {
func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
self.move(to: start)
self.addLine(to: end)
let startEndAngle = atan((end.y - start.y) / (end.x - start.x)) + ((end.x - start.x) < 0 ? CGFloat(Double.pi) : 0)
let arrowLine1 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
let arrowLine2 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))
self.addLine(to: arrowLine1)
self.move(to: end)
self.addLine(to: arrowLine2)
}
}
func addArrows(gameScene: GameScene, from: [Int], to: [Int]) {
for fromPlayerNumber in from {
for toPlayerNumber in to {
guard let fromPos = K.stackPositions[fromPlayerNumber] else { return }
guard let toPos = K.stackPositions[toPlayerNumber] else { return }
if fromPos == toPos { continue }
print(fromPos.y)
print(toPos.y)
let arrow = UIBezierPath()
arrow.addArrow(start: CGPoint(x: fromPos.x, y: fromPos.y),
end: CGPoint(x: toPos.x, y: toPos.y),
pointerLineLength: 7,
arrowAngle: CGFloat(Double.pi / 4))
let arrowLayer = CAShapeLayer()
arrowLayer.strokeColor = UIColor.black.cgColor
arrowLayer.lineWidth = 1.0
arrowLayer.path = arrow.cgPath
arrowLayer.fillColor = UIColor.clear.cgColor
arrowLayer.lineJoin = CAShapeLayerLineJoin.round
arrowLayer.lineCap = CAShapeLayerLineCap.round
gameScene.view?.layer.addSublayer(arrowLayer)
}
}
}
编辑
我已将 "From" 位置强制设为 CGPoint(x: 0, y: 0)
。这是从屏幕左上角开始的箭头,而不是从屏幕左下角开始的所有其他参考。因此,看起来箭头使用的锚点与我场景中的其他对象不同。我尝试设置 arrowLayer.anchorPoint = CGPoint(x: 0, y: 0)
,但没有用...
在这里,坐标系参考屏幕的左上角而不是左下角。为了解决这个问题,我需要做的就是 "invert" 开始和结束 y 点的 y 坐标。修正如下:
func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
// Adjust Y Directions Here! //
let adjStart = CGPoint(x: start.x, y: K._screenH - start.y)
let adjEnd = CGPoint(x: end.x, y: K._screenH - end.y)
self.move(to: adjStart)
self.addLine(to: adjEnd)
let startEndAngle = atan((adjEnd.y - adjStart.y) / (adjEnd.x - adjStart.x)) + ((adjEnd.x - adjStart.x) < 0 ? CGFloat(Double.pi) : 0)
let arrowLine1 = CGPoint(x: adjEnd.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle),
y: adjEnd.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
let arrowLine2 = CGPoint(x: adjEnd.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle),
y: adjEnd.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))
self.addLine(to: arrowLine1)
self.move(to: adjEnd)
self.addLine(to: arrowLine2)
}