2010-08-02 4 views
6

J'ai une application de dessin dans laquelle je voudrais créer une méthode d'annulation. Le dessin a lieu à l'intérieur de la méthode TouchesMoved:Enregistrement de CGContextRef

Je suis en train de créer un CGContextRef et de le pousser dans la pile OU de l'enregistrer dans une propriété de contexte qui peut être restaurée plus tard mais qui n'a pas de chance. Tout conseil serait bon. Voici ce que j'ai ...

UIImageView  *drawingSurface; 
CGContextRef  undoContext; 


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
UIGraphicsBeginImageContext(self.view.frame.size); 
CGContextRef context = UIGraphicsGetCurrentContext(); 
[drawingSurface.image drawInRect:CGRectMake(0, 0, drawingSurface.image.size.width, drawingSurface.image.size.height)]; 
UIGraphicsPushContext(context); 

     // also tried but cant figure how to restore it 
     undoContext = context; 

UIGraphicsEndImageContext(); 
} 

J'ai une méthode déclenchée par mon bouton Annuler ...

- (IBAction)restoreUndoImage { 
UIGraphicsBeginImageContext(self.view.frame.size); 
UIGraphicsPopContext(); 
drawingSurface.image = UIGraphicsGetImageFromCurrentImageContext(); 
UIGraphicsEndImageContext(); 
} 

Quand je cours, je crois que mon DrawingSurface est attribuée nul parce qu'il efface tout dans l'image. Je pense que je ne peux pas utiliser pop et pousser de cette façon. Mais je n'arrive pas à comprendre comment sauvegarder le contexte et le repousser sur la surface de dessin. Hmmmm. Toute aide serait ... eh bien ... utile. Merci d'avance -

Et, juste pour référence, voici ce que je fais pour dessiner à l'écran, qui fonctionne très bien. C'est dans mon TouchesMoved:

UIGraphicsBeginImageContext(self.view.frame.size); 
CGContextRef context = UIGraphicsGetCurrentContext(); 
[drawingSurface.image drawInRect:CGRectMake(0, 0, drawingSurface.image.size.width, drawingSurface.image.size.height)]; 

CGContextSetLineCap(context, kCGLineCapRound); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound 
CGContextSetLineWidth(context, self.brush.size); // for size 

CGContextSetStrokeColorWithColor (context,[currentColor CGColor]); 

CGContextBeginPath(context); 
CGContextMoveToPoint(context, lastPoint.x, lastPoint.y); 
CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y); 
CGContextStrokePath(context); 
drawingSurface.image = UIGraphicsGetImageFromCurrentImageContext(); 
UIGraphicsEndImageContext(); 

Répondre

1

Je pense que vous approchez le problème de la mauvaise façon et des contextes confus.

Dans une API en mode immédiat, vous sauvegardez l '"état" des objets avec push/pop, pas la représentation graphique. L'état se compose de choses comme les largeurs de ligne, les couleurs et les positions. La représentation graphique est le résultat d'une opération de peinture (un bitmap) et généralement quelque chose que vous ne voulez pas enregistrer.

Essayez plutôt d'enregistrer les 'informations' que vous utilisez pour créer le dessin.

Ma suggestion initiale serait de découpler la création de votre forme et la peinture. Sous OSX, vous pouvez utiliser NSBezierPath, mais pour iOS, nous devons utiliser un tableau de points.

Par exemple donné ce protocole:

// ViewController.h 
@protocol DrawSourceProtocol <NSObject> 
- (NSArray*)pathsToDraw; 
@end 

@interface ViewController : UIViewController<DrawSourceProtocol> 
@end 

Vous pouvez implémenter ces fonctions:

// ViewController.m 
@interface ViewController() { 
    NSMutableArray *currentPath; 
    NSMutableArray *allPaths; 
    MyView *view_; 
} 
@end 

... 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    currentPath = [[NSMutableArray alloc] init]; 
    allPaths = [[NSMutableArray alloc] init];  
    view_ = (MyView*)self.view; 
    view_.delegate = self; 
} 

- (NSArray*)pathsToDraw { 
    // Return the currently draw path too 
    if (currentPath && currentPath.count) { 
    NSMutableArray *allPathsPlusCurrent = [[NSMutableArray alloc] initWithArray:allPaths]; 
    [allPathsPlusCurrent addObject:currentPath]; 
    return allPathsPlusCurrent; 
    } 
    return allPaths; 
} 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    currentPath = [[NSMutableArray alloc] init]; 
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    // When a touch ends, save the current path 
    [allPaths addObject:currentPath]; 
    currentPath = [[NSMutableArray alloc] init]; 
    [view_ setNeedsDisplay];  
} 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 
    UITouch *touch = [touches anyObject]; 
    CGPoint currentPoint = [touch locationInView:self.view]; 

    // We store the point with the help of NSValue 
    [currentPath addObject:[NSValue valueWithCGPoint:currentPoint]]; 

    // Update the view 
    [view_ setNeedsDisplay]; 
} 

sous-classe maintenant votre point de vue (je l'appelle le mien MyView ici) et mettre en œuvre quelque chose comme ceci:

// MyView.h 
#import "ViewController.h" 

@protocol DrawSourceProtocol; 

@interface MyView : UIView { 
    __weak id<DrawSourceProtocol> delegate_; 
} 
@property (weak) id<DrawSourceProtocol> delegate; 
@end 

// MyView.m 

@synthesize delegate = delegate_; 

... 

- (void)drawRect:(CGRect)rect { 
    NSLog(@"Drawing!"); 

    // Setup a context 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor); 
    CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0); 
    CGContextSetLineWidth(context, 2.0); 

    // Get the paths 
    NSArray *paths = [delegate_ pathsToDraw]; 

    for (NSArray *aPath in paths) { 
    BOOL firstPoint = TRUE; 
    for (NSValue *pointValue in aPath) { 
     CGPoint point = [pointValue CGPointValue]; 

     // Always move to the first point 
     if (firstPoint) { 
     CGContextMoveToPoint(context, point.x, point.y); 
     firstPoint = FALSE; 
     continue; 
     } 

     // Draw a point 
     CGContextAddLineToPoint(context, point.x, point.y); 
    } 
    } 

    // Stroke! 
    CGContextStrokePath(context); 
} 

Le seul inconvénient ici est que setNeedsDisplay n'est pas très performant. Il est préférable d'utiliser setNeedsDisplayInRect :, voir mon dernier post concernant an efficient way of determining the 'drawn' rect.

Comme pour annuler? Votre opération d'annulation consiste simplement à faire sauter le dernier objet du tableau allPaths. Cet exercice je vais vous laisser à :)

Espérons que cela aide!

+1

Une réponse fantastique et réfléchie. Je ne sais pas pourquoi OP n'a pas accepté cela. – Tim

Questions connexes