【发布时间】:2012-10-11 03:26:04
【问题描述】:
如何获得一个数组,其中包含给定 CGPath (CGMutablePathRef) 中包含的所有 CGPoint(s)?
【问题讨论】:
标签: iphone objective-c ios
如何获得一个数组,其中包含给定 CGPath (CGMutablePathRef) 中包含的所有 CGPoint(s)?
【问题讨论】:
标签: iphone objective-c ios
使用 Swift 2.x(适用于 Swift 3.x、Swift 4.x 和 Swift 5.x 请在下面阅读..),我发现了这篇关于 C Callbacks in Swift 的精彩文章。
正如她所说,试图获得“所有CGPoint(s)”,正如Lily Ballard 所解释的那样,可能是个坏主意。
所以,我认为也许最好的方法是获取用于创建特定CGPath 的路径元素点:
//MARK: - CGPath extensions
extension CGPath {
func forEach(@noescape body: @convention(block) (CGPathElement) -> Void) {
typealias Body = @convention(block) (CGPathElement) -> Void
func callback(info: UnsafeMutablePointer<Void>, element: UnsafePointer<CGPathElement>) {
let body = unsafeBitCast(info, Body.self)
body(element.memory)
}
print(sizeofValue(body))
let unsafeBody = unsafeBitCast(body, UnsafeMutablePointer<Void>.self)
CGPathApply(self, unsafeBody, callback)
}
func getPathElementsPoints() -> [CGPoint] {
var arrayPoints : [CGPoint]! = [CGPoint]()
self.forEach { element in
switch (element.type) {
case CGPathElementType.MoveToPoint:
arrayPoints.append(element.points[0])
case .AddLineToPoint:
arrayPoints.append(element.points[0])
case .AddQuadCurveToPoint:
arrayPoints.append(element.points[0])
arrayPoints.append(element.points[1])
case .AddCurveToPoint:
arrayPoints.append(element.points[0])
arrayPoints.append(element.points[1])
arrayPoints.append(element.points[2])
default: break
}
}
return arrayPoints
}
}
使用此扩展程序,您可以执行以下操作:
var bezier = UIBezierPath(ovalInRect: CGRectMake(0, 0, 400, 300))
let myOval = bezier.CGPath
let junctionPoints = myOval.getPathElementsPoints()
print("junction points are: \(junctionPoints)")
(由于@convention(c)的语法重新引入,有一些更正):
extension CGPath {
func forEach( body: @convention(block) (CGPathElement) -> Void) {
typealias Body = @convention(block) (CGPathElement) -> Void
let callback: @convention(c) (UnsafeMutableRawPointer, UnsafePointer<CGPathElement>) -> Void = { (info, element) in
let body = unsafeBitCast(info, to: Body.self)
body(element.pointee)
}
print(MemoryLayout.size(ofValue: body))
let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self)
self.apply(info: unsafeBody, function: unsafeBitCast(callback, to: CGPathApplierFunction.self))
}
func getPathElementsPoints() -> [CGPoint] {
var arrayPoints : [CGPoint]! = [CGPoint]()
self.forEach { element in
switch (element.type) {
case CGPathElementType.moveToPoint:
arrayPoints.append(element.points[0])
case .addLineToPoint:
arrayPoints.append(element.points[0])
case .addQuadCurveToPoint:
arrayPoints.append(element.points[0])
arrayPoints.append(element.points[1])
case .addCurveToPoint:
arrayPoints.append(element.points[0])
arrayPoints.append(element.points[1])
arrayPoints.append(element.points[2])
default: break
}
}
return arrayPoints
}
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)
}
}
extension CGPath {
func forEach( body: @escaping @convention(block) (CGPathElement) -> Void) {
typealias Body = @convention(block) (CGPathElement) -> Void
let callback: @convention(c) (UnsafeMutableRawPointer, UnsafePointer<CGPathElement>) -> Void = { (info, element) in
let body = unsafeBitCast(info, to: Body.self)
body(element.pointee)
}
//print(MemoryLayout.size(ofValue: body))
let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self)
self.apply(info: unsafeBody, function: unsafeBitCast(callback, to: CGPathApplierFunction.self))
}
func getPathElementsPoints() -> [CGPoint] {
var arrayPoints : [CGPoint]! = [CGPoint]()
self.forEach { element in
switch (element.type) {
case CGPathElementType.moveToPoint:
arrayPoints.append(element.points[0])
case .addLineToPoint:
arrayPoints.append(element.points[0])
case .addQuadCurveToPoint:
arrayPoints.append(element.points[0])
arrayPoints.append(element.points[1])
case .addCurveToPoint:
arrayPoints.append(element.points[0])
arrayPoints.append(element.points[1])
arrayPoints.append(element.points[2])
default: break
}
}
return arrayPoints
}
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)
}
}
【讨论】:
let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self) 上收到错误消息 Converting non-escaping value to 'T' may allow it to escape
苹果新增实例方法CGPath.applyWithBlock,可用于iOS11.0+和macOS10.13+
您仍然可以使用 CGPath.apply 检查路径的每个元素,如前面的答案中所述。 但如果你想避免使用 C 风格的指针和 unsafeBitCast,你应该使用更方便的实例方法applyWithBlock。
例如,如果您想获取 CGPath 的 CGPoints 数组,您可以添加 extension 到 CGPath,在本例中为 计算属性 (点)收集 CGPath 的 CGPoints:
/// Extension to collect CGPath points
extension CGPath {
/// this is a computed property, it will hold the points we want to extract
var points: [CGPoint] {
/// this is a local transient container where we will store our CGPoints
var arrPoints: [CGPoint] = []
// applyWithBlock lets us examine each element of the CGPath, and decide what to do
self.applyWithBlock { element in
switch element.pointee.type
{
case .moveToPoint, .addLineToPoint:
arrPoints.append(element.pointee.points.pointee)
case .addQuadCurveToPoint:
arrPoints.append(element.pointee.points.pointee)
arrPoints.append(element.pointee.points.advanced(by: 1).pointee)
case .addCurveToPoint:
arrPoints.append(element.pointee.points.pointee)
arrPoints.append(element.pointee.points.advanced(by: 1).pointee)
arrPoints.append(element.pointee.points.advanced(by: 2).pointee)
default:
break
}
}
// We are now done collecting our CGPoints and so we can return the result
return arrPoints
}
}
【讨论】:
您可以使用CGPathApply() 遍历路径中的每个段并使用该段运行自定义函数。这将为您提供路径的所有信息。
但是,如果“所有 CGPoint(s)”是指每个点都有一个像素渲染到它,那是一个无限大小的集合。虽然您当然可以使用 apply 函数来获取每个段,然后对于每个非移动段,使用该段的控制点评估您自己的数学,以获得您想要的任何密度的点列表。
【讨论】:
CGPath 是一种不透明的数据类型,不一定存储所有使用的点。除此之外,路径实际上可能不会绘制所有用作输入的点(例如,考虑贝塞尔控制点)。
从路径中获取信息的唯一两个documented ways 是使用CGPathGetBoundingBox 获取边界框,或更复杂的方法是使用CGPathApply 调用回调函数,如果CGPathElement 类型。
【讨论】:
CGPath 是一个点数组,只是一个更复杂且对应用程序程序员不那么透明的点。如果您想处理这些点,您自己管理数据结构可能更容易。