2010-08-02 10 views
6

Tôi có ứng dụng vẽ mà tôi muốn tạo phương thức hoàn tác. Bản vẽ diễn ra bên trong phương thức TouchesMoved:Tiết kiệm CGContextRef

Tôi đang cố gắng tạo một CGContextRef và đẩy nó vào ngăn xếp HOẶC lưu nó vào một thuộc tính ngữ cảnh có thể được khôi phục sau nhưng không có bất kỳ may mắn nào. Bất cứ lời khuyên nào cũng tuyệt vời cả. Dưới đây là những gì tôi có ...

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

Sau đó, tôi có một phương pháp kích hoạt bởi nút undo tôi ...

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

Khi tôi chạy này, tôi tin rằng drawingSurface của tôi đang được giao bằng không bởi vì nó chỉ xóa mọi thứ trong hình ảnh.

Tôi đoán là tôi không thể sử dụng pop và đẩy theo cách này. Nhưng tôi không thể tìm ra cách để lưu bối cảnh và sau đó đẩy nó trở lại bản vẽSurface. Hmmmm. Bất kỳ sự giúp đỡ nào cũng sẽ ... tốt ... hữu ích. Cảm ơn trước -

Và, chỉ để tham khảo, dưới đây là những gì tôi đang làm để vẽ lên màn hình đang hoạt động tuyệt vời. Đây là bên trong TouchesMoved của tôi:

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

Trả lời

1

Tôi nghĩ rằng bạn đang tiếp cận vấn đề theo cách sai và bối cảnh khó hiểu.

Trong API chế độ tức thì, bạn lưu trạng thái 'của các đối tượng bằng push/pop, chứ không phải biểu diễn đồ họa. Nhà nước bao gồm những thứ như độ rộng đường kẻ, màu sắc và vị trí. Các đại diện đồ họa là kết quả của một hoạt động sơn (một bitmap) và thường là một cái gì đó bạn không muốn lưu.

Thay vào đó, hãy thử lưu 'thông tin' mà bạn sử dụng để tạo bản vẽ.

Đề xuất ban đầu của tôi là tách riêng việc tạo và vẽ hình của bạn. Trên OSX, bạn có thể sử dụng NSBezierPath, nhưng đối với iOS, chúng tôi phải sử dụng một mảng các điểm.

Ví dụ cho giao thức này:

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

@interface ViewController : UIViewController<DrawSourceProtocol> 
@end 

Bạn có thể thực hiện các chức năng:

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

Bây giờ phân lớp quan điểm của bạn (tôi gọi tôi MyView đây) và thực hiện một cái gì đó như thế này:

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

Lỗi duy nhất ở đây là setNeedsDisplay không thực sự hiệu quả. Tốt hơn nên sử dụng setNeedsDisplayInRect :, xem bài đăng cuối cùng của tôi về an efficient way of determining the 'drawn' rect.

Để hoàn tác? Hoạt động hoàn tác của bạn chỉ đơn thuần là popping đối tượng cuối cùng từ mảng allPaths. Bài tập này tôi sẽ để lại cho bạn :)

Hy vọng điều này sẽ hữu ích!

+1

Câu trả lời tuyệt vời, được suy nghĩ cẩn thận. Không chắc tại sao OP không chấp nhận điều này. – Tim