【问题标题】:Saving CGContextRef保存 CGContextRef
【发布时间】:2010-08-02 20:30:36
【问题描述】:

我有一个绘图应用程序,我想在其中创建一个撤消方法。绘图发生在 TouchesMoved: 方法中。

我正在尝试创建一个 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。但我似乎无法弄清楚如何保存上下文,然后将其推回绘图表面。嗯。任何帮助都会……嗯……有帮助。提前致谢 -

而且,仅供参考,这是我在屏幕上绘制的内容,效果很好。这是在我的 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();

【问题讨论】:

    标签: objective-c uiview quartz-2d cgcontext


    【解决方案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 数组中弹出最后一个对象。这个练习我会留给你:)

    希望这会有所帮助!

    【讨论】:

    • 绝妙的、深思熟虑的答案。不知道为什么 OP 不接受这个。
    猜你喜欢
    • 1970-01-01
    • 2012-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多