我正在使用iOS应用程序中的UIBezierPath()和DragGesture在画布上绘制线条.为此,我收集手势点并将它们添加到路径中,该路径是UIBezierPath的对象.然后,我设置了特定的线宽,并在图形上下文中对路径进行了描边.

However, I'm facing an issue when trying to draw a circle shape quickly. Instead of getting a smooth curve, the connecting points of the path create corners, resulting in an angular shape rather than a circular one. This issue specifically occurs when drawing circles rapidly, and it affects the overall quality of my drawings. For more visualisation look here: here:

func draw(size: CGSize, drawingImage: UIImage, lines: [Line], path: UIBezierPath) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(size, false, 1.0)
    drawingImage.draw(at: .zero)
    
    let pathCount = lines.count
    let pointCount = lines[pathCount-1].line.count
    let point = lines[pathCount-1].line[pointCount-1]
    
    path.addLine(to: point.toCGPoint())
    
    let context = UIGraphicsGetCurrentContext()!
    context.addPath(path.cgPath)
    context.setStrokeColor(Color.white.cgColor)
    
    path.lineWidth = 50
    path.lineCapStyle = .round
    path.lineJoinStyle = .round
    
    path.stroke()

    let myImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return myImage
}

我正在寻求指导,如何确保路径的连接点创建平滑的曲线,即使在快速绘制圆形时也是如此.如有任何见解或建议,我们将不胜感激.谢谢!

推荐答案

您将需要添加曲线到您的贝塞尔曲线,而不是线.为了使曲线看起来很好,您需要计算曲线上的连续点之间的适当控制点.

我正在改编来自this answer的代码.请注意,本例接受CGPoints数组,而不是您引用的类(Line).

像这样执行它

let path = quadCurvedPath(points) //points is [CGPoint]

并如下计算UIBezierPath

//adapted from https://stackoverflow.com/a/40203583/3680644
func quadCurvedPath(_ points: [CGPoint]) -> UIBezierPath {
    let path = UIBezierPath()

    var p1 = points[0]
    path.move(to: p1)

    if (points.count == 2) {
        path.addLine(to: points[1])
        return path
    }

    var oldControlP: CGPoint?

    for i in 1..<points.count {
        let p2 = points[i]
        var p3: CGPoint?
        if i < points.count - 1 {
            p3 = points[i+1]
        }

        let newControlP = controlPointForPoints(p1: p1, p2: p2, next: p3)

        path.addCurve(to: p2, controlPoint1: oldControlP ?? p1, controlPoint2: newControlP ?? p2)

        p1 = p2
        oldControlP = antipodalFor(point: newControlP, center: p2)
    }
    return path;
}

/// located on the opposite side from the center point
func antipodalFor(point: CGPoint?, center: CGPoint?) -> CGPoint? {
    guard let p1 = point, let center = center else {
        return nil
    }
    let newX = 2 * center.x - p1.x
    let diffY = abs(p1.y - center.y)
    let newY = center.y + diffY * (p1.y < center.y ? 1 : -1)

    return CGPoint(x: newX, y: newY)
}

/// halfway of two points
func midPointForPoints(p1: CGPoint, p2: CGPoint) -> CGPoint {
    return CGPoint(x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2);
}

/// Find controlPoint2 for addCurve
/// - Parameters:
///   - p1: first point of curve
///   - p2: second point of curve whose control point we are looking for
///   - next: predicted next point which will use antipodal control point for finded
func controlPointForPoints(p1: CGPoint, p2: CGPoint, next p3: CGPoint?) -> CGPoint? {
    guard let p3 = p3 else {
        return nil
    }

    let leftMidPoint  = midPointForPoints(p1: p1, p2: p2)
    let rightMidPoint = midPointForPoints(p1: p2, p2: p3)

    var controlPoint = midPointForPoints(p1: leftMidPoint, p2: antipodalFor(point: rightMidPoint, center: p2)!)

    if p1.y.between(a: p2.y, b: controlPoint.y) {
        controlPoint.y = p1.y
    } else if p2.y.between(a: p1.y, b: controlPoint.y) {
        controlPoint.y = p2.y
    }


    let imaginContol = antipodalFor(point: controlPoint, center: p2)!
    if p2.y.between(a: p3.y, b: imaginContol.y) {
        controlPoint.y = p2.y
    }
    if p3.y.between(a: p2.y, b: imaginContol.y) {
        let diffY = abs(p2.y - p3.y)
        controlPoint.y = p2.y + diffY * (p3.y < p2.y ? 1 : -1)
    }

    // make lines easier
    controlPoint.x += (p2.x - p1.x) * 0.1

    return controlPoint
}


extension CGFloat {
    func between(a: CGFloat, b: CGFloat) -> Bool {
        return self >= Swift.min(a, b) && self <= Swift.max(a, b)
    }
}

Swift相关问答推荐

自定义完整屏幕封面动画

是否有一个Kotlin等价的Swift s @ AddendableReport注释'

为什么枚举上的.allCases.forEach不是循环(继续和中断不起作用)?

SWIFT异步/等待,多个监听程序

当计数大于索引时,索引超出范围崩溃

如果通过计时器循环运行,则检索CPU利用率百分比的SWIFT脚本运行良好.似乎在没有计时器的情况下停留在初始百分比上

如何在 Vapor 中制作可选的查询过滤器

关闭 SwiftUI TabView 中的子视图

是否可以利用 Codable 从 Dictionary 初始化符合类型

数组中某些元素的总和

Vapor 4 身份验证问题 [用户未通过身份验证]

按钮图像不会立即刷新

是否可以使任何协议符合 Codable?

如何在 SwiftUI 中正确实现Collection 夹?

不要从 NumberFormatter 获取货币符号

在 Swift 中,你可以用另一个字符串分割一个字符串,而不仅仅是一个字符吗?

如何在 Swift 中遍历 struct 属性?

Swift 中的 CommonHMAC

如何在 Swift 中判断变量的类型

如何在 SwiftUI Segmented Picker 中更改选定的段 colored颜色