Swift: 如何给一个封闭的CGPath加点?

Swift: How to add points to a closed CGPath?

我想让 SKSpriteNodes 沿着字母轮廓移动。我有很多封信,但这里有一个例子:

我想让精灵跟随红线。我发现这个答案大部分涵盖了我的问题:Get path to trace out a character in an iOS UIFont

答案来自这个良好且有效的示例代码:

 let font = UIFont(name: "HelveticaNeue", size: 64)!


var unichars = [UniChar]("P".utf16)
var glyphs = [CGGlyph](count: unichars.count, repeatedValue: 0)
let gotGlyphs = CTFontGetGlyphsForCharacters(font, &unichars, &glyphs, unichars.count)
if gotGlyphs {
    let cgpath = CTFontCreatePathForGlyph(font, glyphs[0], nil)!
    let path = UIBezierPath(CGPath: cgpath)
    print(path)
    XCPlaygroundPage.currentPage.captureValue(path, withIdentifier: "glyph \(glyphs[0])")
}

但是我仍然 运行 遇到了一个问题,因为我的 sprite 没有完成围绕字母的所有字母的完整路径,而是例如"P" 停在这里(并从底部开始):

我尝试向 path 添加一些点,如下所示:

CGPathAddLineToPoint(path, nil, 0, 0)

但结果不起作用可能是因为添加的点在 <Close> 语句之后:

<UIBezierPath: 0x7889ff70; <MoveTo {25.950001, 55.800003}>,
 <LineTo {25.950001, 95.100006}>,
 <LineTo {53.850002, 95.100006}>,
 <QuadCurveTo {71.625, 90.075005} - {66, 95.100006}>,
 <QuadCurveTo {77.25, 75.450005} - {77.25, 85.050003}>,
 <QuadCurveTo {71.625, 60.750004} - {77.25, 65.850006}>,
 <QuadCurveTo {53.850002, 55.800003} - {66, 55.650002}>,
 <Close>,
 <MoveTo {11.700001, 107.10001}>,
 <LineTo {11.700001, 0}>,
 <LineTo {25.950001, 0}>,
 <LineTo {25.950001, 43.800003}>,
 <LineTo {58.650002, 43.800003}>,
 <QuadCurveTo {83.175003, 52.050003} - {74.850006, 43.650002}>,
 <QuadCurveTo {91.5, 75.450005} - {91.5, 60.450001}>,
 <QuadCurveTo {83.175003, 98.775002} - {91.5, 90.450005}>,
 <QuadCurveTo {58.650002, 107.10001} - {74.850006, 107.10001}>,
 <Close>,
 <LineTo {0, 0}>

首先,您需要一个方法来检索 CGPath.

中的所有元素

我已经翻译成Swift这个method(你也可以找到一个如何使用它的例子)。

正如我在您的代码中看到的那样,您还需要知道构成路径的元素类型,但是 CGPathElementUnsafeMutablePointer<CGPoint> 一起使用(可能不太舒服),因此您可以通过创建一个带有两个输出数组的函数来修改我的翻译:

//MARK: - CGPath extensions
extension CGPath {
    func getPathElementsPointsAndTypes() -> ([CGPoint],[CGPathElementType]) {
            var arrayPoints : [CGPoint]! = [CGPoint]()
            var arrayTypes : [CGPathElementType]! = [CGPathElementType]()
            self.forEach { element in
                switch (element.type) {
                case CGPathElementType.MoveToPoint:
                    arrayPoints.append(element.points[0])
                    arrayTypes.append(element.type)
                case .AddLineToPoint:
                    arrayPoints.append(element.points[0])
                    arrayTypes.append(element.type)
                case .AddQuadCurveToPoint:
                    arrayPoints.append(element.points[0])
                    arrayPoints.append(element.points[1])
                    arrayTypes.append(element.type)
                    arrayTypes.append(element.type)
                case .AddCurveToPoint:
                    arrayPoints.append(element.points[0])
                    arrayPoints.append(element.points[1])
                    arrayPoints.append(element.points[2])
                    arrayTypes.append(element.type)
                    arrayTypes.append(element.type)
                    arrayTypes.append(element.type)
                default: break
                }
            }
            return (arrayPoints,arrayTypes)
    }
}

之后你有一个点数组和一个类型的镜面数组,它们解释了我们列表中每个点的类型。这次所有 closePath 都被删除了。

现在是时候根据您的情况重新创建路径:

func createNewPath(path:CGPath) -> UIBezierPath {
        let (points,types) = path.getPathElementsPointsAndTypes()
        if points.count <= 1 {
            return  UIBezierPath() // exit
        }

        let pathRef = UIBezierPath()
        var i = 0
        while i < points.count {
            switch (types[i]) {
                case CGPathElementType.MoveToPoint:
                    pathRef.moveToPoint(CGPointMake(points[i].x,points[i].y))
                case .AddLineToPoint:
                    pathRef.addLineToPoint(CGPointMake(points[i].x,points[i].y))
                case .AddQuadCurveToPoint:
                    pathRef.addQuadCurveToPoint(CGPointMake(points[i].x,points[i].y), controlPoint: CGPointMake(points[i+1].x,points[i+1].y))
                    i += 1
                case .AddCurveToPoint:
                    pathRef.addCurveToPoint(CGPointMake(points[i].x,points[i].y), controlPoint1: CGPointMake(points[i+1].x,points[i+1].y), controlPoint2: CGPointMake(points[i+2].x,points[i+2].y))
                    i += 2
            default: break
            }
            i += 1
        }
        //pathRef.closePath() if you want to add new elements dont uncomment this
        return pathRef
    }

启动此方法后,您的路径将被所有 closePath 清理并准备好根据需要添加新行。