Не все плитки перерисовываются после CATiledLayer -setNeedsDisplay - PullRequest
4 голосов
/ 04 марта 2011

Мой взгляд имеет 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 of tiles with mismatched colors Screenshot of tiles with mismatched colors

Кажется, я вижу неправильные результаты только после уменьшения масштаба. Проблема может быть связана с тем фактом, что scrollViewDidEndZooming, и, следовательно, updateCanvas, могут вызываться несколько раз за уменьшение. ( Edit : Нет - не вызывается несколько раз.)

1 Ответ

10 голосов
/ 11 марта 2011

Существует довольно дорогой хак, который может сработать для вас:

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

Если я правильно помню, первая строка фактически превращает плиточный слой в обычный слой, а вторая - обратно.Тем не менее, он выбросит все в мозаичный слой.

...