You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift实现带10个以上控制点可调整为任意形状的圆形绘制方案

需求说明

我想要实现具备10个以上控制点、可调整为任意形状的圆形效果,需使用Swift语言完成开发。我已经找到Javascript实现的类似效果,但不知道如何迁移到Swift平台开发。我也尝试过使用Bezier path进行实现,现有代码如下,但不知道如何完成后续开发:

现有代码

自定义贝塞尔路径视图类

class MyBezierPathView: UIView {
    
    private var path: UIBezierPath?
    
    // 起点
    var startP = CGPoint.zero
    
    // 终点
    var endP = CGPoint.zero
    
    // 控制点
    var controlP = CGPoint.zero
    
    var pathColor: UIColor?
     
    var pathWidth: CGFloat = 0.0
    
    // 当前被触摸的点标识
    private var currentTouchP = 0
    
    // 初始化
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    // 绘制贝塞尔路径
    override func draw(_ rect: CGRect) {
        path = UIBezierPath()
        path?.move(to: startP)

        path?.addQuadCurve(to: endP, controlPoint: controlP)

        path?.lineWidth = pathWidth
        pathColor?.setStroke()
        path?.stroke()

        path = UIBezierPath()

        path?.lineWidth = 1
        UIColor.gray.setStroke()
        
        let lengths: [CGFloat] = [5]
        path?.setLineDash(lengths, count: 1, phase: 1)
        path?.move(to: controlP)
        path?.addLine(to: startP)
        path?.stroke()

        path?.move(to: controlP)
        path?.addLine(to: endP)
        path?.stroke()
    
        
        path = UIBezierPath(arcCenter: startP, radius: 4, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        UIColor.black.setStroke()
        path?.fill()
        
        path = UIBezierPath(arcCenter: endP, radius: 4, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        UIColor.black.setStroke()
        path?.fill()
        
        path = UIBezierPath(arcCenter: controlP, radius: 3, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        path?.lineWidth = 2
        UIColor.black.setStroke()
        path?.stroke()
        
        let startMsgRect = CGRect(x: startP.x + 8, y: startP.y - 7, width: 50, height: 20)
        "start point".draw(in: startMsgRect, withAttributes: nil)

        let endMsgRect = CGRect(x: endP.x + 8, y: endP.y - 7, width: 50, height: 20)
        "end point".draw(in: endMsgRect, withAttributes: nil)

        let control1MsgRect = CGRect(x: controlP.x + 8, y: controlP.y - 7, width: 50, height: 20)
        "control point".draw(in: control1MsgRect, withAttributes: nil)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let startPoint = touches.first?.location(in: self)     
        
        let startR = CGRect(x: startP.x - 4, y: startP.y - 4, width: 80, height: 80)
        let endR = CGRect(x: endP.x - 4, y: endP.y - 4, width: 80, height: 80)
        let controlR = CGRect(x: controlP.x - 4, y: controlP.y - 4, width: 80, height: 80)
        
        guard let startPoint = startPoint else {
            print("startPoint is nil.")
            return
        }
        
        if startR.contains(startPoint) {
            currentTouchP = 1
        } else if endR.contains(startPoint) {
            currentTouchP = 2
        } else if controlR.contains(startPoint) {
            currentTouchP = 3
        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        var touchPoint = touches.first?.location(in: self)
        
        if touchPoint!.x < 0 {
           touchPoint!.x = 0
        }
        if touchPoint!.x > bounds.size.width {
            touchPoint!.x = bounds.size.width
        }
        if touchPoint!.y < 0 {
            touchPoint!.y = 0
        }
        if touchPoint!.y > bounds.size.height {
            touchPoint!.y = bounds.size.height
        }
        
        switch currentTouchP {
        case 1:
            startP = touchPoint!
        case 2:
            endP = touchPoint!
        case 3:
            controlP = touchPoint!
        default:
            break
        }
               
        setNeedsDisplay()
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        currentTouchP = 0
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        touchesEnded(touches, with: event)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

视图控制器类

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let frame = CGRect(x: 0, y: 0, width: view.bounds.size.width, height: view.bounds.size.height)
        let pathView = MyBezierPathView(frame: frame)
        
        pathView.startP = CGPoint(x: 110, y: 150)
        pathView.endP = CGPoint(x: 258.47, y: 211.53)
        pathView.controlP = CGPoint(x: 196.94, y: 150)
        pathView.pathColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)
        pathView.pathWidth = 2

        pathView.backgroundColor = UIColor.white
        pathView.layer.borderWidth = 1

        view.addSubview(pathView)
    }
}

内容的提问来源于stack exchange,提问作者Nick Wen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 13:18:04