Подтвердить что ты не робот

Эквивалент или альтернатива CGPathApply в Свифт?

В документации перед выпуском нет версии Swift версии CGPathApply. Есть ли эквивалент или альтернатива? Я пытаюсь получить все подпуты CGPath, чтобы я мог перерисовать его из другой начальной точки.

4b9b3361

Ответ 1

Swift 3.0

В Swift 3.0 вы можете использовать CGPath.apply следующим образом:

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

С добавлением @convention(c) вы можете теперь вызвать CGPathApply непосредственно из Swift. Вот обертка, которая делает необходимую магию:

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)
    }
}

(Обратите внимание, что @convention(c) не упоминается в моем коде, но используется в объявлении CGPathApply в модуле Core Graphics.)

Пример использования:

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()")
    }
}

Ответ 2

Дмитрий Родионов создал функцию преобразования функции Swift в CFunctionPointer (см. 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;
}

Ответ 3

Вот основные моменты от Ole Begemann отличный пост (спасибо @Gouldsc!), адаптированный для Swift 3, который позволяет получать доступ к отдельным элементам, составляющим 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
        }
    }

}

Ответ 4

Увидел следующий подход к аналогичной проблеме.

Кажется, что это работает. Вызывается обратный вызов, но я сталкиваюсь с проблемами при доступе к памяти каждого элемента.

Игровая площадка:

import UIKit

var counter = 0

let block: @objc_block
(UnsafeMutablePointer<Void>, UnsafePointer<(CGPathElement)>) -> Void = { (info, element) in    

    counter++
    element.memory // <- bzzt. 

}

let imp: COpaquePointer = imp_implementationWithBlock(unsafeBitCast(block, AnyObject.self))
let callback : CGPathApplierFunction = unsafeBitCast(imp, CGPathApplierFunction.self)

let points = [CGPoint(x: 0,y: 0),CGPoint(x: 50,y: 0),CGPoint(x: 50,y: 50),CGPoint(x: 0,y: 50)]
let path =  CGPathCreateMutable()

CGPathMoveToPoint(path, nil, points[0].x, points[0].y)
CGPathAddLineToPoint(path, nil, points[1].x, points[1].y)
CGPathAddLineToPoint(path, nil, points[2].x, points[2].y)
CGPathAddLineToPoint(path, nil, points[3].x, points[3].y)
CGPathCloseSubpath(path)

CGPathApply(path, nil, callback)