ios5:在 CATiledLayer 中为同一个 Rect 调用了两次绘制矩形

发布于 2024-12-10 13:15:31 字数 690 浏览 0 评论 0原文

我遇到了 CATiledLayer 的问题。它在 iOS 4 上工作完美,但在 iOS 5 上有问题。

我的问题是,对于同一个矩形,同时从两个不同的线程调用 drawRect 两次。由于我在此调用中加载图像,因此导致视图加载速度非常慢。

2011-10-18 14:07:18.802 APP[12436:19003] drawRect:{{0, 400}, {368, 400}} (view:<TiledScrollColumn: 0x91bc880; frame = (0 1600; 368 5400); userInteractionEnabled = NO; layer = <CATiledLayer: 0x919c1b0>>)
2011-10-18 14:07:18.805 APP[12436:1b103] drawRect:{{0, 400}, {368, 400}} (view:<TiledScrollColumn: 0x91bc880; frame = (0 1600; 368 5400); userInteractionEnabled = NO; layer = <CATiledLayer: 0x919c1b0>>)

有谁知道什么可能导致这个问题或者我可以做些什么来解决这个问题?我没有对视图做特殊的事情,它是基于 photoscroller 示例。

巴斯蒂安

I have a problem with a CATiledLayer. It is working perfect in on iOS 4, but has problems on iOS 5.

My Problem ist that drawRect is called twice for the same rect from two different threads at the same time. Since I load images in this call, it causes the view to load very slow.

2011-10-18 14:07:18.802 APP[12436:19003] drawRect:{{0, 400}, {368, 400}} (view:<TiledScrollColumn: 0x91bc880; frame = (0 1600; 368 5400); userInteractionEnabled = NO; layer = <CATiledLayer: 0x919c1b0>>)
2011-10-18 14:07:18.805 APP[12436:1b103] drawRect:{{0, 400}, {368, 400}} (view:<TiledScrollColumn: 0x91bc880; frame = (0 1600; 368 5400); userInteractionEnabled = NO; layer = <CATiledLayer: 0x919c1b0>>)

Has anyone an idea what could cause that or what I could do to fix that? I'm not doing special stuff with the view, it's based on the photoscroller example.

Bastian

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

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

发布评论

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

评论(1

姐不稀罕 2024-12-17 13:15:31

我找到了一个解决方法。创建一个串行(一次仅执行一项操作)调度队列并在其中执行所有绘图,然后缓存结果。

我已附上我正在使用的代码,请注意,我使用的是 CATiledLayer 的子类,而不是通过委托或其他技术来绘制它。

我还创建了一个无限期的切片缓存,这可能会导致内存问题,具体取决于您的情况。您可能需要管理缓存中的切片数量,在添加新切片时删除旧切片。当收到低内存警告时,还应该清除缓存。如果有人添加了这些改进,请随时编辑我的答案以将其包含在内。

- (id)init
{
  if (!(self = [super init]))
    return nil;

  tileCache = [[NSMutableDictionary alloc] init];

  return self;
}

- (void)drawInContext:(CGContextRef)layerContext
{
  // CATiledLayer has a bug, where it spawns multiple threads for drawing, and then tries to draw the same tile multiple times simultaniously on separate threads
  // so we create our own serial background queue, and do dispatch_async on it. This will cache each draw operation, so multiple calls on one tile are efficient
  static dispatch_queue_t drawQueue = NULL;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
      drawQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
  });

  dispatch_sync(drawQueue, ^{

    // no map ways? draw nothing
    if (!self.mapWays)
      return;

    // load from cache?
    CGRect tileRect = CGContextGetClipBoundingBox(layerContext);
    NSString *tileCacheKey = [NSString stringWithFormat:@"%f%f%f%f", tileRect.origin.x, tileRect.origin.y, tileRect.size.width, tileRect.size.height];
    __block UIImage *tileImage;
    dispatch_sync(dispatch_get_main_queue(), ^{
      tileImage = [tileCache objectForKey:tileCacheKey];
    });
    if (tileImage) {
      CGContextDrawImage(layerContext, tileRect, tileImage.CGImage);
      return;
    }

    // prepare to draw the tile image
    UIGraphicsBeginImageContextWithOptions(tileRect.size, YES, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // filp coords
    CGContextTranslateCTM(context, 0, tileRect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);


    /*** do actual drawing here ***/


    // store tile in cache
    tileImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    dispatch_sync(dispatch_get_main_queue(), ^{
      [tileCache setObject:tileImage forKey:tileCacheKey];
    });

    // draw the tile
    CGContextDrawImage(layerContext, tileRect, tileImage.CGImage);
  });
}

I found a workaround. Create a serial (only one operation at at time) dispatch queue and perform all your drawing inside it, then cache the result.

I've attached the code I'm using, note I'm using a subclass of CATiledLayer, rather than drawing it through a delegate or other techniques.

I'm also creating an indefinite tile cache, which may cause memory issues depending on your situation. You may need to manage the number of tiles in the cache, deleting old ones as new ones are added. The cache should also be cleared when a low memory warning is received. If anyone adds these improvements, please feel free to edit my answer to include it.

- (id)init
{
  if (!(self = [super init]))
    return nil;

  tileCache = [[NSMutableDictionary alloc] init];

  return self;
}

- (void)drawInContext:(CGContextRef)layerContext
{
  // CATiledLayer has a bug, where it spawns multiple threads for drawing, and then tries to draw the same tile multiple times simultaniously on separate threads
  // so we create our own serial background queue, and do dispatch_async on it. This will cache each draw operation, so multiple calls on one tile are efficient
  static dispatch_queue_t drawQueue = NULL;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
      drawQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
  });

  dispatch_sync(drawQueue, ^{

    // no map ways? draw nothing
    if (!self.mapWays)
      return;

    // load from cache?
    CGRect tileRect = CGContextGetClipBoundingBox(layerContext);
    NSString *tileCacheKey = [NSString stringWithFormat:@"%f%f%f%f", tileRect.origin.x, tileRect.origin.y, tileRect.size.width, tileRect.size.height];
    __block UIImage *tileImage;
    dispatch_sync(dispatch_get_main_queue(), ^{
      tileImage = [tileCache objectForKey:tileCacheKey];
    });
    if (tileImage) {
      CGContextDrawImage(layerContext, tileRect, tileImage.CGImage);
      return;
    }

    // prepare to draw the tile image
    UIGraphicsBeginImageContextWithOptions(tileRect.size, YES, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // filp coords
    CGContextTranslateCTM(context, 0, tileRect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);


    /*** do actual drawing here ***/


    // store tile in cache
    tileImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    dispatch_sync(dispatch_get_main_queue(), ^{
      [tileCache setObject:tileImage forKey:tileCacheKey];
    });

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