2013-05-14 2 views
2

У меня есть пользовательский подкласс UICollectionViewCell, где я рисую с обрезкой, поглаживанием и прозрачностью. Он отлично работает на Simulator и iPhone 5, но на более старых устройствах заметны проблемы с производительностью.Рисование в другом потоке с CGImage/CGLayer

Так что я хочу переместить отвлекающий много времени рисунок в фоновый поток. Поскольку метод -drawRect всегда вызывается в основном потоке, я закончил тем, что сохранил обратный контекст в CGImage (исходный вопрос содержал код с использованием CGLayer, но он отчасти устарел, как указал Мэтт Лонг).

Вот моя реализация метода DrawRect внутри этого класса:

-(void)drawRect:(CGRect)rect { 

    CGContextRef ctx = UIGraphicsGetCurrentContext(); 
    if (self.renderedSymbol != nil) { 
     CGContextDrawImage(ctx, self.bounds, self.renderedSymbol); 
    } 
} 

метод рендеринга, который определяет это свойство renderedSymbol:

- (void) renderCurrentSymbol { 

    [self.queue addOperationWithBlock:^{ 

// creating custom context to draw there (contexts are not thread safe) 
     CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); 
     CGContextRef ctx = CGBitmapContextCreate(nil, self.bounds.size.width, self.bounds.size.height, 8, self.bounds.size.width * (CGColorSpaceGetNumberOfComponents(space) + 1), space, kCGImageAlphaPremultipliedLast); 
     CGColorSpaceRelease(space); 

// custom drawing goes here using 'ctx' context 

// then saving context as CGImageRef to property that will be used in drawRect 
     self.renderedSymbol = CGBitmapContextCreateImage(ctx); 

// asking main thread to update UI  
     [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
      [self setNeedsDisplayInRect:self.bounds]; 
     }]; 

     CGContextRelease(ctx); 

    }]; 
} 

Эта установка работает отлично на главном потоке, но когда я обернуть его с NSOperationQueue или GCD, я получаю много разных «недопустимых контекстов 0x0». Приложение не разбивается, но рисования не происходит. Я полагаю, что существует проблема с выпуском настраиваемого CGContextRef, но я не знаю, что с этим делать.

Вот мои объявления о собственности. (Я попытался с помощью атомных версий, но это не помогло)

@property (nonatomic) CGImageRef renderedSymbol; 
@property (nonatomic, strong) NSOperationQueue *queue; 
@property (nonatomic, strong) NSString *symbol; // used in custom drawing 

Пользовательские сеттеры/добытчиками для свойств:

-(NSOperationQueue *)queue { 
    if (!_queue) { 
     _queue = [[NSOperationQueue alloc] init]; 
     _queue.name = @"Background Rendering"; 
    } 
    return _queue; 
} 
-(void)setSymbol:(NSString *)symbol { 
    _symbol = symbol; 
    self.renderedSymbol = nil; 
    [self setNeedsDisplayInRect:self.bounds]; 
} 

-(CGImageRef) renderedSymbol { 
    if (_renderedSymbol == nil) { 
     [self renderCurrentSymbol]; 
    } 
    return _renderedSymbol; 
} 

Что я могу сделать?

+2

Ваш первый 'drawInRect' в конечном итоге приведет к созданию NULL-'CGLayer', поскольку вы вызываете' renderCurrentSymbol' асинхронным в 'renderedSymbol', но без задержки возвращаете' _renderedSymbol'. –

+0

Вы пытались не делать CGContextRelease (ctx), пока не освободите CGLayer (предположительно, в dealloc?) –

+0

@MikePollard Освобождение контекста является правильным, если вы не освободите контекст, у вас будет Memoryleak. –

ответ

1

Проблема решена путем использования удивительной сторонней библиотеки Mind Snacks - MSCachedAsyncViewDrawing.

1

Вы заметили, что документ на CGLayer, на который вы ссылаетесь, не обновлялся с 2006 года? Предположение о том, что CGLayer является правильным решением, неверно. Apple полностью отказалась от этой технологии, и вы, вероятно, тоже должны: http://iosptl.com/posts/cglayer-no-longer-recommended/ Использовать Core Animation.

+0

Whoa. Благодаря! Перед CGLayer я попытался сделать то же самое с сохранением контекста в CGImageRef (как описано в этом вопросе в [Переполнение стека] (http://stackoverflow.com/questions/14606943/asynchronous-drawing-and-touches)), но он не сделал Не работай. Все еще получаю это ** 0x0 недопустимый контекст ** ошибки –

+0

Я бы не сказал, что CGLayer - это не правильное решение, это может быть не так, но это может быть. –

+0

@RinatKhanov Я предлагаю вам задать свой вопрос с более высокого уровня и посмотреть, что, по мнению людей, лучший подход. Из вашего вопроса это звучит для меня так же, как вы могли бы просто собрать несколько слоев Core Animation и позволить ему сделать всю тяжелую работу для вас. Я создал множество пользовательских коллекций, и они использовали такие вещи, как поглаживание и непрозрачность. Возможно, вы захотите посмотреть свойство canRasterize CALayer. Это улучшит производительность чертежа. Возможно, вы даже рассмотрите возможность использования изображений вместо рисования программно, так как это также может повысить производительность. –