ios5: drawRect вызывается дважды в CATiledLayer для одного и того же Rect - PullRequest
1 голос
/ 18 октября 2011

У меня проблема с 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>>)

Кто-нибудь знает, что может вызвать это или что я могу сделать, чтобы это исправить?Я не делаю особых вещей с видом, он основан на примере фотокроллера.

Bastian

1 Ответ

1 голос
/ 09 января 2012

Я нашел обходной путь. Создайте последовательную (только одну операцию за один раз) очередь отправки и выполните все свои рисунки внутри нее, затем кэшируйте результат.

Я приложил код, который я использую, обратите внимание, что я использую подкласс 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);
  });
}
...