特定の( )CGPoint
に含まれるすべての(s)を含む配列を取得するにはどうすればよいですか?CGPath
CGMutablePathRef
4 に答える
Swift 2.xを使用して(Swift 3.x、Swift 4.x、およびSwift 5.xについては、以下をお読みください。)、SwiftでのCコールバックに関するこの素晴らしい記事を見つけました。
リリー・バラードCGPoint
が説明したように、「すべて」を取得しようとすることは、彼女が言ったように悪い考えである可能性があります。
したがって、おそらく最良の方法は、特定の作成に使用されるパス要素のポイントを取得することだと思います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)")
Swift3.xおよびSwift4.1(Swift 4.2またはメジャーについては以下を参照してください。)
(構文の再導入によるいくつかの修正があります@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)
}
}
Swift> 4.1(Swift 5.xも)およびiOS9.xおよび>互換
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)
}
}
Appleは、 iOS11.0以降およびmacOS10.13以降で利用可能なインスタンスメソッド CGPath.applyWithBlockを追加しました
前の回答で説明したように、CGPath.applyを使用してパスの各要素を調べることができます。ただし、CスタイルのポインタとunsafeBitCastの使用を避けたい場合は、はるかに便利なインスタンスメソッドを使用する必要があります applyWithBlock
。たとえば、CGPathのCGPointの配列を取得する場合は、CGPathに拡張機能 を追加できます。この例では、CGPathのCGPointを収集する計算プロパティ(ポイント)です。
/// 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」とは、ピクセルがレンダリングされているすべてのポイントを意味する場合、それは無限のサイズのセットです。確かにapply関数を使用して各セグメントを取得し、次に移動しないすべてのセグメントについて、セグメントのコントロールポイントを使用して独自の計算を評価し、必要な密度のポイントのリストを取得できます。
ACGPath
は不透明(OPAQUE)型であり、使用されるすべてのポイントを格納する必要はありません。これに加えて、パスは実際に入力として使用されるすべてのポイントを描画しない場合があります(たとえば、ベジェコントロールポイントを検討してください)。
パスから情報を取得するための文書化された2つの方法は、バウンディングボックスを取得するために使用する方法、またはタイプの場合にシーケンスを提供するコールバック関数を呼び出すためCGPathGetBoundingBox
に使用するより複雑な方法です。CGPathApply
CGPathElement