CGcontext和drawRect没有绘图

时间:2014-02-10 07:55:57

标签: ios objective-c drawrect cgcontext

需要一双新鲜的眼睛,我已停止工作,不能理解我自己的代码...

我正在尝试使用纸笔在绘图样式上制作绘图应用程序。以下是它的工作方式:

  • 用户触摸,app抓取位置
  • 设置var以告诉drawRect配置CGcontext,创建新路径,移动到点等等(因为每当我使用drawRect方法之外的CGcontext做任何事情时,我总是在我的nslog中获取错误/警告)
  • 然后设置
  • var以确定当用户抬起手指时是否放置点或线
  • 如果用户拖动,则更改var以告诉drawRect绘制一条线并调用[self setneedsdisplay]
  • 每当用户将手指放在屏幕上时,每隔5秒(或直到他们抬起手指)激活计时器,应用程序就会捕获'屏幕内容,将其粘贴在图像中并擦拭屏幕,替换图像并继续绘图(实际缓存图像以便所有这些线条不必重新绘制)

更新#1 所以我重新写了它(因为它显然非常糟糕而且没有工作......)并最终得到了这个:

- (void)drawRect:(CGRect)rect {
    [_incrImage drawInRect:rect]; /* draw image... this will be blank at first 
    and then supposed to be filled by the Graphics context so that when the view 
    is refreshed the user is adding lines ontop of an image (but to them it looks 
    like a continuous drawing)*/
    CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapSquare);
    CGContextSetLineJoin(UIGraphicsGetCurrentContext(), kCGLineJoinRound);
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(), _brushW);
    CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), [UIColor colorWithRed:_brushR green:_brushG blue:_brushB alpha:_brushO].CGColor);
    CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
    CGContextAddPath(UIGraphicsGetCurrentContext(), _pathref);
    CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathStroke);
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    _drw = 2; //set this to draw a dot if the user taps
    UITouch *touch = [touches anyObject];
    _cp = [touch locationInView:self];
    _lp = _cp;
    CGPathRelease(_pathref); /* this line and the line below is to clear the 
    path so the app is drawing as little as possible (the idea is that as 
    the user draws and lifts their finger, the drawing goes into _incrImage 
    and then the contents is cleared and _incrImage is applied to the 
    screen so there is as little memory being used as possible and so the 
    user feels as if they're adding to what they've drawn when they touch 
    the device again) */
    _pathref = CGPathCreateMutable();
    CGPathMoveToPoint(_pathref, NULL, _lp.x, _lp.y);
    touch = nil;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    _drw = 1; //user moved their finger, this was not a tap so they want 
              //to draw a line
    UITouch *touch = [touches anyObject];
    _cp = [touch locationInView:self];
    CGPathAddLineToPoint(_pathref, NULL, _cp.x, _cp.y);
    [self setNeedsDisplay]; //as the user moves their finger, it draws
    _lp = _cp;
    touch = nil;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    _cp = [touch locationInView:self];
    switch (_drw) { //logic to determine what to draw when the user 
                    //lifts their finger
         case 1:
            //line
            CGPathAddLineToPoint(_pathref, NULL, _cp.x, _cp.y);

            break;
         case 2:
            //dot
            CGPathAddArc(_pathref, NULL, _cp.x, _cp.y, _brushW, 0.0, 360.0, 1);

            break;

            default:
            break;
    }
    /* here's the fun bit, the Graphics context doesn't seem to be going 
    into _incrImage and therefore is not being displayed when the user 
    goes to continue drawing after they've drawn a line/dot on the screen */
    UIGraphicsBeginImageContext(self.frame.size);
    CGContextAddPath(UIGraphicsGetCurrentContext(), _pathref); /* tried adding
    my drawn path to the context and then adding the context to the image 
    before the user taps down again and the path is cleared... not sure 
    why it isn't working. */
    [_incrImage drawAtPoint:CGPointZero];
    _incrImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [self setNeedsDisplay]; //finally, refresh the contents
    touch = nil;
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [self touchesEnded:touches withEvent:event];
}

我的新问题是,当调用drawRect并且' _incrImage'没有得到当前图形的内容并显示它们

_ _old,我想不再需要,但请留待参考_ _ _

这是相关代码:

- (void)drawRect:(CGRect)rect {
/* REFERENCE: _drw: 0 = clear everything
                    1 = cache the screen contents in the image and display that
                        so the device doesn't have to re-draw everything
                    2 = set up new path
                    3 = draw lines instead of a dot at current point
                    4 = draw a 'dot' instead of a line
*/
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapSquare);
CGContextSetLineJoin(UIGraphicsGetCurrentContext(), kCGLineJoinRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), _brushW);
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), [UIColor colorWithRed:_brushR green:_brushG blue:_brushB alpha:_brushO].CGColor);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);

switch (_drw) {
    case 0:
        //clear everything...this is the first draw... it can also be called to clear the view
        UIGraphicsBeginImageContext(self.frame.size);
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
        CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [UIColor clearColor].CGColor);
        CGContextFillRect(UIGraphicsGetCurrentContext(), self.frame);
        CGContextFlush(UIGraphicsGetCurrentContext());
        _incrImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        [_incrImage drawAtPoint:CGPointZero];
        [_incrImage drawInRect:rect];
        break;
    case 1:
        //capture the screen content and stick it in _incrImage... 
        then apply_incrImage to screen so the user can continue drawing ontop of it
        _incrImage = UIGraphicsGetImageFromCurrentImageContext();
        [_incrImage drawAtPoint:CGPointZero];
        [_incrImage drawInRect:rect];
        break;
    case 2:
        //begin path and set everything up, this is called when touchesBegan: fires...
        _incrImage = UIGraphicsGetImageFromCurrentImageContext();
        [_incrImage drawAtPoint:CGPointZero];
        [_incrImage drawInRect:rect];
        CGContextBeginPath(UIGraphicsGetCurrentContext());
        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _p.x, _p.y);
        break;
    case 3:
        //add lines, this is after the path is created and set...this is fired when touchesMoved: gets activated and _drw is set to draw lines instead of adding dots
        CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), _p.x, _p.y);
        CGContextStrokePath(UIGraphicsGetCurrentContext());
        break;
    case 4:
        //this is fired when touchesEnd: is activated... this sets up ready if the app needs to draw a 'dot' or the arc with a fill...
        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _p.x, _p.y);
        CGContextAddArc(UIGraphicsGetCurrentContext(), _p.x, _p.y, _brushW, 0.0, 360.0, 1);
        CGContextFillPath(UIGraphicsGetCurrentContext());
        CGContextFlush(UIGraphicsGetCurrentContext());
        break;

    default:
        break;
  }
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    dTimer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(timeUP) userInfo:Nil repeats:YES];
    _drw = 2;
    UITouch *touch = [touches anyObject];
    _p = [touch locationInView:self];
    [self setNeedsDisplay];
    _drw = 4;
}
- (void)timeUP {
    _drw = 1;
    [self setNeedsDisplay];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    _drw = 3;
    UITouch *touch = [touches anyObject];
    _p = [touch locationInView:self];
    [self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event // (2)
{
    [dTimer invalidate];
    UITouch *touch = [touches anyObject];
    _p = [touch locationInView:self];
    [self setNeedsDisplay];
    [self timeUP];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [self touchesEnded:touches withEvent:event];
}

我的问题是:

  1. 这有效吗?或者有更好的方法吗? 和
  2. 为什么这不是画画?我什么也得不到,但我可以看到我的记忆被使用......

2 个答案:

答案 0 :(得分:3)

扯扯。

你的第一个问题是setNeedsDisplay没有立即绘制,只是标记了在事件结束时绘制的视图。因此,当你设置_drw = 2并调用setNeedsDisplay然后设置_drw = 4时,它只会实际调用-drawRect: _drw = 4(如果那样,因为那仍然可能不是当前事件)。

但是,也不要,呃,使用_drw开关的东西。那不好。

你想要创建一个图像并在触摸发生时绘制到图像中,然后在drawRect中:只需将图像放到屏幕上即可。如果你发现自己在-drawRect;内部调用了UIGraphicsGetImageFromCurrentImageContext(),那么你就是在向后做事(就像你在这里一样)。不要将图像屏幕上扯下来,创建一个你将移动到屏幕的图像。

屏幕永远不应该是你的“状态”。那种方式就是疯狂。

答案 1 :(得分:1)

除了我同意的@WilShipley的回答之外(不要将状态管理逻辑放在drawRect:中,只有纯重绘逻辑),你当前从不绘制绘制除了微小线以外的任何东西,因为{ {1}}从上下文中清除当前行。

上下文并非旨在成为不完整绘图操作的临时缓存,它是您的屏幕门户(或后备图像/ PDF文件/ ...)。您需要在CGContextStrokePath之外创建绘图状态,然后将其渲染到drawRect:内的屏幕。

为了缓解性能问题,只重绘最新触摸周围的区域(或最新触摸和上一次触摸之间的区域)。