【问题标题】:CGcontext and drawRect not drawingCGcontext 和 drawRect 不绘图
【发布时间】:2014-02-10 07:55:56
【问题描述】:

需要一双新的眼睛,我的眼睛已经停止工作,无法再理解我自己的代码了......

我正在尝试使用笔在纸上的绘图风格制作绘图应用程序。以下是它的工作原理:

  • 用户触摸,应用获取位置
  • 设置了一个 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' 没有获取当前图形的内容并显示它们

__旧,我想不再需要,但留在这里以供参考___

这里是相关代码:

- (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. 为什么这不是绘图?我什么也没得到,但我可以看到我的内存正在被使用......

【问题讨论】:

    标签: ios objective-c drawrect cgcontext


    【解决方案1】:

    埃加德。

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

    但是,也不要,呃,使用那个 _drw 开关。这不好。

    您想创建一个图像并在触摸发生时绘制到图像中,然后在 drawRect: 中将图像拉到屏幕上。如果你发现自己在-drawRect; 中调用 UIGraphicsGetImageFromCurrentImageContext(),你就是在倒退(就像你在这里一样)。不要从屏幕 啜饮图像,创建一个您将 屏幕的图像。

    屏幕不应该是你的“状态”。那就是疯狂。

    【讨论】:

    • 啊,好吧!因此,您显然可以看出我可能对此有点陌生,谢谢您的提示! (setneeds display 是有道理的)我认为我必须使用某种逻辑,因为每次我尝试在 drawRect 方法之外设置任何类型的 CGcontext 时,我都可以使用一个 CGcontext '无论我试图做什么' 无效的上下文错误.. .感谢您的澄清。所以我的新查询是:如何创建并仅更新图像的某些部分(即:添加新绘制的线条)但也改变所有部分(例如将其擦干净,或更改每条线的颜色)?无论如何,感谢您的帮助!
    【解决方案2】:

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

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

    为缓解性能问题,仅重绘最近一次触摸周围的区域(或最近一次触摸与上一次触摸之间的区域)。

    【讨论】:

    • 是的,我有一种预感,状态管理很糟糕......但就像我说的,我在尝试将内容添加到 drawRect 方法之外的上下文时遇到错误......我的想法是它是否清除任何内容都没有关系,因为所有绘制的点都将缓存在用户背后绘制的图像中(所以对他们来说,看起来他们只是在添加到画布上)不过只是一个问题,我将如何找到只需要重新绘制哪些部分并重新绘制那些部分?
    • 如果在drawRect: (CGBitmapContextCreate) 之外,您需要创建自己的上下文。重绘基于当前点和用户之前/正在触摸的点(因为那是创建新线段的位置)。
    • 好吧,可能是我现在很累,没有好好思考,但我该怎么做呢?抱歉,我对重新绘制的工作原理没有正确理解。
    • stackoverflow.com/questions/8100845/…(创建要重绘的区域)。我会保留一个实例变量,即“当前路径”,在用户触摸时创建,在用户移除触摸时修改和销毁。这在触摸移动以创建/更新图像时使用(这可能会证明效率有点低,请仅在触摸结束时创建新图像)。绘制 rect 只是绘制图像(也可能是当前路径)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多