Swift 中可拖动连接的 UIView

2023-12-27

我正在尝试创建一些通过线连接的可拖动 UIView。见下图:

我可以通过创建一个 UIView 子类的类并覆盖绘制函数来创建可拖动的圆圈

override func draw(_ rect: CGRect) {
    let path = UIBezierPath(ovalIn: rect)
    let circleColor:UIColor

    switch group {
    case .forehead:
        circleColor = UIColor.red
    case .crowsFeetRightEye:
        circleColor = UIColor.green
    case .crowsFeetLeftEye:
        circleColor = UIColor.blue
    }

    circleColor.setFill()
    path.fill()
}

然后添加一个用于拖动的平移手势识别器

func initGestureRecognizers() {
    let panGR = UIPanGestureRecognizer(target: self, action: #selector(DragPoint.didPan(panGR:)))
    addGestureRecognizer(panGR)
}

@objc func didPan(panGR: UIPanGestureRecognizer) {

    if panGR.state == .changed {
        self.superview!.bringSubview(toFront: self)
        let translation = panGR.translation(in: self)

        self.center.x += translation.x
        self.center.y += translation.y

        panGR.setTranslation(CGPoint.zero, in: self)

    }

}

然而,我完全困惑于如何处理连接线以及在拖动时将起点/终点设置为其相应的圆。有谁可以帮助我或指出正确的方向吗?


你想使用CAShapeLayers with UIBezierpaths 在圆圈之间绘制线条,然后在用户移动视图时更改路径。

这是一个展示实现的操场。您可以将其复制并粘贴到游乐场中以查看其实际效果。

//: A UIKit based Playground for presenting user interface

import UIKit
import PlaygroundSupport

class CircleView : UIView {

    var outGoingLine : CAShapeLayer?
    var inComingLine : CAShapeLayer?
    var inComingCircle : CircleView?
    var outGoingCircle : CircleView?

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.layer.cornerRadius = self.frame.size.width / 2
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func lineTo(circle: CircleView) -> CAShapeLayer {
        let path = UIBezierPath()
        path.move(to: self.center)
        path.addLine(to: circle.center)

        let line = CAShapeLayer()
        line.path = path.cgPath
        line.lineWidth = 5
        line.strokeColor = UIColor.red.cgColor
        circle.inComingLine = line
        outGoingLine = line
        outGoingCircle = circle
        circle.inComingCircle = self
        return line
    }
}

class MyViewController : UIViewController {

    let circle1 = CircleView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
    let circle2 = CircleView(frame: CGRect(x: 100, y: 200, width: 50, height: 50))
    let circle3 = CircleView(frame: CGRect(x: 100, y: 300, width: 50, height: 50))
    let circle4 = CircleView(frame: CGRect(x: 100, y: 400, width: 50, height: 50))

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white
        self.view = view

        circle1.backgroundColor = .red
        view.addSubview(circle1)

        circle2.backgroundColor = .red
        view.addSubview(circle2)

        circle3.backgroundColor = .red
        view.addSubview(circle3)

        circle4.backgroundColor = .red
        view.addSubview(circle4)

        circle1.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:))))

        circle2.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:))))

        circle3.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:))))

        circle4.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:))))

        view.layer.addSublayer(circle1.lineTo(circle: circle2))
        view.layer.addSublayer(circle2.lineTo(circle: circle3))
        view.layer.addSublayer(circle3.lineTo(circle: circle4))
    }

    @objc func didPan(gesture: UIPanGestureRecognizer) {
        guard let circle = gesture.view as? CircleView else {
            return
        }
        if (gesture.state == .began) {
            circle.center = gesture.location(in: self.view)
        }
        let newCenter: CGPoint = gesture.location(in: self.view)
        let dX = newCenter.x - circle.center.x
        let dY = newCenter.y - circle.center.y
        circle.center = CGPoint(x: circle.center.x + dX, y: circle.center.y + dY)


        if let outGoingCircle = circle.outGoingCircle, let line = circle.outGoingLine, let path = circle.outGoingLine?.path {

            let newPath = UIBezierPath(cgPath: path)
            newPath.removeAllPoints()
            newPath.move(to: circle.center)
            newPath.addLine(to: outGoingCircle.center)
            line.path = newPath.cgPath
        }

        if let inComingCircle = circle.inComingCircle, let line = circle.inComingLine, let path = circle.inComingLine?.path {

            let newPath = UIBezierPath(cgPath: path)
            newPath.removeAllPoints()
            newPath.move(to: inComingCircle.center)
            newPath.addLine(to: circle.center)
            line.path = newPath.cgPath
        }
    }
}

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
PlaygroundPage.current.needsIndefiniteExecution = true
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Swift 中可拖动连接的 UIView 的相关文章

随机推荐

  • 访问shared_ptr数组

    include
  • 从文本文件中提取数据时出现问题

    我是 python 新手 我想从这种格式中提取数据 FBpp0143497 5 151 5 157 PF00339 22 Arrestin N Domain 1 135 149 83 4 1 1e 23 1 CL0135 FBpp01434
  • Javascript 获取表单数组值

    我设置了一个表单 其中有一个添加按钮 以便我的用户可以一次向该网站提交多人 首先 表单有一个输入来填写人员姓名 如下例所示
  • 计算每列中小于 x 的元素数量

    我有一个如下所示的数据框 我试图计算每列中小于 2 0 的元素数量 然后我将在条形图中可视化结果 我使用列表和循环来做到这一点 但我想知道是否有一种 熊猫方式 可以快速做到这一点 x for i in range 6 x append df
  • F# 使用累加器,仍然出现堆栈溢出异常

    在下面的函数中 我尝试通过使用累加器来设置尾递归 但是 我遇到了堆栈溢出异常 这让我相信我设置函数的方式没有正确启用尾递归 F attempting to make a tail recursive call via accumulator
  • 创建表达式树以访问通用类型的属性

    我需要编写一个泛型方法 它采用泛型类型的实例和字符串格式的属性名称 并返回一个表达式树 我需要转换一个简单的 lambda 表达式 a gt a SomePropertyName where a是泛型类型将拥有财产按名字SomeProper
  • “无法在此文件中预览 - 无法启动 [应用程序名称]”

    我正在尝试在 Xcode 11 和 macOS Catalina 中使用 swiftUI 但 创建新项目后 会出现一个样板代码 其中包含文本 hello World 我想在代码旁边的画布中实时预览这个简单的欢迎消息 当我点击恢复按钮时 它说
  • 优雅的蟒蛇? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • JS:不确定函数返回的原因

    确实是新手问题 但为什么我的函数返回 1 该函数确实有匹配项 那么为什么该函数返回 1 而不是索引值 如果我删除 else 语句 它将返回正确的输出 let array 1 2 3 4 5 6 7 8 9 let target 5 func
  • Powershell 在过去 6 个月内禁用 AD 用户?

    如何获取过去 6 个月内禁用的 AD 用户以及禁用时的时间戳 以 dd MM yyyy 格式作为 CSV 文件 就像使用这个 Powershellhttps learn microsoft com en us powershell modu
  • PHP 会话注销错误

    许多人可能会使用 PHP mySQL 函数作为网站的登录部分 我正在尝试使用这段代码 在每个内容页面上 检查是否已登录 在每个内容页面的标题中 Page Content Here 登录脚本 由我的 main login php 页面引用
  • 在 onDraw() 中保存画布;

    我试图将 Canvas 对象保存在 onDraw 方法中 这是因为我在 onDraw 方法中使用了 foreach 循环 结果是 canvas DrawText 文本项 x y textpaint 我必须这样做 因为我在遮罩区域周围绘制文本
  • Java 8 Streams 中的并行性和 Flatmap

    考虑以下示例 IntStream of 1 1 parallel flatMap i gt IntStream range 0 1000 parallel forEach System out println 我是否将内部标志设置为并行有关
  • C 语言的接口

    我正在设计一个应用程序并遇到一个实施问题 我有以下结构定义 app h struct application t void run application struct application t void stop application
  • 如何确定 ActiveRecord 中的关联是否发生变化?

    ActiveRecord 提供更改跟踪 其中调用 name changed 返回 true false 取决于是否name属性在模型加载时和现在之间发生了变化 协会有类似的东西吗 我专门寻找 has many 关联 但所有关联类型都会有用
  • 对于每种变量类型,将所有行除以一个参考行

    n c rep A 3 rep B 4 rep C 3 s 1 10 b 5 14 c 20 29 df data frame n s b c 我想要每个第一行 对于每种类型的变量n A B C 作为参考行 我想将每一行划分为每个类别n除以
  • 为什么方法调用会将非易失性变量的值刷新到主线程?

    为什么吸气剂Val碰巧模拟了该领域的波动性val 我假设利用方法调用并不是保持变量易失性的可靠方法 要尝试一下 请构建发布并直接执行 无需调试器 class Program private int val 0 public int Val
  • JSON.parse:意外的数据结束错误

    对于下面的代码我收到此错误 JSON parse 数据意外结束 在行 var data JSON parse json 使用的代码是 document ready function button1 click function post s
  • Python中定义一个数据类的正确方法是什么,该数据类同时具有自动生成的 __init__ 和来自值字典的附加 init2

    在 Python 中 我有一个包含十几个成员的数据类 我用它来创建我发布的字典进入ElasticSearch 现在我想要一个字典来自弹性搜索并用它来初始化数据类 Since Python 不允许创建具有不同签名的第二个 init 我不想手动
  • Swift 中可拖动连接的 UIView

    我正在尝试创建一些通过线连接的可拖动 UIView 见下图 我可以通过创建一个 UIView 子类的类并覆盖绘制函数来创建可拖动的圆圈 override func draw rect CGRect let path UIBezierPath