CATiledLayer绘图崩溃

发布于 2024-08-21 23:50:21 字数 1996 浏览 10 评论 0原文

在我的应用程序中,我使用具有以下视图层次结构:

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 允许滚动和缩放其内容。
Overlay 视图位于 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:]

我是否缺少多个图形上下文之间所需的任何同步?或者可能有更好的方法来做我正在尝试的事情?

in my application I use have the following views hierarchy:

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

In short - TiledView displays large tiled image I also apply custom rotation to that view:

tiledView.transform = CGAffineTransformMakeRotation(angle);

Drawing method for 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 allows both scrolling and zooming of its contents.
Overlay view lays over UIScrollView, has transparent background and performs some custom drawing. I use separate view to make sure that line width and font size are not affected by zoom scale in scroll view.

Drawing method for 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);
}

Initially everything works ok, but after some playing with the view (e.g. scrolling it to and fro etc) it crashes on some random line in one of the drawing functions. As an example app crashes on line:

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

with stack:

#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:]

Am I missing any synchronization required between several graphics contexts? Or may be there's better way to do what I'm trying?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

李不 2024-08-28 23:50:21

找到了问题的解决方案。如 Apple 的技术说明 CATiledLayer 使用单独的线程来获取其图块的内容:

CATiledLayer 实现了它的
使用后台线程绘图
获取每个图块的内容,
然而提供的绘图功能
UIKit 依赖于全局上下文
堆栈,因此当 CATiledLayer
使用任何 UIKit 开始渲染
绘图函数可能会导致一场竞赛
情况。

因此,解决方案是将所有绘图代码从 -drawRect: 方法移至 -drawLayer:inContext:使用 Core Graphics 函数进行绘制。使用 CoreGraphics 绘图还需要在坐标系之间进行一些转换 - 苹果论坛的这篇帖子提供了帮助与它们(请参阅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]);
}

Found solution to the problem. As described in Apple's technical note CATiledLayer uses separate thread to fetch the contents for its tiles:

The CATiledLayer implements its
drawing by using a background thread
to fetch the contents of each tile,
however the drawing functions provided
by UIKit rely on a global context
stack, and as such when a CATiledLayer
starts rendering, using any of UIKit's
drawing functions can result in a race
condition.

So the solution is to move all drawing code from -drawRect: method to -drawLayer:inContext: and draw using only Core Graphics functions. Drawing with CoreGraphics also requires some transformations between coordinates systems - this post from apple forums helped with them (see drawLayer: method implementation).

So the correct drawing code for 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]);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文