Exibindo uiactivityIndicator por x segundos
-
03-07-2019 - |
Pergunta
Eu tenho um uabableViewController, que é rootViewController. É XIB apenas uma visualização de tabela. Adicionei um uiacityIndicator através do IB e criei um iboutlet. Adicionei programaticamente uma barra de ferramentas com um botão de exclusão ao rootViewController. Quando o usuário clica no botão Excluir da barra de ferramentas, quero exibir o indicador, centrado na tela. O método abaixo exclui dados de um banco de dados, que acontece muito rápido. Eu quero que o usuário saiba que algo está acontecendo. Se eu executar o código abaixo sem sono (), ele acontece rapidamente e não vejo o indicador. Mas alguns por algum motivo, não estou vendo o indicador de qualquer maneira. Estou supondo que o sono () bloqueie a repintura? Se eu tomar sono () e as duas últimas linhas, vejo o indicador, mas é claro que ele nunca desaparece. Ele também é exibido no canto superior esquerdo da janela.
Como faço para que o código abaixo funcione para que o indicador seja exibido por 1/2 a 1 segundo, pelo menos?
Como alinha o indicador no meio da janela?
[self.navigationController.view addSubview:activityIndicator];
[activityIndicator startAnimating];
[activityIndicator setNeedsDisplay];
//do something which might happen really fast
sleep(1); //create illusion
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
Solução
O spinner não começará a girar até que você processe o evento e retorne ao loop de corrida. A maneira de contornar isso é pedir ao loop de corrida para terminar as coisas para você.
Aqui está um exemplo do que quero dizer:
- (IBAction)deleteAction:(id)sender {
[self.navigationController.view addSubview:activityIndicator];
[activityIndicator startAnimating];
// Spinner won't start spinning until we finish processing this event, so
// we're just going to schedule the rest of what we need to do.
// doDelete: will run when the main thread gets its next event.
[self performSelectorOnMainThread:@selector(doDelete:)
withObject:record
waitUntilDone:NO];
// removeSpinner: will run in at least one second, but will wait if
// another event (like the doDelete: one) is in the middle of running.
[self performSelector:@selector(removeSpinner:)
withObject:activityIndicator
afterDelay:1.0];
}
- (void)doDelete:(id)record {
[record delete]; // or whatever it is you need to do
}
- (void)removeSpinner:(UIActivityIndicator*)activityIndicator {
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
}
NOTA: Não há nada neste código que garantisse que ele o processe outros toques durante o período de "sono", portanto, bloqueie outros eventos de alguma forma.
Outras dicas
Como Adam mencionou, o spinner não será exibido, a menos que o trabalho (neste caso a operação de exclusão) seja feita em um encadeamento de segundo plano. Este é o código que comecei a trabalhar com base no código inicial de Brent. Modifiquei para chamar minha função que faz o trabalho em um encadeamento de segundo plano e, em seguida, use o encadeamento principal para remover o spinner quando pronto.
- (void)removeSpinner:(id)record {
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Thank You" message:@"Thanks for your feedback!"
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
[alert release];
// return to feedings tab
tabBarController.selectedIndex = 0;
}
- (void)doFeedback:(id)record {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int j;
for (int i = 0; i < 1000000000; i++) {
j = j + i;
}
[self performSelectorOnMainThread:@selector(removeSpinner:) withObject:nil waitUntilDone:NO];
[pool release];
}
- (IBAction)handleSend {
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
// Spinner won't start spinning until we finish processing this event, so
// we're just going to schedule the rest of what we need to do.
// doFeedback: will run when the main thread gets its next event.
[self performSelectorInBackground:@selector(doFeedback:) withObject:nil];
}
O problema é que, enquanto você faz o que você gosta, o loop de corrida principal não está em execução. Para que o indicador de atividade seja exibido, o loop de corrida principal precisa estar em execução para que possa atualizar sua animação. Para obter o comportamento pretendido, você precisará fazer sua operação de banco de dados de forma assíncrona em um encadeamento separado.
self.navigationController.View AddSubView: ActivityIndicator]; [ActivityIndicator Startanimating];
Auto -executivo: @selector (removepinner :) withObject: ActivityIndicator AfterDelay: 5.0];