iOS: получить CGMutablePath не в drawRect: и назначить его пути CAShapeLayer

Я бился головой об этом часами. Я пытался читать и искал ответы. Я даже попробовал подход "уходи и возвращайся к нему позже", он не сработал. Я даже пробовал йогу и медитировал! Я не победил..

Поэтому, пожалуйста, просветите меня, эксперты.

Все, что я пытаюсь получить копию CGPath не внутри drawRect:

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, ......);
CGPathAddLineToPoint(path, ....);
CGPathAddLineToPoint(path, ....);
CGPathAddLineToPoint(path, ....);
CGPathAddLineToPoint(path, ....);

И наконец:
CGPathCloseSubpath(path);
CGContextAddPath(context, path);
Это не сработало, поэтому я попытался также использовать вместо 2 вызовов выше: CGPathCreateMutableCopy(path);
И в конце концов - я просто хочу иметь возможность получить этот путь и вставить его как путь в CAShapeLayer и анимировать его, выполнив этот вызов:

animation.toValue = [UIBezierPath bezierPathWithCGPath:path];

Поэтому, прежде чем я попытаюсь заняться другим расслабляющим занятием, таким как "Небесный дайвинг" без парашюта, в надежде очистить свой разум и, наконец, сделать это правильно, не могли бы вы помочь мне здесь, показав мне несколько примеров? Кроме того, можете ли вы подробно объяснить мне, как правильно использовать следующие функции:

CGPathCloseSubpath(path);
CGContextAddPath(context, path);
CGPathCreateMutableCopy(path);

Заранее спасибо.

1 ответ

  • CGPath - это структура данных в стиле C с API в стиле C. В основном это список точек, которые составляют путь.

  • CGContext - это также структура данных в стиле C (возможно, немного больше) для рисования изображений, то есть преобразования точек в цветные пиксели.

Я поместил свои фрагменты в функцию, которую вы можете просто скопировать и вставить в один экранный проект xcode. Вы найдете объяснения в виде комментариев в коде и две функции для отладки и глубокого изучения объектов CGPath (которые я взял из https://github.com/erica/iOS-6-Cookbook с некоторыми дополнениями).

#import "ViewController.h"
#import "OverlayView.h"
#import <QuartzCore/QuartzCore.h>

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // create a graphics context with the C-API of the QuartzCore framework
    // the graphics context is only required for drawing the path
    CGRect compositionBounds = CGRectMake(30, 30, 300, 508);
    UIGraphicsBeginImageContext(compositionBounds.size);
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // create a mutable path with QuartzCore
    CGMutablePathRef p1 = CGPathCreateMutable();

    // create a CGPath with UIKit classes using Obj-C
    UIBezierPath *bp = [UIBezierPath bezierPath];
    [bp moveToPoint:(CGPoint){180, 120}];
    [bp addLineToPoint:(CGPoint){30, 170}];
    [bp addLineToPoint:(CGPoint){135, 175}];
    [bp addLineToPoint:(CGPoint){105, 115}];
    // if you fill the path during drawing it gets closed implicitly
//    [bp closePath];
    // add the CGPath to our mutable path
    CGPathAddPath(p1, nil, bp.CGPath);

    // add a closed triangle as subpath to our mutable path
    CGPathMoveToPoint(p1, nil, 50, 250);
    CGPoint p2[3];
    p2[0] = CGPointMake(10, 20);
    p2[1] = (CGPoint){210, 50};
    p2[2] = (CGPoint){80, 120};
    CGPathAddLines(p1, nil, p2, 3);
    // explicitly close our last added subpath
    CGPathCloseSubpath(p1);

    // draw our mutable path (with all its subpaths) filling its area
    CGContextAddPath(ctx, p1);
    CGContextSetFillColorWithColor(ctx, [[UIColor redColor] CGColor]);
    CGContextFillPath(ctx);

    // draw our mutable path (with all its subpaths) stroking its outline
    CGContextAddPath(ctx, p1);
    CGContextSetRGBStrokeColor(ctx, 0, 0, 0, 1.0);
    CGContextStrokePath(ctx);

    // display our mutable path in an image view on screen
    UIImage *i = UIGraphicsGetImageFromCurrentImageContext();
    UIImageView *iv = [[UIImageView alloc] initWithImage:i];
    [self.view addSubview:iv];
    iv.frame = self.view.frame;

    // release ressources created with the C-API of QuartzCore
    CGPathRelease(p1);
    UIGraphicsEndImageContext();

    // create a mutable copy of a path and don't use it for drawing
    CGMutablePathRef p3 = CGPathCreateMutableCopy(bp.CGPath);
    NSArray *p3points = [self pointsFromCGPath:p3];
    for (NSValue *point in p3points) {
        NSLog(@"path element in p3: %@", NSStringFromCGPoint(point.CGPointValue));
    }
    // note that UIBezierPath takes care of its path itself
    CGPathRelease(p3);

}

// modified from https://github.com/erica/iOS-6-Cookbook

#define VALUE(_INDEX_) [NSValue valueWithCGPoint:points[_INDEX_]]

void getPointsFromBezier(void *info, const CGPathElement *element)
{
    NSMutableArray *bezierPoints = (__bridge NSMutableArray *)info;

    // Retrieve the path element type and its points
    CGPathElementType type = element->type;
    CGPoint *points = element->points;

    switch (type) {
        case kCGPathElementMoveToPoint:
            NSLog(@"MoveToPoint (%3.2f, %3.2f", points->x, points->y);
            break;
        case kCGPathElementAddLineToPoint:
            NSLog(@"AddLineToPoint (%3.2f, %3.2f)", points->x, points->y);
            break;
        case kCGPathElementAddQuadCurveToPoint:
            NSLog(@"AddQuadCurveToPoint (%3.2f, %3.2f), (%3.2f, %3.2f)", points->x, points->y, points[1].x, points[1].y);
            break;
        case kCGPathElementAddCurveToPoint:
            NSLog(@"AddCurveToPoint (%3.2f, %3.2f), (%3.2f, %3.2f), (%3.2f, %3.2f)", points->x, points->y, points[1].x, points[1].y, points[2].x, points[2].y);
            break;
        case kCGPathElementCloseSubpath:
            NSLog(@"CloseSubpath (%3.2f, %3.2f)", points->x, points->y);
            break;
        default:
            NSLog(@"unknown");
            break;
    }

    // Add the points if they're available (per type)
    if (type != kCGPathElementCloseSubpath)
    {
        [bezierPoints addObject:VALUE(0)];
        if ((type != kCGPathElementAddLineToPoint) &&
            (type != kCGPathElementMoveToPoint))
            [bezierPoints addObject:VALUE(1)];
    }
    if (type == kCGPathElementAddCurveToPoint)
        [bezierPoints addObject:VALUE(2)];
}

- (NSArray *)pointsFromCGPath:(CGPathRef)path
{
    NSMutableArray *points = [NSMutableArray array];
    CGPathApply(path, (__bridge void *)points, getPointsFromBezier);
    return points;
}
Другие вопросы по тегам