Estou criando um aplicativo simples que faz alguns cálculos. Alguns desses cálculos são bastante complexos, por isso pode demorar um pouco para obter o resultado. Estou usando UITableView
para permitir que o usuário insira os valores. Depois de clicar no botão "Calcular", faço uma simples UIView
com um UIActivityIndicatorView
e coloquei no meio da tela. E então eu chamo o método de cálculo em segundo plano. Algum código:
self.activityIndicatorView = [[UIView alloc]initWithFrame:CGRectMake(320/2 - 50, (480 - 64)/2 - 50, 100, 100)];
self.activityIndicatorView.layer.cornerRadius = 10.0f;
self.activityIndicatorView.backgroundColor = [UIColor colorWithRed:0. green:0. blue:0. alpha:0.7];
UIActivityIndicatorView *actInd = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
actInd.frame = CGRectMake((100 - actInd.frame.size.width) / 2, (100 - actInd.frame.size.height) / 2, actInd.frame.size.width, actInd.frame.size.height);
[actInd startAnimating];
[self.activityIndicatorView addSubview:actInd];
[self.activityIndicatorView bringSubviewToFront:actInd];
[self.view addSubview:self.activityIndicatorView];
[self.view bringSubviewToFront:self.activityIndicatorView];
[self performSelectorInBackground:@selector(calculateAllThatShizzle) withObject:nil];
Como você vê, é bem simples. Mas se o teclado ainda estiver visível, ele trava (EXC_BAD_ACCESS
) quando chega a [self.tableView reloadData]
no -(void)calculateAllThatShizzle
com o método no log:
2012-03-23 11:06:32.418 MyApp[869:5c07] bool _WebTryThreadLock(bool), 0x6adb270: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
1 WebThreadLock
2 -[UITextRangeImpl isEmpty]
3 -[UITextRange(UITextSelectionAdditions) _isCaret]
4 -[UITextSelectionView setCaretBlinks:]
5 -[UIKeyboardImpl setCaretBlinks:]
6 -[UIKeyboardImpl setDelegate:force:]
7 -[UIKeyboardImpl setDelegate:]
8 -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
9 -[UIResponder _finishResignFirstResponder]
10 -[UIResponder resignFirstResponder]
11 -[UITextField resignFirstResponder]
12 -[UITableView reloadData]
13 -[ChildViewController calculateAllThatShizzle]
14 -[NSThread main]
15 __NSThread__main__
16 _pthread_start
17 thread_start
Portanto, o problema é que ele está tentando fazer algo com o teclado a partir de um thread em segundo plano. Eu tentei percorrer as células e chamar [cell.rightTextField resignFirstResponder]
, mas não ajuda. Eu também tentei usar isso
-(void)reloadTableViewData {
[self.tableView reloadData];
}
-(void)calculateAllThatShizzle {
//some code omitted - code that uses UIKit
if ([dob timeIntervalSinceDate:calcDate]>0) {
[errorButton setTitle:@"Calculation Date must be more than Date of Birth" forState:UIControlStateNormal];
errorButton.hidden = NO;
[self.activityIndicatorView removeFromSuperview];
self.activityIndicatorView = nil;
return;
}
[self performSelectorOnMainThread:@selector(reloadTableViewData) withObject:nil waitUntilDone:NO];
}
mas isso também não ajuda. Estou usando ELCTextfieldCell
Qualquer ajuda será apreciada.
Obrigado
P.S. Eu verifiquei algumas outras perguntas sobre EXC_BAD_ACCESS
em reloadData, mas não cobre o problema que tenho
P.P.S. Também posso executar os cálculos no encadeamento principal, mas a GUI fica sem resposta e o indicador de atividade não aparece
Respostas:
1 para resposta № 1tente este código
for(int i=0;i<[tblViewData count];i++)
{
if([[(ELCTextfieldCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]] rightTextField] isFirstResponder])
{
[[(ELCTextfieldCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]] rightTextField] resignFirstResponder];
}
}
mude de acordo com você.
2 para resposta № 2
Sugestão:
- make the tableVer o firstResponder antes você chama calculeAllThatShizzle, ou seja, force o primeiro atendedor atual a renunciar e, assim, ignore o teclado
- e, em seguida, DEFERIR a chamada para o calcularAllThatShizzle após a conclusão do loop de execução atual, ou seja, garantir que o código para descartar o teclado seja executado em um momento adequado
Então, algo assim:
[[self tableView] becomeFirstReponder]; // will dismiss keyboard
[[self performSelector: @selector(startBackgroundStuff) withObject: nil afterDelay: 0.0];
Em seguida, no seu startBackgroundStuff, você pode inserir:
[self performSelectorInBackground:@selector(calculateAllThatShizzle) withObject:nil];
No método, você definitivamente deseja garantir que o método reloadData seja chamado no thread principal. A maneira como você fez parece OK. Você também pode fazer o seguinte:
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableViewData];
});
(Não sei se um é melhor que o outro, apenas o que se segue definitivamente funciona de maneira confiável.)
Comentário complementar
Em generateAllThatShizzle, você está fazendo uma chamada UIKit em segundo plano em algumas condições, ou seja, [self.activityIndicatorView removeFromSuperview];
É provável que isso cause um acidente. As chamadas UIKit precisam estar no segmento principal.
Dentro