2010-08-02 1 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 и нажимать таким образом. Но я не могу понять, как просто сохранить контекст, а затем вернуть его обратно на рисунок. Хммм. Любая помощь будет ... ну ... полезной. Заранее спасибо -

И только для справки, вот что я делаю, чтобы рисовать на экране, который отлично работает. Это внутри моего 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