UIAlertView makes the program crash - ios7

I've got a crash:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue performTask:] may only be called from the main thread.'
And I could not find a solution for 2 days.
And here is the code:
[alert dismissWithClickedButtonIndex:0 animated:YES];
UIAlertView *noTicketAlert = [[UIAlertView alloc] initWithTitle:#"Aradığınız kriterlere uygun bilet bulunamadı!" message:nil delegate:self cancelButtonTitle:#"Tamam" otherButtonTitles: nil];
[noTicketAlert show];

I triggered this error by attempting to display an alert from a background thread. Fixed like this:
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:...
[alertView show];
});

I got this error when presenting a UIAlertView normally (no funny button override stuff). It turned out that I was presenting it twice in quick succession. The fix in my case was to remove the erroneous duplicate call.
If you do need to present two alert views at close to the same time, and you get this error, then a fix that works (and addresses the error message itself) is to run the code on the main thread:
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
// Your code that presents the alert view(s)
}];

Yes, I've found the solution and I share that with you guys.
I tried to override the dismissWithClickedButtonIndex function, and sent unique buttonIndexes
like 9999 for each of my alerts.
That is,
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self viewWillDisappear:YES];
if(buttonIndex == 9999) {
noTicketAlert = [[UIAlertView alloc] initWithTitle:#"Aradığınız kriterlere uygun bilet bulunamadı!" message:nil delegate:self cancelButtonTitle:#"Tamam" otherButtonTitles: nil];
[noTicketAlert show];
}
}
and if I want to display the noticketAlert, I call this method like :
[alert dismissWithClickedButtonIndex:9999 animated:YES];

If you have a custom button make sure you implement the delegate method:
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
return YES;
}
if this selector is not found then the program with crash..

For those looking for the Swift 2 answer to this issue, I ran into a similar problem and solved it with #Dave Batton's solution
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier("loginSegue", sender: self)
})

Related

UIAlertView button results in a child NSThread

I would like a user to get a prompt screen, it would have yes and no options.
The problem is the UIAlertView cannot be called from child thread, if I call it from child thread I am getting a runtime error(EXC_BAD_ACCESS). I am using NSThread on IOS6.1
This is the code that i am using
-(void) construct
{
NSThread *initThread =[[NSThread alloc]initWithTarget:self selector:#selector(error) object:nil];
[initThread start];
}
- (void) error
{
//make sure it runs on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Save"
message:#"Enter File Name"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[alertView show];
});
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSLog(#"Alert View dismissed with button at index %d",buttonIndex);
switch (alertView.alertViewStyle)
{
case UIAlertViewStylePlainTextInput:
{
UITextField *textField = [alertView textFieldAtIndex:0];
NSLog(#"Plain text input: %#",textField.text);
} break;
case UIAlertViewStyleSecureTextInput:
{
UITextField *textField = [alertView textFieldAtIndex:0];
NSLog(#"Secure text input: %#",textField.text);
} break;
case UIAlertViewStyleLoginAndPasswordInput:
{
UITextField *loginField = [alertView textFieldAtIndex:0];
NSLog(#"Login input: %#",loginField.text);
UITextField *passwordField = [alertView textFieldAtIndex:1];
NSLog(#"Password input: %#",passwordField.text);
}break;
default: break;
}
}
This is the error that I am getting:
[NSURLCacheInternal alertView:didDismissWithButtonIndex:]: unrecognized selector sent to instance 0x8653630
2013-07-26 16:12:35.738 iOSTrack[7455:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURLCacheInternal alertView:didDismissWithButtonIndex:]: unrecognized selector sent to instance 0x8653630'
*** First throw call stack:
(0x1e7a012 0x1796e7e 0x1f054bd 0x1e69bbc 0x1e6994e 0x7c0e13 0x408d66 0x408f04 0x20e7d8 0x266b014 0x265b7d5 0x1e20af5 0x1e1ff44 0x1e1fe1b 0x1dd47e3 0x1dd4668 0x3caffc 0x21cd 0x20f5)
libc++abi.dylib: terminate called throwing an exception
Most likely what is happening is that your UIAlertView is a local variable and it's getting destroyed before the delegate has a chance to run. You need to keep a reference to it around long enough such that your delegate has a chance to run and do it's work. Then you are in the clear. This concept applies to both ARC and manual memory managed based code.
See this related question: UIAlertViewDelegate class "self" instance gets dealloc'd before button gets pressed
The code I tried actually worked fine for me
The only difference is
[alertView show] => [alertView show]; you miss ;,a typo in question i guess
running the code in seperate thread
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
// Perform long running process
[self error];
});
- (void) error {
//make sure it runs on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Save"
message:#"Enter File Name"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[alertView show];
});
}

UIAlertView Loading indicator doesn't work in a secondary thread

I have following code for the UIAlertView Loading indicator which is not working and giving me
- (void) launchActivity
{
//some logic...
[NSThread detachNewThreadSelector:#selector(updateFilterProgress) toTarget:self withObject:nil];
}
- (void) updateFilterProgress {
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN))
{
UIAlertView *myAlert = [[[UIAlertView alloc] initWithTitle:#"No Internet Connectivity" message:#"This app require an internet connection via WiFi or cellular network to work." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil] autorelease];
[myAlert show];
}
else{
UIAlertView *alertMe = [[[UIAlertView alloc] initWithTitle:#"Loading..." message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles: nil] autorelease] ;
//tried this way by placing below line....no result
[alertMe performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
//[alertMe show];
//some logic...
}
Updated:
On main thread I am calling web-service and loading data. hence I have given another thread for Loading UIAlertView and it was working with iOS 4,5. but its crashing in iOS 6. If I am placing AlerView on main thread then while loading nothing shows but after getting data loaded AlertView shows Loading Indicator for few seconds. Any suggestion...
You are showing the alert from the detached thread while you must do it from the main thread, either use GCD or performSelectorOnMainThread.
On the main thread you usually want to perform only the UI update, all the complex calculations and data loading are to be executed in the detached threads. If you try to load the data in the main thread, the UI will no be responding during the loading. So that is a good practice to load the data in the detached thread, on the main thread you show the alert as you need on the loading start and dismiss it on the loading (and parsing) finished, calling the content UI update as well:
It's a bad practise.
Apple docs say that you need to handle the UI elements on the main thread.
I think the issue is with this line:
[NSThread detachNewThreadSelector:#selector(updateFilterProgress) toTarget:self withObject:nil];
You won't handle the UI element on other threads rather than on main thread.
Use:
[self updateFilterProgress];
Or use like:
[yourAlert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
Also I checked with your code. There is one error popping up:
Error is: No visible #interface for 'UIAlertView' declares the selector 'performSelectorOnCurrentThread:withObject:waitUntilDone:'
The performSelectorOnMainThread is working perfectly for me.
Try this
- (void) launchActivity
{
//some logic...
[NSThread detachNewThreadSelector:#selector(updateFilterProgress) toTarget:self withObject:nil];
}
- (void) updateFilterProgress {
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN))
{
[self performSelectorOnMainThread: #selector(showAlertForNoNetConnect)];
}
else{
[self performSelectorOnMainThread: #selector(showAlertForLoading)];
//some logic...
}
- (void) showAlertForNoNetConnect
{
UIAlertView *myAlert = [[[UIAlertView alloc] initWithTitle:#"No Internet Connectivity" message:#"This app require an internet connection via WiFi or cellular network to work." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil] autorelease];
[myAlert show];
}
- (void) showAlertForLoading
{
UIAlertView *alertMe = [[[UIAlertView alloc] initWithTitle:#"Loading..." message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles: nil] autorelease] ;
[alertMe show];
}
You have to call all the UIKit elements in the main thread. That's what the problem is.
Hope this helps. Happy Coding. :)

Alert View in the Beginning and the End of a void

I have a Void. This void do something really slow, so at the beginning of Void I put an alert, and in the end I put another warning. Like here:
-(void)action {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Start" message:#"Start." delegate:self cancelButtonTitle:#"Ok." otherButtonTitles:nil];
[alerta show];
[alert release];
//Something really slow
UIAlertView *alertEnd = [[UIAlertView alloc] initWithTitle:#"End" message:#"End." delegate:self cancelButtonTitle:#"Ok." otherButtonTitles:nil];
[alertEnd show];
[alertEnd release];
}
But when I run this code, the alerts are only shown at the end of the Void, after all the slow action.
How can I fix this?
First of all: try to avoid using many alerts since it's not humane to you users. Show UIActivityIndicator instead.
If you must, then try this code for showing the first alert:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Start" message:#"Start." delegate:self cancelButtonTitle:#"Ok." otherButtonTitles:nil];
[alert performSelector: #selector(show)
onThread: [NSThread mainThread]
withObject: nil
waitUntilDone: NO];
you will have to separate the two in two methods and then call one using
[self performSelector: afterDelay:]
method

UIAlertView without any buttons

I wanted to know whether the following code is okay or not. I am trying to dismiss the alertView automatically after 2 seconds (and without any buttons in the alertView) from the "timedAlert" method.
//this is in another method
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:#"Login successful." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
[alert show];
[alert release];
[self timedAlert];
}
-(void)timedAlert
{
[self performSelector:#selector(dismissAlert:) withObject:alert afterDelay:2];
}
-(void)dismissAlert:(UIAlertView *) alertView
{
[alertView dismissWithClickedButtonIndex:nil animated:YES];
}
If the cancelButton of the alertView is set to "nil", how will the "[alertView dismissWithClickedButtonIndex:0 animated:YES];" thing work??? I tried making the cancelButton "nil" and it worked, but cant figure out how....
P.S: I call the timedAlert method from another
Any help is appreciated! Thank you!
First let me say it would be better if you handle this with a custom view, but with that said the problem looks to be with
[alert release];
You are releasing the object before you are done with it (I am surprise it does not crash).
Do something like this
// other code
alert = [[UIAlertView alloc] initWithTitle:nil message:#"Login successful." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
[alert show];
[self performSelector:#selector(dismissAlert:) withObject:alert afterDelay:3.0f];
}
-(void)dismissAlert:(UIAlertView *) alertView
{
[alertView dismissWithClickedButtonIndex:nil animated:YES];
[alertView release];
}
Your code should work, and you should have no problems. I have done this in one of my previous apps. The button is not displayed because the title is nil but I think the instance of the button still exists. Put a breakpoint before closing your alert and take a look at the alert variable, and check to see if there is a buttons array or something, that should tell you how that works.

Cannot get UIAlert to work

Does somebody see something wrong with this? I have an UIAltertView but get an EXC_BAD_ACCESS when I click any of the two buttons:
UIAlertView *alert = [[UIAlertView alloc] init];
[alert setTitle:#"First Sync"];
[alert setMessage:#"The App is going to do its first synchronisation. This might take a few moment..."];
[alert setDelegate:self];
[alert addButtonWithTitle:#"OK"];
[alert addButtonWithTitle:#"Cancel"];
[alert show];
[alert release];
and to catch the response:
#pragma mark UIAlertView
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex != 0)
{
NSLog(#"TEST1");
return;
}
NSLog(#"TEST2");
}
It must be something simple...
You'll have to use the designated initializer:
initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:
As hinted above, the problem was that the UIAlert was generated in a thread. By using an NSCondition and firing the Alert into a background thread I got the worker thread to wait while the background thread waits for a response from the user. Once the response comes it signals the worker thread to continue (as the required data is stored in the database at that point).