我正在尝试在 UIImage 中绘制几个 UIView ,并且我正在使用 drawRect: 手动完成。鉴于图像是从网络动态下载的,我创建了一个 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 模型的自定义单元格,所以我的单元格的父类(super class)使:
- (void)drawRect:(CGRect)r
{
    [(ABTableViewCell *)[self superview] drawContentView:r];
}
drawContentView: 中的代码在父类(super class)的 drawRect: 中被绘制。任何提示?

最佳答案

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

所以,你可以做什么?其实应该比较容易。事实上,Anomie 的回答已经告诉你了。您需要做的就是在您知道自己有上下文的 drawContentView: 中调用图像上的 draw 方法。在 loadPreview 方法中,您调用 setNeedsDisplay *,并在 drawContentView: 中检查 preview 是否为 nil ,如果不是则绘制。只是为了扩展 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 的创建和调度移出绘图方法。建议所有绘图方法只做他们需要做的事情,并尽快终止。

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

关于objective-c - drawRect中上下文和线程之间的问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6025682/

10-14 11:50
查看更多