Проблема между контекстом и потоком в drawRect - PullRequest
3 голосов
/ 17 мая 2011

Я пытаюсь нарисовать пару UIImage с UIView, и я делаю это вручную с drawRect:. Учитывая, что изображения динамически загружаются из Интернета, я создаю NSOperation и выполняю код загрузки изображений из второго потока, чтобы обеспечить отзывчивость пользовательского интерфейса. Поэтому после загрузки изображений они появляются на экране.

Но ... Я получаю следующие ошибки для каждого из изображений:

<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: получает draw'd в drawRect: суперкласса. Любой намек?

Ответы [ 2 ]

3 голосов
/ 18 мая 2011

Вы совершенно правы в том, что к моменту рисования изображений контекст исчезает.UIKit устанавливает контекст рисования для вашего вида перед вызовом drawRect:, а затем уничтожается.Как правило, для вас только и всегда будет установлен контекст, подобный этому, в методе draw..., который вы унаследовали от класса фреймворка.

Так что вы можете сделать?На самом деле это должно быть довольно легко.Ответ Аноми уже сказал вам, на самом деле.Все, что вам нужно сделать, это вызвать метод draw для вашего изображения обратно в drawContentView:, где вы знаете, что у вас есть контекст.В методе loadPreview вы вызываете setNeedsDisplay*, а в drawContentView: вы проверяете, является ли preview nil, рисует если нет.Просто чтобы расширить ответ Аноми, вот как должен выглядеть код:

-(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 из метода рисования.Рекомендуется, чтобы все методы рисования выполняли только , что им нужно, и завершались как можно быстрее.


* Я думаю, что Аноми может ошибаться из-за необходимости вызывать это через performSelector:...onMainThread:, потому что setNeedsDisplay не вызывает drawRect: напрямую, он просто устанавливает флаг.

2 голосов
/ 17 мая 2011

Самый простой способ сделать это - вообще не связываться с drawRect:, а вместо этого поместить свои изображения в UIImageViews. Apple утверждает, что даже пустой drawRect: меняет то или иное, что делает рендеринг намного медленнее.

Но если вам необходимо, вам придется сохранить изображения в ivars в вашем классе (не забудьте сохранить их), позвонить по номеру setNeedsDisplay или setNeedsDisplayInRect: (в главном потоке, поэтому вам понадобится использовать performSelector:withObject:onMainThread:, если вы этого еще не сделали), чтобы сообщить системе, что ей нужно снова вызвать ваш drawRect:, а затем при повторном вызове drawRect: использовать сохраненные изображения для рисования.

...