drawViewHierarchyInRect в фоновом потоке

#ios #multithreading #uiview #screenshot

#iOS #многопоточность #пользовательский просмотр #скриншот

Вопрос:

Я пытаюсь выполнить некоторый «закадровый рендеринг» в фоновом потоке, чтобы обновить предварительный просмотр дизайнерского приложения, которое я создаю.

Я использовал renderInContext для этого производную от NSOperationQueue очередь, но обратите внимание, что это медленно.

Итак, я начал использовать drawViewHierarchyInRect , который отлично работает и намного быстрее. Однако я заметил, что мой пользовательский интерфейс полностью блокируется при запуске этого метода в b / g.

Если я сделаю это в основном потоке…

     UIView *preview = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
    preview.backgroundColor = [UIColor redColor];

    UIGraphicsBeginImageContextWithOptions(preview.bounds.size, NO, 0.0);

    BOOL ok = [preview drawViewHierarchyInRect:preview.bounds afterScreenUpdates:YES];

    UIImage *img = nil;

    if( ok )
    {
        img = UIGraphicsGetImageFromCurrentImageContext();
    }

    UIGraphicsEndImageContext();
  

… все работает нормально.

Однако, если я (скажем) отправлю это…

 if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW) == 0) 
{
    dispatch_async(renderQueue, ^{
        // capture
        UIView *preview = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
        preview.backgroundColor = [UIColor redColor];

        UIGraphicsBeginImageContextWithOptions(preview.bounds.size, NO, 0.0);

        BOOL ok = [preview drawViewHierarchyInRect:preview.bounds afterScreenUpdates:YES];

        UIImage *img = nil;

        if( ok )
        {
            img = UIGraphicsGetImageFromCurrentImageContext();
        }

        UIGraphicsEndImageContext();
}
        dispatch_semaphore_signal(semaphore);
    });
  

… мой пользовательский интерфейс зависает полностью после выполнения.

‘ok’ возвращает YES, так что, похоже, все работает.

Если я установлю для afterUpdates значение NO, ‘ok’ — это NO (сбой), но пользовательский интерфейс продолжает реагировать.

Существуют ли какие-либо ограничения на использование drawViewHierarchyInRect в чем-либо, кроме основного потока?

Комментарии:

1. Вы решили это?

Ответ №1:

Объектами UIKit следует манипулировать только в основном потоке. В документации UIKit от Apple упоминается это высказывание

По большей части, используйте классы UIKit только из основного потока вашего приложения. Это особенно верно для классов, производных от UIResponder или которые каким-либо образом манипулируют пользовательским интерфейсом вашего приложения.

Ответ №2:

Вызываете ли вы вышеуказанное dispatch_async из основного потока? моя теория здесь заключается в том, что вы вызвали [preview drawViewHierarchyInRect:preview.bounds afterScreenUpdates:YES]; с YES , которому необходимо выполнить некоторый код в основном потоке, однако основной поток заблокирован на семафоре. Что вы, вероятно, хотите сделать, так это время от времени переходить к основному потоку, чтобы drawViewHierarchyInRect можно было завершить.

 __block dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_async(renderQueue, ^{
    /// your code here
    /// ...
   BOOL ok = [preview drawViewHierarchyInRect:preview.bounds afterScreenUpdates:YES];
    .....
    dispatch_semaphore_signal(sema);
});

while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW)) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}