在 CATiledLayer -setNeedsDisplay 之后,并非所有图块都重新绘制

发布于 2024-10-20 08:55:46 字数 2594 浏览 0 评论 0原文

我的视图有一个CATiledLayer。视图控制器具有自定义缩放行为,因此在 -scrollViewDidEndZooming 上,每个图块都需要重新绘制。但是,尽管每次缩放后都会在图层上调用 -setNeedsDisplay,但并非所有图块都会被重绘。这会导致缩放后视图有时看起来不正确。 (本应仅出现在 1 个图块中的内容却出现在多个位置)。它通常会在另一次缩放后自行纠正。

这是相关代码。 updatedRects 用于测试 - 它存储由 -drawLayer 请求绘制的唯一矩形。

CanvasView.h:

@interface CanvasView : UIView
@property (nonatomic, retain) NSMutableSet *updatedRects; 
@end

CanvasView.m:

@implementation CanvasView

@synthesize updatedRects; 

+ (Class)layerClass 
{
    return [CATiledLayer class];
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
    CGRect rect = CGContextGetClipBoundingBox(context); 
    [updatedRects addObject:[NSValue valueWithCGRect:rect]]; 

    CGContextSaveGState(context); 
    CGContextTranslateCTM(context, rect.origin.x, rect.origin.y); 
    // ...draw into context...
    CGContextRestoreGState(context); 
}

@end

MyViewController.m:

@implementation MyViewController

- (void)viewDidLoad 
{
    [super viewDidLoad];

    canvasView.updatedRects = [NSMutableSet set]; 
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
    canvasView.transform = CGAffineTransformIdentity; 
    canvasView.frame = CGRectMake(0, 0, canvasView.frame.size.width * scale, canvasView.frame.size.height); 
    [self updateCanvas]; 
}

- (void)updateCanvas
{
    NSLog(@"# rects updated: %d", [canvasView.updatedRects count]); 
    [canvasView.updatedRects removeAllObjects]; 
    canvasView.frame = CGRectMake(canvasView.frame.origin.x, canvasView.frame.origin.y, canvasView.frame.size.width, myHeight); 
    CGFloat tileSize = 256; 
    NSLog(@"next # rects updated will be: %f", [canvasView.layer bounds].size.width / tileSize); 
    [canvasView.layer setNeedsDisplay]; 
}

@end

测试时,我总是在缩放后一直滚动视图,以确保看到每个图块。每当视图看起来错误时,预测的“下一个更新的矩形数”就会大于下次调用 -updateCanvas 时的实际“更新的矩形数”。什么会导致这种情况呢?

编辑:

这是一种更好的可视化问题的方法。每次调用 updateCanvas 时,我都会更改绘制图块的背景颜色并记录调用时间 - 颜色和时间存储在 canvasView 中。在每个图块上绘制图块的矩形、图块沿 x 轴的索引、设置背景颜色的时间(秒)以及绘制图块的时间(秒)。如果一切正常,所有瓷砖应该是相同的颜色。相反,我有时会看到以下内容:

Screenshot oftiles with notmatched color 颜色不匹配的图块的屏幕截图

我似乎只在缩小后看到错误的结果。 该问题可能与 scrollViewDidEndZooming 以及 updateCanvas 在每次缩小时可能被多次调用有关。 (编辑:不——没有被多次调用。)

My view has a CATiledLayer. The view controller has custom zooming behavior, so on -scrollViewDidEndZooming every tile needs to be redrawn. But, even though -setNeedsDisplay is being called on the layer after every zoom, not all tiles are being redrawn. This is causing the view to sometimes look wrong after a zoom. (Things that should appear in just 1 tile are appearing in multiple places). It often corrects itself after another zoom.

Here's the relevant code. updatedRects is for testing -- it stores the unique rectangles requested to be drawn by -drawLayer.

CanvasView.h:

@interface CanvasView : UIView
@property (nonatomic, retain) NSMutableSet *updatedRects; 
@end

CanvasView.m:

@implementation CanvasView

@synthesize updatedRects; 

+ (Class)layerClass 
{
    return [CATiledLayer class];
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
    CGRect rect = CGContextGetClipBoundingBox(context); 
    [updatedRects addObject:[NSValue valueWithCGRect:rect]]; 

    CGContextSaveGState(context); 
    CGContextTranslateCTM(context, rect.origin.x, rect.origin.y); 
    // ...draw into context...
    CGContextRestoreGState(context); 
}

@end

MyViewController.m:

@implementation MyViewController

- (void)viewDidLoad 
{
    [super viewDidLoad];

    canvasView.updatedRects = [NSMutableSet set]; 
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
    canvasView.transform = CGAffineTransformIdentity; 
    canvasView.frame = CGRectMake(0, 0, canvasView.frame.size.width * scale, canvasView.frame.size.height); 
    [self updateCanvas]; 
}

- (void)updateCanvas
{
    NSLog(@"# rects updated: %d", [canvasView.updatedRects count]); 
    [canvasView.updatedRects removeAllObjects]; 
    canvasView.frame = CGRectMake(canvasView.frame.origin.x, canvasView.frame.origin.y, canvasView.frame.size.width, myHeight); 
    CGFloat tileSize = 256; 
    NSLog(@"next # rects updated will be: %f", [canvasView.layer bounds].size.width / tileSize); 
    [canvasView.layer setNeedsDisplay]; 
}

@end

When testing this, I always scrolled all the way across the view after zooming to make sure every tile was seen. Whenever the view looked wrong, the predicted "next # of rects updated" was greater than the actual "# of rects updated" on the next call to -updateCanvas. What would cause this?

Edit:

Here's a better way to visualize the problem. Each time updateCanvas is called, I've made it change the background color for drawing tiles and record the time it was called -- the color and time are stored in canvasView. On each tile is drawn the tile's rectangle, the tile's index along the x axis, the time (sec.) when the background color was set, and the time (sec.) when the tile was drawn. If everything is working correctly, all tiles should be the same color. Instead, here's what I'm sometimes seeing:

Screenshot of tiles with mismatched colors
Screenshot of tiles with mismatched colors

I only seem to see wrong results after zooming out. The problem might be related to the fact that scrollViewDidEndZooming, and therefore updateCanvas, can get called multiple times per zoom-out. (Edit: No-- not called multiple times.)

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

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

发布评论

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

评论(1

屋檐 2024-10-27 08:55:46

有一个相当昂贵的技巧可能对您有用:

tiledLayer.contents = nil;
[tiledLayer setNeedsDisplayInRect:tiledLayer.bounds];

如果我没记错的话,第一行实际上将平铺层转换为普通层,但第二行将其返回。但是,它会丢弃平铺层中的所有内容。

There is a rather expensive hack that might work for you:

tiledLayer.contents = nil;
[tiledLayer setNeedsDisplayInRect:tiledLayer.bounds];

If I recall correctly, the first line actually turns the tiled layer to an ordinary layer, but the second line turns it back. However, it’ll throw everything in the tiled layer away.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文