2010-08-02 53 views
6

我有一个绘图应用程序,我想创建一个撤消方法。绘图发生在TouchesMoved:方法内部。保存CGContextRef

我想创建一个CGContextRef并将其推送到堆栈或将其保存在可以稍后恢复的上下文属性中,但没有任何运气。任何建议都会很棒。以下是我有...

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(); 
} 

然后我就用我的复原按钮,触发一个方法...

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

当我运行这一点,我相信我的drawingSurface被分配为零,因为它只是删除图像中的所有内容。

我的猜测是我不能用pop和push这种方式。但我似乎无法弄清楚如何保存上下文,然后将其推回到drawingSurface上。 Hmmmm。任何帮助将会......很好......有帮助。在此先感谢 -

而且,仅供参考,这是我正在做的画在屏幕上,这是伟大的工作。这是我的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(); 

回答

1

我想你接近这个问题的错误的方式和混乱的环境。

在即时模式API中,您可以使用push/pop保存对象的'状态',而不是图形表示。状态由线宽,颜色和位置组成。图形表示是绘制操作(位图)的结果,通常是您不想保存的内容。

请尝试保存用于创建图形的“信息”。

我最初的建议是解耦你的形状创建和绘画。在OSX上,您可以使用NSBezierPath,但对于iOS,我们必须使用一组点。

例如,给定此协议:

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

@interface ViewController : UIViewController<DrawSourceProtocol> 
@end 

您可以实现以下功能:

// 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]; 
} 

现在继承你的观点(我称之为我的MyView的位置),并执行这样的事情:

// 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); 
} 

这里唯一的缺点是setNeedsDisplay不是很高效。最好使用setNeedsDisplayInRect :,看看我的最后一篇关于an efficient way of determining the 'drawn' rect的帖子。

至于撤消?您的撤消操作仅仅是从allPaths数组中弹出最后一个对象。这个练习我会留给你:)

希望这会有所帮助!

+1

神奇的,深思熟虑的答案。不知道为什么OP不接受这一点。 – Tim 2012-05-31 21:35:44