как отобразить UIActivityIndicatorView ДО начала вращения - PullRequest
0 голосов
/ 12 августа 2011

Я хотел бы отобразить индикатор активности ДО работы, выполняемой willAnimateRotationToInterfaceOrientation: duration: начинается. Большую часть времени в моем приложении эта работа быстро завершается, и в ней не требуется указатель активности, но иногда (первая ротация, т.е. до того, как я кэшировал данные, при работе с большим файлом) может быть заметная задержка , Вместо того, чтобы перепроектировать мое приложение, чтобы справиться с этим необычным случаем, я бы просто показал UIActivityIndicatorView , пока приложение генерирует кэш и обновляет отображение.

Проблема заключается в том (или кажется, что), что дисплей не обновляется между willRotateToInterfaceOrientation: duration и willAnimateRotationToInterfaceOrientation: duration: метод. Таким образом, запрос iOS показать UIActivityIndicator представление в методе willRotate фактически не влияет на отображение до тех пор, пока не будет завершен willAnimateRotation метод.

Следующий код иллюстрирует проблему. При запуске индикатор активности появляется очень ненадолго, и ПОСЛЕ того, как метод simulateHardWorkNeededToGetDisplayInShapeBeforeRotation завершен.

Я что-то упускаю из виду? И если нет, какие-нибудь умные идеи относительно того, как я мог бы обойти эту проблему?

Обновление : Хотя предложения о переносе тяжелой работы в другую ветку и т. Д., Как правило, полезны, в моем конкретном случае я хочу заблокировать основной поток , чтобы мой подъем. В приложении у меня есть tableView, все высоты которого необходимо пересчитать. Когда - что не очень распространенный вариант использования, или я бы даже не рассматривал этот подход - строк очень много, все новые высоты вычисляются (и затем кэшируются) во время [tableView reloadData]. Если я произвожу отключение подъема и продолжу вращение, то после поворота и до подъема мой tableView не был повторно загружен. Например, в случае портрета и пейзажа он не занимает всю ширину. Конечно, есть и другие обходные пути, например, построение tableView всего за несколько строк до поворота, а затем перезагрузка реального поверх этого и т. д.

Пример кода для иллюстрации проблемы:

@implementation ActivityIndicatorViewController

@synthesize activityIndicatorView = _pgActivityIndicatorView;
@synthesize label = _pgLabel;

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}


- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
{
    NSLog(@"willRotate");

    [self showActivityIndicatorView];
}


- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
{
    NSLog(@"willAnimateRotation");
    [self simulateHardWorkNeededToGetDisplayInShapeBeforeRotation];
}


- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
    NSLog(@"didRotate");
    [self hideActivityIndicatorView];
}


- (void) simulateHardWorkNeededToGetDisplayInShapeBeforeRotation;
{
    NSLog(@"Starting simulated work");
    NSDate* date = [NSDate date];

    while (fabs([date timeIntervalSinceNow]) < 2.0)
    {
        //
    }
    NSLog(@"Finished simulated work");
}


- (void) showActivityIndicatorView;
{
    NSLog(@"showActivity");
    if (![self activityIndicatorView])
    {
        UIActivityIndicatorView* activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        [self setActivityIndicatorView:activityIndicatorView];
        [[self activityIndicatorView] setCenter:[[self view] center]];
        [[self activityIndicatorView] startAnimating];
        [[self view] addSubview: [self activityIndicatorView]];
    }

    // in shipping code, an animation with delay would be used to ensure no indicator would show in the good cases
    [[self activityIndicatorView] setHidden:NO];
}

- (void) hideActivityIndicatorView;
{
    NSLog(@"hideActivity");
    [[self activityIndicatorView] setHidden:YES];
}


- (void) dealloc;
{
    [_pgActivityIndicatorView release];
    [super dealloc];
}


- (void) viewDidLoad;
{
    UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(50.0, 50.0, 0.0, 0.0)];
    [label setText:@"Activity Indicator and Rotate"];
    [label setTextAlignment: UITextAlignmentCenter];
    [label sizeToFit];
    [[self view] addSubview:label];
    [self setLabel:label];
    [label release];
}

@end

Ответы [ 4 ]

1 голос
/ 12 августа 2011

Еще копание (сначала в «Программировании iPhone 4» Мэтта Нойберга), а затем этот полезный вопрос о том, как заставить Core Animation запускать поток из stackoverflow , и у меня есть решение, которое, кажется, работает хорошо.И Нойберг, и Apple настороженно относятся к этому подходу из-за возможного появления нежелательных побочных эффектов.До сих пор при тестировании все было в порядке для моего конкретного случая.

Изменение приведенного выше кода следующим образом реализует это изменение.Ключевым добавлением является [CATransaction flush] , заставляющее UIActivityIndicatorView начать отображение, даже если цикл выполнения не будет завершен, пока не завершится willAnimateRotationToInterfaceOrientation: duration метод.1010 *

1 голос
/ 12 августа 2011

Приложение не обновляет экран, отображая UIActivityIndicatorView, пока основной цикл выполнения не восстановит управление.Когда происходит событие поворота, методы willRotate... и willAnimateRotation... вызываются за один проход через главный цикл выполнения.Таким образом, вы блокируете метод тяжелой работы перед отображением индикатора активности.

Чтобы сделать эту работу, вам нужно перенести тяжелую работу на другой поток.Я бы поместил вызов метода тяжелой работы в метод willRotate....Этот метод будет вызывать этот контроллер представления, когда работа будет завершена, чтобы представление могло быть обновлено.Я бы поставил показывать индикатор активности в методе willAnimateRotation....Я не стал бы беспокоиться о методе didRotateFrom....Я рекомендую прочитать Руководство по многопоточному программированию .

Изменить в ответ на комментарий: Вы можете эффективно блокировать взаимодействие с пользователем, если метод willAnimateRotation... не работаетинтерфейс на экране, такой как вид, показывающий наложение темноты и UIActivityIndicatorView.Затем, когда тяжелая работа будет выполнена, это наложение будет удалено, и интерфейс снова станет активным.Тогда у кода рисунка будет возможность правильно добавить и анимировать индикатор активности.

0 голосов
/ 12 августа 2011

Либо выполните тяжелую работу в фоновом потоке и опубликуйте результаты в потоке переднего плана для обновления пользовательского интерфейса (UIKit является безопасным только для потоков начиная с iOS 4.0):

[self performSelectorInBackground:@selector(simulateHardWorkNeededToGetDisplayInShapeBeforeRotation) withObject:nil]

Или вы можете запланировать способ поднятия тяжестей после вращения:

[self performSelector:@selector(simulateHardWorkNeededToGetDisplayInShapeBeforeRotation) withObject:nil afterDelay:0.4]

Но это всего лишь взломы, и реальное решение состоит в том, чтобы иметь надлежащую фоновую обработку, если ваш пользовательский интерфейс требует интенсивной обработки для обновления, может ли это быть в портретной или альбомной ориентации. NSOperation и NSOperationQueue - хорошее место для начала.

0 голосов
/ 12 августа 2011

Попробуйте выполнить свою работу во втором потоке после отображения представления активности.

[self showActivityIndicatorView];
[self performSelector:@selector(simulateHardWorkNeededToGetDisplayInShapeBeforeRotation) withObject:nil afterDelay:0.01];
...