Сбой рисования CATiledLayer - PullRequest
       27

Сбой рисования CATiledLayer

1 голос
/ 19 февраля 2010
В моем приложении

используется следующая иерархия представлений:

UIView
----UIScrollView
--------TiledView (UIView subclass, uses CATiledLayer for drawing)
----OverlayView (UIView subclass)

Короче говоря - TiledView отображает большое мозаичное изображение. Я также применяю пользовательское вращение к этому виду:

tiledView.transform = CGAffineTransformMakeRotation(angle);

Метод рисования для TiledView:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    CGContextRef context = UIGraphicsGetCurrentContext();  
    ...
  NSString *fileName = [NSString stringWithFormat:@"tile_%d_%d.jpg", y + 1, x + 1];
    UIImage *image = [UIImage imageNamed:fileName];
    [image drawInRect:rect];
 }

UIScrollView позволяет прокручивать и масштабировать его содержимое.
Оверлейный вид лежит над UIScrollView, имеет прозрачный фон и выполняет некоторые пользовательские рисования. Я использую отдельный вид, чтобы убедиться, что на ширину линии и размер шрифта не влияет масштаб увеличения при просмотре с прокруткой.

Метод рисования для OverlayView:

- (void)drawRect:(CGRect)rect {
    // Drawing code
    [super drawRect:rect];

    // Custom drawing code
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetRGBStrokeColor(context, 1.0, 1.0, 0, 1);
   CGContextSetLineWidth(context, lineWidth);

    CGContextBeginPath(context);
    CGContextMoveToPoint(context, (int)startPoint.x,(int) startPoint.y);

    if (usesMidPoint)
        CGContextAddLineToPoint(context, (int)midPoint.x, (int)midPoint.y); 
    CGContextAddLineToPoint(context, endPoint.x, endPoint.y);

    CGContextStrokePath(context);
}

Первоначально все работает нормально, но после некоторой игры с видом (например, прокрутка его туда-сюда и т. Д.) Он падает на некоторой случайной линии в одной из функций рисования. В качестве примера приложение вылетает на линии:

CGContextSetRGBStrokeColor(context, 1.0, 1.0, 0, 1); 

со стеком:

#0  0x00503088 in CGColorEqualToColor
#1  0x00505430 in CGGStateSetStrokeColor
#2  0x005053b6 in setStrokeColorWithComponents
#3  0x0056150f in CGContextSetRGBStrokeColor
#4  0x000764ab in -[GADrawView drawRect:] at GADrawView.m:38
#5  0x016a7a78 in -[UIView(CALayerDelegate) drawLayer:inContext:]
#6  0x0077c007 in -[CALayer drawInContext:]

Мне не хватает синхронизации, необходимой между несколькими графическими контекстами? Или, может быть, есть лучший способ сделать то, что я пытаюсь?

1 Ответ

7 голосов
/ 22 февраля 2010

Нашел решение проблемы.Как описано в техническом примечании Apple , CATiledLayer использует отдельный поток для извлечения содержимого для своих плиток:

CATiledLayer реализует свой рисунок, используя фоновый поток для извлечения содержимогокаждая плитка, однако функции рисования, предоставляемые UIKit, полагаются на стек глобального контекста, и, следовательно, когда CATiledLayer начинает рендеринг, использование любой из функций рисования UIKit может привести к состоянию гонки.

Так чтоРешение состоит в том, чтобы переместить весь код рисования из метода -drawRect: в -drawLayer:inContext: и рисовать, используя только Основные графические функции.Рисование с помощью CoreGraphics также требует некоторых преобразований между системами координат - в этом помогла запись с форумов Apple (см. drawLayer: реализация метода).

Итак, правильный код для TiledView:

- (void)drawRect:(CGRect)rect {
    //Still need to have this method implemented even if its empty!
}

-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx
{
    // Do all your drawing here. Do not use UIGraphics to do any drawing, use Core Graphics instead.
    // convert the CA coordinate system to the iPhone coordinate system
    CGContextTranslateCTM(ctx, 0.0f, 0.0f);
    CGContextScaleCTM(ctx, 1.0f, -1.0f);

    CGRect box = CGContextGetClipBoundingBox(ctx);

    // invert the Y-coord to translate between CA coords and iPhone coords
    CGPoint pixelTopLeft = CGPointApplyAffineTransform(box.origin, CGAffineTransformMakeScale(1.0f, -1.0f));

    NSString *tileUrlString = [self urlForPoint:pixelTopLeft];

    UIImage *image = [UIImage imageNamed:tileUrlString];
    CGContextDrawImage(ctx, box, [image CGImage]);
}
...