drawRect:/renderInContext: проблемы

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

Моя базовая настройка такова: у меня есть контроллер представления, которому принадлежит UIPageViewController, в который я загружаю контроллеры с представлениями, которые могут быть нарисованы пальцем пользователя. В общем, у меня есть книга, в которой можно рисовать.

Когда страница переворачивается в книге, я сохраняю изображение, вызывая

- (UIImage *)wholeImage {
    // Render the layer into an image and return
    UIGraphicsBeginImageContext(self.bounds.size);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *wholePageImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return wholePageImage;
}

Возвращаемое значение этого затем сохраняется в файл. Однако, когда я вызываю этот метод сохранения, тогда строка

[self.layer renderInContext:UIGraphicsGetCurrentContext()];

получает удар, который, кажется, вызывает мой метод drawRect:, который переопределяется следующим образом:

- (void)drawRect:(CGRect)rect {
    // Draw the current state of the image
    [self.currentImage drawAtPoint:CGPointMake(0.0f, 0.0f)];
    CGPoint midPoint1 = [self midPointOfPoint1:previousPoint1 point2:previousPoint2];
    CGPoint midPoint2 = [self midPointOfPoint1:currentPoint point2:previousPoint1];

    // Get the context
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    // Add the new bit of line to the image
    [self.layer renderInContext:context];
    CGContextMoveToPoint(context, midPoint1.x, midPoint1.y);
    CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, midPoint2.x, midPoint2.y); 
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, self.lineWidth);
    if (self.drawMode == LNDrawViewDrawModeTipex) CGContextSetBlendMode(context, kCGBlendModeClear);
    else CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGContextSetStrokeColorWithColor(context, self.lineColour.CGColor);
    CGContextStrokePath(context);

    // Call super
    [super drawRect:rect];
}

что имеет смысл, но, похоже, он вызывается рекурсивно, пока в конце концов я не получу EXC_BAD_ACCESS в этой строке

[self.currentImage drawAtPoint:CGPointMake(0.0f, 0.0f)];

Я в полной растерянности относительно того, что вызывает это и сводило меня с ума.

Если это поможет, мой стек вызовов начинается так

введите здесь описание изображения

а затем продолжается рекурсивно, пока, наконец, не закончится

введите здесь описание изображения

Был бы очень признателен за помощь и понимание, которое может дать каждый!

РЕДАКТИРОВАТЬ: (добавлено добавление касаний)

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    // Get the touch
    UITouch *touch = [touches anyObject];

    // Get the points
    previousPoint2 = previousPoint1;
    previousPoint1 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];

    // Calculate mid point
    CGPoint mid1 = [self midPointOfPoint1:previousPoint1 point2:previousPoint2]; 
    CGPoint mid2 = [self midPointOfPoint1:currentPoint point2:previousPoint1];

    // Create a path for the last few points
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);
    CGPathAddQuadCurveToPoint(path, NULL, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
    CGRect pathBounds = CGPathGetBoundingBox(path);
    CGPathRelease(path);

    // Take account of line width
    pathBounds.origin.x -= self.lineWidth * 2.0f;
    pathBounds.origin.y -= self.lineWidth * 2.0f;
    pathBounds.size.width += self.lineWidth * 4.0f;
    pathBounds.size.height += self.lineWidth * 4.0f;

    UIGraphicsBeginImageContext(pathBounds.size);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    self.currentImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    [self setNeedsDisplayInRect:pathBounds];    

}


person George Green    schedule 12.04.2012    source источник


Ответы (1)


Вы не должны звонить [self.layer renderInContext:] в drawRect:, и точка. Как вы поняли, renderInContext: вызывает drawRect:, если он реализован, что вызовет бесконечную рекурсию.

Вы должны визуализировать книгу как есть отдельно, а затем использовать визуализированное изображение, чтобы пользователи могли рисовать поверх него. Один из способов сделать это — заключить автономный код рисования в блок UIGraphicsBeginImageContext()/UIGraphicsEndImageContext() и использовать полученное изображение в блоке drawRect:. Другой способ - использовать два разных вида, которые не являются подвидами друг друга, один рисует неизмененную книгу, а другой рисует пользовательские эскизы.

РЕДАКТИРОВАТЬ: прежде всего вам нужно исходное изображение (страница книги, что угодно). Нарисуйте его внутри блока UIGraphicsBeginImageContext()/UIGraphicsEndImageContext() без вызова [self.layer renderInContext:] и сохраните его в переменной представления (скажем, self.currentImage). В touchesMoved:withEvent: вызовите setNeedsDisplay (он вызовет drawRect: для вас), чтобы обновить представление. Используйте self.currentImage в drawRect: в качестве фонового изображения.

Надеюсь, я не слишком расплывчат.

person Costique    schedule 12.04.2012
comment
Привет, спасибо за ваш ответ. Не думайте, что я полностью уверен, что вы имеете в виду. Только что добавили мои штрихи, переместили код в вопрос. Я делаю некоторый код рисования в блоке UIGraphicsBeginImageContext()/UIGraphicsEndImageContext(), но если я снова не вызову renderInContext в drawRect, я, кажется, получу довольно базарное поведение..? - person George Green; 12.04.2012
comment
Кажется, что drawRect не всегда вызывается из renderInContext, поскольку я, кажется, могу нормально рисовать в своем представлении без этой проблемы...!? Я очень смущен. - person George Green; 12.04.2012
comment
Я думаю, что вы немного ошиблись в своей архитектуре. По сути, вам нужно сделать две несвязанные вещи: нарисовать изображение и нарисовать что-то поверх него. Поскольку они независимы, представление, отображающее окончательный результат, должно брать исходное/фоновое изображение где-то еще, а не из собственного содержимого. Результат drawRect: устанавливается как contents слоя представления. renderInContext: может или не может звонить drawRect: каждый раз, в зависимости от множества факторов, находящихся вне вашего контроля. Он может использовать кешированное изображение, но это деталь реализации. - person Costique; 12.04.2012
comment
Я понимаю. Вы также можете преобразовать рисунок пользователя в изображение, а затем нарисовать изображение в drawRect:. При рисовании в изображение можно использовать результат предыдущего рисования в качестве фона, накапливая операции рисования. drawRect: в этом случае всегда будет рисовать финальное изображение. - person Costique; 12.04.2012
comment
Извините, только что увидел ваше обновление. Проблема в том, что я не могу получить исходное изображение без вызова [self.layer renderInContext:], поскольку исходное изображение на самом деле представляет собой то, что уже было нарисовано в слое? - person George Green; 12.04.2012
comment
Извините за такую ​​боль, но как мне это сделать? - person George Green; 12.04.2012
comment
Думаю, я разработал, как сделать то, что вы сказали. СПАСИБО!! Когда вы делаете [self.layer renderInContext:], он помещает весь слой в контекст. Есть ли способ отобразить только часть слоя в контексте?? - person George Green; 13.04.2012
comment
Конечно, используйте обтравочные контуры Core Graphics, см. CGContext.h - person Costique; 13.04.2012
comment
Разобрался в итоге. Спасибо!! - person George Green; 13.04.2012