如何修复NSBezierPath的便利初始化和闭包扩展,以使用Swift 5.0中失败的CGPath?

如何修复NSBezierPath的便利初始化和闭包扩展,以使用Swift 5.0中失败的CGPath?,swift,macos,swift5,cgpath,nsbezierpath,Swift,Macos,Swift5,Cgpath,Nsbezierpath,以下内联是我在某处获得的一种方法,用于使用方便的初始值设定项扩展NSBezierPath,该初始值设定项将CGPath作为参数,使其行为更像iOS上的UIBezierPath 它以前可以工作,但当我尝试在Swift 5上编译它(几年后)时,我得到以下编译时错误: 捕获上下文的闭包不能形成C函数指针 我如何解决这个问题 convenience init(path : CGPath) { path.apply(info: nil, function: { (_, elementPointer

以下内联是我在某处获得的一种方法,用于使用方便的初始值设定项扩展
NSBezierPath
,该初始值设定项将
CGPath
作为参数,使其行为更像iOS上的
UIBezierPath

它以前可以工作,但当我尝试在Swift 5上编译它(几年后)时,我得到以下编译时错误:

捕获上下文的闭包不能形成C函数指针

我如何解决这个问题

convenience init(path : CGPath) {
    path.apply(info: nil, function: { (_, elementPointer) in
        let element = elementPointer.pointee
        switch element.type {
        case .moveToPoint:
            let points = Array(UnsafeBufferPointer(start: element.points, count: 1))
            self.move(to: points[0])
            break
        case .addLineToPoint:
            let points = Array(UnsafeBufferPointer(start: element.points, count: 1))
            self.line(to: points[0])
            break
        case .addQuadCurveToPoint:
            let points = Array(UnsafeBufferPointer(start: element.points, count: 2))
            let qp0 = self.currentPoint
            let qp1 = points[0]
            let qp2 = points[1]
            let m = CGFloat(2.0 / 3.0)
            var cp1 = NSPoint()
            var cp2 = NSPoint()
            cp1.x = (qp0.x + ((qp1.x - qp0.x) * m))
            cp1.y = (qp0.y + ((qp1.y - qp0.y) * m))
            cp2.x = (qp2.x + ((qp1.x - qp2.x) * m))
            cp2.y = (qp2.y + ((qp1.y - qp2.y) * m))
            self.curve(to: qp2, controlPoint1:cp1, controlPoint2:cp2)
        case .addCurveToPoint:
            let points = Array(UnsafeBufferPointer(start: element.points, count: 3))
            self.curve(to:points[2], controlPoint1:points[0], controlPoint2:points[1])
            break
        case .closeSubpath:
            self.close()
        @unknown default:
            break;
        }
    })
}

我建议使用
path.applyWithBlock
。我也会丢失所有那些不简洁的
break
语句,直接访问
元素.points

也许是这样的:

convenience init(path: CGPath) {
    self.init()

    path.applyWithBlock { elementPointer in
        let element = elementPointer.pointee
        switch element.type {
        case .moveToPoint:
            move(to: element.points[0])

        case .addLineToPoint:
            line(to: element.points[0])

        case .addQuadCurveToPoint:
            let qp0 = self.currentPoint
            let qp1 = element.points[0]
            let qp2 = element.points[1]
            let m = CGFloat(2.0 / 3.0)
            let cp1 = NSPoint(x: qp0.x + ((qp1.x - qp0.x) * m),
                              y: qp0.y + ((qp1.y - qp0.y) * m))
            let cp2 = NSPoint(x: qp2.x + ((qp1.x - qp2.x) * m),
                              y: qp2.y + ((qp1.y - qp2.y) * m))
            curve(to: qp2, controlPoint1: cp1, controlPoint2: cp2)

        case .addCurveToPoint:
            curve(to: element.points[2], controlPoint1: element.points[0], controlPoint2: element.points[1])

        case .closeSubpath:
            close()

        @unknown default:
            break
        }
    }
}
作为概念证明,我使用所有不同的元素类型创建了一个
CGPath
,并使用上述元素类型创建了一个
NSBezierPath
。然后我用它们各自的API对它们进行了笔划(深蓝色笔划中的
NSBezierPath
,顶部白色笔划中的
CGPath
)。这是一个快速的经验验证,转换逻辑产生了等效的路径:


这是对问题的补充“回答”。。。这是@Rob修复的问题中便利方法的补充功能,它将
NSBezierPath
转换为
CGPath
。例如,这两种方法可以方便地在macOS和iOS之间进行移植,并且可以更轻松地使用NSBezierPath和其他CoreGraphics代码

private func transformToCGPath() -> CGPath {

    let path = CGMutablePath()
    let points = UnsafeMutablePointer<NSPoint>.allocate(capacity: 3)
    let numElements = self.elementCount

    if numElements > 0 {

        var didClosePath = true

        for index in 0..<numElements {

            let pathType = self.element(at: index, associatedPoints: points)

            switch pathType {
            case .moveTo:
                path.move(to: CGPoint(x: points[0].x, y: points[0].y))
            case .lineTo:
                path.addLine(to: CGPoint(x: points[0].x, y: points[0].y))
                didClosePath = false
            case .curveTo:
                path.addCurve(to: CGPoint(x: points[0].x, y: points[0].y), control1: CGPoint(x: points[1].x, y: points[1].y), control2: CGPoint(x: points[2].x, y: points[2].y))
                didClosePath = false
            case .closePath:
                path.closeSubpath()
                didClosePath = true
            @unknown default:
                print("Warning! New NSBezierPath.ElementTypes() added, may affect transformToCGPath!")
            }
        }

        if !didClosePath { path.closeSubpath() }
    }
    points.deallocate()
    return path
}
private func transformToCGPath()->CGPath{
let path=CGMutablePath()
let points=unsafemeutablepointer.allocate(容量:3)
让numElements=self.elementCount
如果数值>0{
var didClosePath=true

对于0中的索引。我不知道您是在哪种情况下编写此代码的。假设它是
NSView
的子类,我从未见过有人在其中使用闭包的情况。@Rob,我想知道。我已经开始这样做了,但我会修复它。谢谢。谢谢@Rob!我将查找此代码并尝试对其进行更多分析。您能向我解释一下闭包是什么吗ror真的很了解,applyWithBlock有多不同,为什么它会修复它?我想知道为什么原始解决方案会使用基于不安全指针的数组。你认为这与早期Swift版本的数组有关吗?看起来像
CGPath。applyWithBlock()
更快速?看起来像
.apply()
方法进一步推动了本机C机制,不必要地使其复杂化。因此我想我理解您的基本做法。但不太清楚编译器在抱怨什么,也不太清楚原始作者为什么使用Array()类。再次感谢!非常好。我做了大量快速但日常的工作(Linux/C/虚拟化)一直让我远离,我忘记了一些事情。对UnsafePointer的回顾很有帮助。所以我认为答案现在对我来说很清楚。节省了我很多时间,帮助我更快地戴上雨燕帽。非常感谢。应用可以追溯到OS 10.2,而
应用程序WithBlock
是在10.13中引入的,毫无疑问是为了避免旧语法的局限性。显然,块到C函数的桥接随着时间的推移而不断发展。重新使用
数组
,这可能是作者不熟悉使用指针的原因。从
break
语句的存在来看,这些语句从来都不是必需的,这意味着有人不熟悉Sw伊芙:我不确定我是否会花太多时间去猜测原作者的理论基础。谢谢。这是一个很好的奥秘,几乎可以结束这个循环。一路上都有原始的教育经验。