I think you are approaching the problem in the wrong way and confusing contexts.
In the Instant Mode API, you save the "state" of objects using push / pop, not a graphical representation. A condition consists of things such as line width, color, and position. The graphical representation is the result of a drawing operation (bitmap) and, as a rule, what you do not want to save.
Instead, try to save the "information" that you use to create the drawing.
My initial suggestion was to separate your figure creation and painting. In OSX, you can use NSBezierPath, but for iOS we must use an array of points.
For example, this protocol:
// ViewController.h @protocol DrawSourceProtocol <NSObject> - (NSArray*)pathsToDraw; @end @interface ViewController : UIViewController<DrawSourceProtocol> @end
You can implement the following functions:
// 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]; }
Now subclass your view (I call my MyView here) and implement something like this:
// 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); }
The only lane here is that setNeedsDisplay is not very efficient. Better to use setNeedsDisplayInRect: see my last post regarding an effective way to define a "drawn" rectangle .
Regarding cancellation? Your undo operation simply pops the last object from the allPaths array. In this exercise I will leave you :)
Hope this helps!