ios - Swift - 使用 Xcode 中的蓝色拖动线在两个标签或按钮之间创建连接
问题描述
我已经设法像在 Xcode 中一样创建了蓝线,但是您如何识别何时将线的终点拖动并释放到其他标签或按钮上方以便建立连接?
代码
class ViewController: UIViewController {
@IBOutlet var label: UILabel!
@IBOutlet var label2: UILabel!
private lazy var lineShape: CAShapeLayer = {
var color = hexStringToUIColor(hex: "#5DBCD2")
let lineShape = CAShapeLayer()
lineShape.strokeColor = UIColor.blue.cgColor
lineShape.fillColor = color.cgColor
lineShape.lineWidth = 2.0
return lineShape
}()
private var panGestureStartPoint: CGPoint = .zero
private lazy var panRecognizer: UIPanGestureRecognizer = {
return UIPanGestureRecognizer(target: self, action: #selector(panGestureCalled(_:)))
}()
override func viewDidLoad() {
super.viewDidLoad()
self.label.addGestureRecognizer(panRecognizer)
}
@objc func panGestureCalled(_: UIPanGestureRecognizer) {
let currentPanPoint = panRecognizer.location(in: self.view)
switch panRecognizer.state {
case .began:
panGestureStartPoint = currentPanPoint
self.view.layer.addSublayer(lineShape)
case .changed:
let linePath = UIBezierPath()
linePath.move(to: panGestureStartPoint)
linePath.addLine(to: currentPanPoint)
lineShape.path = linePath.cgPath
lineShape.path = CGPath.barbell(from: panGestureStartPoint, to: currentPanPoint, barThickness: 2.0, bellRadius: 6.0)
case .ended:
lineShape.path = nil
lineShape.removeFromSuperlayer()
default: break
}
}
func hexStringToUIColor (hex:String) -> UIColor {
var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
if (cString.hasPrefix("#")) {
cString.remove(at: cString.startIndex)
}
if ((cString.count) != 6) {
return UIColor.gray
}
var rgbValue:UInt32 = 0
Scanner(string: cString).scanHexInt32(&rgbValue)
return UIColor(
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
}
extension CGPath {
class func barbell(from start: CGPoint, to end: CGPoint, barThickness proposedBarThickness: CGFloat, bellRadius proposedBellRadius: CGFloat) -> CGPath {
let barThickness = max(0, proposedBarThickness)
let bellRadius = max(barThickness / 2, proposedBellRadius)
let vector = CGPoint(x: end.x - start.x, y: end.y - start.y)
let length = hypot(vector.x, vector.y)
if length == 0 {
return CGPath(ellipseIn: CGRect(origin: start, size: .zero).insetBy(dx: -bellRadius, dy: -bellRadius), transform: nil)
}
var yOffset = barThickness / 2
var xOffset = sqrt(bellRadius * bellRadius - yOffset * yOffset)
let halfLength = length / 2
if xOffset > halfLength {
xOffset = halfLength
yOffset = sqrt(bellRadius * bellRadius - xOffset * xOffset)
}
let jointRadians = asin(yOffset / bellRadius)
let path = CGMutablePath()
path.addArc(center: .zero, radius: bellRadius, startAngle: jointRadians, endAngle: -jointRadians, clockwise: false)
path.addArc(center: CGPoint(x: length, y: 0), radius: bellRadius, startAngle: .pi + jointRadians, endAngle: .pi - jointRadians, clockwise: false)
path.closeSubpath()
let unitVector = CGPoint(x: vector.x / length, y: vector.y / length)
var transform = CGAffineTransform(a: unitVector.x, b: unitVector.y, c: -unitVector.y, d: unitVector.x, tx: start.x, ty: start.y)
return path.copy(using: &transform)!
}
}
上面的代码复制了 gif 动画。如果有帮助,我使用下面的问题来创建它。
画一条可以像 Swift 中的 Xcode 助手编辑器一样伸展的线
我认为答案就在上面的问题中,但我无法理解它,因为它适用于 MacOS 而不是 iOS。
任何帮助深表感谢
更新
根据给出的非常好的答案,我在 AdditionalPanGesture() 中添加了以下代码。这不是代码的完成版本。
case .changed:
let linePath = UIBezierPath()
linePath.move(to: panGestureStartPoint)
linePath.addLine(to: currentPanPoint)
lineShape.path = linePath.cgPath
lineShape.path = CGPath.barbell(from: panGestureStartPoint, to: currentPanPoint, barThickness: 2.0, bellRadius: 6.0)
let labels = [label2, label3]
for labelPoint in labels {
let point = panRecognizer.location(in: labelPoint)
if labelPoint!.layer.contains(point){
labelPoint!.layer.borderWidth = 10
labelPoint!.layer.borderColor = UIColor.green.cgColor
} else {
labelPoint!.layer.borderWidth = 0
labelPoint!.layer.borderColor = UIColor.clear.cgColor
}
}
case .ended:
let labels = [label2, label3]
for labelPoint in labels {
let point = panRecognizer.location(in: labelPoint)
if labelPoint!.layer.contains(point){
labelPoint!.layer.borderWidth = 2
labelPoint!.layer.borderColor = UIColor.green.cgColor
} else {
lineShape.path = nil
lineShape.removeFromSuperlayer()
labelPoint!.layer.borderWidth = 0
labelPoint!.layer.borderColor = UIColor.clear.cgColor
}
}
如果有更好的方法,我会喜欢输入。谢谢!
解决方案
CGRect
实现了一个contains(_:)
方法,可以让你判断一个矩形是否包含一个点。
您需要添加逻辑以确保您的起始拖动点在按钮内,并且您的结束点在另一个按钮内,使用这些按钮的框架作为您询问的矩形。如果您发现用户已从一个按钮拖动到下一个按钮,那么您的.ended
处理程序(如 M Abubaker Majeed 在他/她的评论中所提到的)需要检测到这一点并以某种方式添加一个永久链接。你如何做到这一点取决于你。您可以使用单独的形状层来保存要保留的线条,以及连接线的起点/终点数组,或各种其他方法。
推荐阅读
- c# - 从 .NET MVC 中的 POST 操作方法重定向到 GET 操作方法
- sql - postgresql 与 ms sql 中的 cte
- camunda - 如何在任务分配中向组发送邮件
- windows - 你如何关闭 Mingw / Git Bash“ctrl 左键单击”打开超链接功能?
- algorithm - 大 O 类术语,但保持常量
- git - git describe tags 命令返回未知标签
- python - 将我的 python 应用程序部署到 heroku 时出现此错误,这让我发疯
- python-3.x - 我的 Python 包在某些 Windows 上部分工作
- typescript - 用于在特定嵌套路径中删除对象的类型
- java - 从导入的依赖项覆盖 Gradle 项目配置