2010-01-03 6 views
2

Я могу загрузить ZIP-файл из Интернета. Последовательная обработка выполняется в connectionDidFinishLoading и работает нормально, за исключением того, что элементы UIView не обновляются. Например, я устанавливаю statusUpdate.text = @ «Uncompressing file», но это изменение не появляется до тех пор, пока не завершится connectionDidFinishLoading. Аналогично, объекты UIProgressView и UIActivityIndicatorView не обновляются до тех пор, пока этот метод не завершится.connectionDidFinishLoading - как заставить обновить UIView?

Есть ли способ принудительно обновить UIView из этого метода? Я попытался установить [self.view setNeedsDisplay], но это не сработало. Кажется, он работает в основном потоке. Все остальные команды здесь работают очень хорошо - единственная проблема заключается в обновлении пользовательского интерфейса.

Спасибо!

Обновление: Вот код, который НЕ обновляя UIView:

-(void)viewWillAppear:(BOOL)animated { 
    timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(processUpdate:) userInfo:nil repeats:YES]; 
    downloadComplete = NO; 
    statusText.text = @""; 

} 

-(void)processUpdate:(NSTimer *)theTimer { 
    if (! downloadComplete) { 
     return; 
    } 

    [timer invalidate]; 
    statusText.text = @"Processing update file."; 
    progress.progress = 0.0; 
     totalFiles = [newFiles count]; 
    for (id fileName in newFiles) { 
     count++; 
      progress.progress = (float)count/(float)totalFiles; 
     // ... process code goes here ... 
     } 
} 

В то конец ProcessUpdate, я поставил downloadComplete = YES. Эта сборка & работает без ошибок и работает так, как предполагалось, за исключением ничего обновлений в UIVIEW до тех пор, пока процесс processUpdate не завершится, а затем все будет обновляться сразу.

Спасибо за помощь!

ответ

0

Проблема заключалась в том, что пользовательский интерфейс не обновляется в цикле for(). См. the answer in this thread для простого решения!

0

Пока вы находитесь в соединенииDidFinishLoading ничего не происходит в цикле запуска приложения. Контроль должен быть возвращен в цикл выполнения, чтобы он мог организовать обновление пользовательского интерфейса.

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

Приложение перезвонит вашим представлениям, предоставив им возможность обновить их содержимое позже в цикле выполнения. Внедрите drawRect в свои собственные пользовательские представления.

0

Если вы получаете connectionDidFinishLoading в основной теме, вам не повезло. Если вы не вернетесь из этого метода, в пользовательском интерфейсе ничего не будет обновлено.

С другой стороны, если вы запускаете соединение в отдельном потоке, то вы смело можете обновить пользовательский интерфейс, используя следующий код:

UIProgressView *prog = ... <your progress view reference> ... 
[prog performSelectorOnMainThread:@selector(setProgress:) 
         withObject:[NSNumber numberWithFloat:0.5f] 
        waitUntilDone:NO]; 

Будьте осторожны, чтобы не обновить пользовательский интерфейс от неосновного thread - всегда используйте метод performSelectorOnMainThread!

1

Как сказал Нильс, вы должны вернуть управление циклу запуска, если хотите просмотреть обновление просмотров. Но не начинайте отсоединять новые потоки, если вам это действительно нужно. Я рекомендую этот подход:

- (void)connectionDidFinishLoading:(NSConnection *)connection { 
    statusUpdate.text = @"Uncompressing file"; 
    [self performSelector:@selector(doUncompress) withObject:nil afterDelay:0]; 
} 

- (void)doUncompress { 
    // Do work in 100 ms chunks 
    BOOL isFinished = NO; 
    NSDate *breakTime = [NSDate dateWithTimeIntervalSinceNow:100]; 
    while (!isFinished && [breakTime timeIntervalSinceNow] > 0) { 
     // do some work 
    } 
    if (! isFinished) { 
     statusUpdate.text = // here you could update with % complete 
     // better yet, update a progress bar 
     [self performSelector:@selector(doUncompress) withObject:nil afterDelay:0]; 
    } else { 
     statusUpdate.text = @"Done!"; 
     // clean up 
    } 
} 

Основная идея заключается в том, что вы работаете в небольших кусках. Вы возвращаетесь из своего метода, чтобы периодически запускать цикл выполнения. Вызовы для выполненияSelector: гарантируют, что управление в конечном итоге вернется к вашему объекту.

Обратите внимание, что риск сделать это заключается в том, что пользователь может нажать кнопку или взаимодействовать с пользовательским интерфейсом каким-то образом, чего вы не ожидаете. Может быть полезно вызвать UIApplication beginIgnoringInteractionEvents, чтобы игнорировать ввод во время работы ... если вы не хотите быть действительно приятным и предлагаете кнопку отмены, которая устанавливает флаг, который вы проверяете в своем методе doUncompress ...

Вы также можете попробовать запустить цикл запуска самостоятельно, вызывая [[NSRunLoop currentRunLoop] runUntilDate:...] каждый раз так часто, но я никогда не пробовал это в своем собственном коде.

+0

Благодарим за ответы! Я реализовал это, как описано (copy & paste), но он зависает в цикле while() и никогда не вводит его. Я запускаю это на симуляторе. Любые идеи, почему это висит? – waltcrit

+0

У меня было неправильное условие цикла (должно быть должно быть '> 0' вместо' <0'). Я исправил свой ответ; возможно, это что-то не так с вашим кодом? В противном случае мне не хватает информации, чтобы рассказать. – benzado

+0

Это работает, спасибо! – waltcrit

0

Сделайте то, что вы делаете с таймером, просто отправьте свой код обработки в новый поток с ConnectionDidFinish :. Таймеры могут обновлять пользовательский интерфейс, поскольку они запускаются из основного потока.

Смежные вопросы