drawRect中上下文和线程之间的问题

时间:2011-05-17 02:42:51

标签: objective-c multithreading ios uiview drawrect

我正在尝试在UIImage中绘制几个UIView,我正在用drawRect:手动完成。鉴于图像是从Web动态下载的,我创建了一个NSOperation并从第二个线程执行图像加载代码,以保持UI响应。因此,一旦下载了图像,它们就会出现在屏幕上。

但是......对于每张图片,我都会收到以下错误:

<Error>: CGContextSaveGState: invalid context 0x0
<Error>: CGContextSetBlendMode: invalid context 0x0
<Error>: CGContextSetAlpha: invalid context 0x0
<Error>: CGContextTranslateCTM: invalid context 0x0
<Error>: CGContextScaleCTM: invalid context 0x0
<Error>: CGContextDrawImage: invalid context 0x0
<Error>: CGContextRestoreGState: invalid context 0x0

对于我所看到的,这意味着我尝试[image drawInRect...]的上下文是nil,因为我不再在drawRect:

尝试做

UIGraphicsPushContext(UIGraphicsGetCurrentContext());
在绘制图像之前

但没有任何改变。我怎么能克服这件事?多线程是响应的必要条件,上下文在路上迷失。

更新:这是我的代码:

- (void)drawContentView:(CGRect)r
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    ....
    CGContextFillRect(context, r);
    UIApplication* app = [UIApplication sharedApplication];
        app.networkActivityIndicatorVisible = YES;


    NSOperationQueue *queue = [NSOperationQueue new];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc]
                                            initWithTarget:self 
                                            selector:@selector(loadPreview) 
                                            object:nil];
    [queue addOperation:operation];
    [operation release];
}

然后

-(void)loadPreview
{
    self.preview = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString:self.picStringPreview]]];
    [self.preview drawInRect:CGRectMake(256, 18, 80, 65)];
}

我尝试在UIGraphicsPushContext...中添加loadPreview,即使在那里[self performSelectorOnMainThread...]也没有。这是一个基于Atebit模型的自定义单元格,因此我的单元格的超类使:

- (void)drawRect:(CGRect)r
{
    [(ABTableViewCell *)[self superview] drawContentView:r];
}

并且drawContentView:中的代码在超类的drawRect:中被绘制。任何提示?

2 个答案:

答案 0 :(得分:3)

当你开始绘制图像时,你对上下文的了解非常正确。 UIKit在调用drawRect:之前为您的视图设置绘图上下文,之后它被销毁。作为一般规则,在您从框架类继承的draw...方法中,只有并且始终是为您设置的上下文。

那你能做什么?它实际上应该很容易。事实上,Anomie的回答已经告诉过你了。您需要做的就是在drawContentView:中调用图像上的draw方法,在那里您知道自己有上下文。在loadPreview方法中,您拨打setNeedsDisplay *,在drawContentView:中检查previewnil,如果没有,则绘制。{只是为了扩展Anomie的答案,这里的代码应该是这样的:

-(void)loadPreview
{
    self.preview = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString:self.picStringPreview]]];
    [self setNeedsDisplay];
}

- (void)drawContentView:(CGRect)r
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    ....
    CGContextFillRect(context, r);
    UIImage * prvw = self.preview;    // Avoid making two method calls
    if( prvw ){
        [prvw drawInRect:CGRectMake(256, 18, 80, 65)];
    }
    // etc.

您最好还是可以创建和调度绘图方法的NSOperation out 。建议所有绘图方法仅 他们需要做什么,并尽快终止。


*我认为Anomie可能需要通过performSelector:...onMainThread:来调用,因为setNeedsDisplay没有直接调用drawRect:,只是设置了一个标志。

答案 1 :(得分:2)

最简单的方法是不要弄乱drawRect:,而是将图像放在UIImageViews中。 Apple声称即使是空的drawRect:也会改变某些东西,或者使其变得更慢。

但如果必须,你必须将图像保存到班级中的ivars(不要忘记保留它们),调用setNeedsDisplaysetNeedsDisplayInRect:(在主线程上,所以您需要使用performSelector:withObject:onMainThread:(如果您还没有)通知系统它需要再次呼叫您的drawRect:,然后再次调用drawRect:时使用保存的图像画画。