#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]];
}