2010-02-19 4 views
1

dans ma demande que j'utilise avoir la hiérarchie des vues suivantes:CATiledLayer dessin accident

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

En bref - TiledView affiche une grande image en mosaïque J'applique également une rotation personnalisée à ce point de vue:

tiledView.transform = CGAffineTransformMakeRotation(angle); 

méthode Dessin pour 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 permet le défilement et le zoom de son contenu.
La vue de superposition repose sur UIScrollView, a un arrière-plan transparent et effectue un dessin personnalisé. J'utilise une vue séparée pour m'assurer que la largeur de la ligne et la taille de la police ne sont pas affectées par l'échelle du zoom en mode défilement.

méthode Dessin pour 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); 
} 

Au départ, tout fonctionne bien, mais après un certain jeu avec la vue (par exemple le défilement à çà et là, etc.), il se bloque sur une ligne au hasard dans l'une des fonctions de dessin. Comme plantage d'une application exemple en ligne:

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

avec pile:

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

Suis-je manque une synchronisation nécessaire entre plusieurs contextes graphiques? Ou peut-être qu'il y a une meilleure façon de faire ce que j'essaie?

Répondre

7

Solution trouvée au problème. Comme décrit dans technical note Apple CATiledLayer utilise thread séparé pour récupérer le contenu de ses tuiles:

Le CATiledLayer met en œuvre son dessin en utilisant un fil de fond pour récupérer le contenu de chaque tuile, cependant les fonctions de dessin fourni par UIKit s'appuyer sur une pile de contexte global , et en tant que tel lorsque un CATiledLayer commence le rendu, en utilisant l'une des fonctions de dessin d'UIKit peut entraîner une condition de race .

Donc, la solution est de déplacer tout le code de dessin -drawRect: méthode pour -drawLayer:inContext: et dessiner à l'aide seulement fonctions graphiques de base. Dessiner avec CoreGraphics nécessite également des transformations entre les systèmes de coordonnées - ce post provenant des forums Apple les a aidés (voir implémentation de la méthode).

Ainsi, le code de dessin correct pour 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]); 
} 
+0

Pourriez-vous s'il vous plaît expliquer comment accrocher le TiledView lui-même sur le point de vue de défilement? merci – Jonny

+0

il suffit de l'ajouter comme une sous-vue et définir le contenu de la vue de défilement Taille à une valeur correcte – Vladimir

+0

Apparemment, ce n'est plus nécessaire à partir de iOS 4.0 - selon la note technique liée à la réponse acceptée. Dessin dans drawRect: autorisé maintenant sans avoir à sauter à travers des cerceaux avec la transformation des coordonnées. –