In the pre-release documentation there appears to be no Swift version of CGPathApply. Is there an equivalent or alternative? I'm trying to get all subpaths of a CGPath so that I can redraw it from a different starting point.
Swift 3.0
In Swift 3.0, you can use CGPath.apply
like this:
let path: CGPath = ...
// or let path: CGMutablePath
path.apply(info: nil) { (_, elementPointer) in
let element = elementPointer.pointee
let command: String
let pointCount: Int
switch element.type {
case .moveToPoint: command = "moveTo"; pointCount = 1
case .addLineToPoint: command = "lineTo"; pointCount = 1
case .addQuadCurveToPoint: command = "quadCurveTo"; pointCount = 2
case .addCurveToPoint: command = "curveTo"; pointCount = 3
case .closeSubpath: command = "close"; pointCount = 0
}
let points = Array(UnsafeBufferPointer(start: element.points, count: pointCount))
Swift.print("\(command) \(points)")
}
Swift 2.2
With the addition of @convention(c)
, you can now call CGPathApply
directly from Swift. Here's a wrapper that does the necessary magic:
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)
}
}
(Note that @convention(c)
isn't mentioned in my code, but is used in the declaration of CGPathApply
in the Core Graphics module.)
Example usage:
let path = UIBezierPath(roundedRect: CGRectMake(0, 0, 200, 100), cornerRadius: 15)
path.CGPath.forEach { element in
switch (element.type) {
case CGPathElementType.MoveToPoint:
print("move(\(element.points[0]))")
case .AddLineToPoint:
print("line(\(element.points[0]))")
case .AddQuadCurveToPoint:
print("quadCurve(\(element.points[0]), \(element.points[1]))")
case .AddCurveToPoint:
print("curve(\(element.points[0]), \(element.points[1]), \(element.points[2]))")
case .CloseSubpath:
print("close()")
}
}
Here's the highlights from Ole Begemann's great post (thanks @Gouldsc!), adapted for Swift 3, which allows for accessing the individual elements composing a UIBezierPath
instance:
extension UIBezierPath {
var elements: [PathElement] {
var pathElements = [PathElement]()
withUnsafeMutablePointer(to: &pathElements) { elementsPointer in
cgPath.apply(info: elementsPointer) { (userInfo, nextElementPointer) in
let nextElement = PathElement(element: nextElementPointer.pointee)
let elementsPointer = userInfo!.assumingMemoryBound(to: [PathElement].self)
elementsPointer.pointee.append(nextElement)
}
}
return pathElements
}
}
public enum PathElement {
case moveToPoint(CGPoint)
case addLineToPoint(CGPoint)
case addQuadCurveToPoint(CGPoint, CGPoint)
case addCurveToPoint(CGPoint, CGPoint, CGPoint)
case closeSubpath
init(element: CGPathElement) {
switch element.type {
case .moveToPoint: self = .moveToPoint(element.points[0])
case .addLineToPoint: self = .addLineToPoint(element.points[0])
case .addQuadCurveToPoint: self = .addQuadCurveToPoint(element.points[0], element.points[1])
case .addCurveToPoint: self = .addCurveToPoint(element.points[0], element.points[1], element.points[2])
case .closeSubpath: self = .closeSubpath
}
}
}
Dmitry Rodionov has produced a function for converting a Swift function to a CFunctionPointer (see https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift).
#define kObjectFieldOffset sizeof(uintptr_t)
struct swift_func_object {
uintptr_t *original_type_ptr;
#if defined(__x86_64__)
uintptr_t *unknown0;
#else
uintptr_t *unknown0, *unknown1;
#endif
uintptr_t function_address;
uintptr_t *self;
};
uintptr_t _rd_get_func_impl(void *func)
{
struct swift_func_object *obj = (struct swift_func_object *)*(uintptr_t *)(func + kObjectFieldOffset);
//printf("-->Address of C-Func %lx unk=%lx ori=%lx<--\n", obj->function_address, obj->unknown0, obj->original_type_ptr);
return obj->function_address;
}
I am using this successfully with CGPathApply along with a Swift callback function. (code at http://parker-liddle.org/CGPathApply/CGPathApply.zip) Although as Dmitry says this is a reverse engineered function and not a supported one.
来源:https://stackoverflow.com/questions/24274913/equivalent-of-or-alternative-to-cgpathapply-in-swift