You need to drop down to CGPath
and use CGPathApply
to walk the elements. You only want the first one, but you have to look at them all.
I'm assuming that your path is well-formed, and starts with a "move." That should always be true for a UIBezierPath
(I'm not aware of any way to make it not be true.)
You'll need some help from rob mayoff's CGPath.forEach
, which is quite tricky, but with that in place it's pretty straightforward:
// rob mayoff's CGPath.foreach
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)
}
let unsafeBody = unsafeBitCast(body, UnsafeMutablePointer<Void>.self)
CGPathApply(self, unsafeBody, callback)
}
}
// Finds the first point in a path
extension UIBezierPath {
func firstPoint() -> CGPoint? {
var firstPoint: CGPoint? = nil
self.CGPath.forEach { element in
// Just want the first one, but we have to look at everything
guard firstPoint == nil else { return }
assert(element.type == .MoveToPoint, "Expected the first point to be a move")
firstPoint = element.points.memory
}
return firstPoint
}
}
In Swift 3, it's basically the same:
// rob mayoff's CGPath.foreach
extension CGPath {
func forEach( body: @convention(block) (CGPathElement) -> Void) {
typealias Body = @convention(block) (CGPathElement) -> Void
func callback(info: UnsafeMutableRawPointer?, element: UnsafePointer<CGPathElement>) {
let body = unsafeBitCast(info, to: Body.self)
body(element.pointee)
}
let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self)
self.apply(info: unsafeBody, function: callback)
}
}
// Finds the first point in a path
extension UIBezierPath {
func firstPoint() -> CGPoint? {
var firstPoint: CGPoint? = nil
self.cgPath.forEach { element in
// Just want the first one, but we have to look at everything
guard firstPoint == nil else { return }
assert(element.type == .moveToPoint, "Expected the first point to be a move")
firstPoint = element.points.pointee
}
return firstPoint
}
}