Update text in alert message - iphone

Still trying to update the message in an UIAlertview while the alert is active. I removed most of the first part of the question and publish more code in the update below.
UPDATE 3: Adding more code!
In the .h file I declare the following (among others):
#interface WebViewController : UIViewController <UIWebViewDelegate> {
IBOutlet UIWebView *webView;
UIAlertView *alert;
}
I #property and #synthesize the UIAlertview to.
Next I create the alert in an IBAction which is run by a button click:
-(IBAction)convert {
convertButton.enabled = NO;
mailButton.enabled = NO;
backButton.enabled = NO;
//show the alert window
alert = [[UIAlertView alloc] initWithTitle:#"Converting in progress\nPlease Wait..." message:#"\n\n\n" delegate:self cancelButtonTitle:nil otherButtonTitles: nil];
[alert show];
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
// Adjust the indicator so it is up a few pixels from the bottom of the alert
indicator.center = CGPointMake(alert.bounds.size.width / 2, alert.bounds.size.height - 50);
[indicator startAnimating];
[alert addSubview:indicator];
[indicator release];
[alert setMessage:#"getting roster"];
}
It then jumps to the following function:
- (void)didPresentAlertView:(UIAlertView *)progressAlert {
//A lot of code
[alert setMessage:#"Checking history"];
//more code
[alert setMessage:#"writing history"];
//even more code
[alert setMessage:#"converting roster"];
}
The didPresentAlertView method ends with an ASIHTTPRequest to post data to a webpage and when this request is finished the code finally jumps to the last method to exit the UIAlertView and closes everything up:
- (void)requestFinished:(ASIHTTPRequest *)request {
[timer invalidate];
[alert dismissWithClickedButtonIndex:0 animated:YES];
backButton.enabled = YES;
[alert release];
}
I also removed the autorelease from my UIAlertView init to make sure it exists during the rest of the process.
As it is now, the code only fires the very first setMessage -> 'getting roster' and the very last -> 'converting roster'. The setMessage requests in the middle do not get fired..
Really hope someone can help me here!
Thanks all!

Now I see the problem.
When you update the message property, it doesn't fire redrawing the view right away. The view is marked as 'needed to be drawn', and the actual drawing happens later, typically at the end of the current or next runloop in the main thread.
Therefore, while your didPresentAlertView method is running on the main thread, the alert view is not redrawn until the method is finished. This is why a computation-intensive job needs to run on a separate thread to increase the responsiveness of the UI, as the UI-related job is done on the main thread.
What you should do is run your //A lot of code //more code and //even more code on a separate thread, and update the message property on the main thread.
For example, your code may look similar to :
// this is inside didPresentAlertView method
NSOperationQueue* aQueue = [[NSOperationQueue alloc] init];
[aQueue addOperationWithBlock: ^{
// A lot of code
[alert performSelector:#selector(setMessage:) onThread:[NSThread mainThread]
withObject:#"Checking history" waitUntilDone:NO];
// more code
[alert performSelector:#selector(setMessage:) onThread:[NSThread mainThread]
withObject:#"writing history" waitUntilDone:NO];
// it goes on
}];
If you are working with iOS 4.0 and later, and want to use the GDC, be careful because it may detect independency of your computation and message updates and let them happen concurrently (which is not desired here).

Related

EXC_Bad_Request "only" after calling the uialert 3 or more times

I am a newbie in objective c;
I am getting an error at creating a UIAlertview instantiation statements (UIAlert * myAlert, they are in separate scopes)
I have referred the following stackoverflow entries such as This, this, this too and much more research over the internet.
I am unable to get a break through
Following are my alert calls
This is my view controller code where I have put up the "UIAlertViewDelegate"
#import <UIKit/UIKit.h>
#interface gameFirstViewController : UIViewController<UIAlertViewDelegate>
#end
This is my class declaration
#import <Foundation/Foundation.h>
#interface GameLogic : UIView
//all sorts of various non relevant property declarations
#end
Here is my implementation for the alerts
//action to take when an alert is shown
- (void)alertView:(UIAlertView *)alertView
didDismissWithButtonIndex:(NSInteger) buttonIndex
{ NSLog(#"OK Tapped");
NSUserDefaults *MySettingsData = [NSUserDefaults standardUserDefaults];
row= [MySettingsData integerForKey:#"Row_count"];
col = [MySettingsData integerForKey:#"Column_count"];
if(buttonIndex==0)
{
for(int i=0;i<row;++i)
{
for(int j=0;j<col;++j)
{
myarr[i][j]=0;
}
}
if(_TimerStatus ==1)
{
[mainTimer invalidate];
mainTimer=nil;
_TimerStatus=0;
}
[self super_reset];
[self setNeedsDisplay];
NSLog(#"Game reset");
return;
}
}
//my usage of the alerts at 2 different places
UIAlertView *myAlert = [[UIAlertView alloc]initWithTitle: #"GAME OVER"
message:#"You clicked on a mine, tap on ok to reset"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
[myAlert performSelectorOnMainThread:#selector(show)
withObject:nil
waitUntilDone:YES];
UIAlertView *myAlert = [[UIAlertView alloc]initWithTitle: #"You Won! Whoo Hoo"
message:#"You have successfully dodged every minefield"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
[myAlert performSelectorOnMainThread:#selector(show)
withObject:nil
waitUntilDone:YES];
I am not sure where I am going wrong, any help would be great!
Thanks.
The way you are creating the UIAlertView is not safe in ARC. It's fine if you use the object right away, but you're passing it the performSelectorOnMainThread method. ARC may dealloc your myAlert variable by the time the main thread gets to perform the selector. That's likely why you're seeing the EXC_Bad_Request.
Either make myAlert a strong property, so it survives, or delay the creation of the UIAlert until the main thread is able to call the show method. You can do that like this:
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:#"GAME OVER" message:#"You clicked on a mine, tap on ok to reset" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil] show];
});
Note: I removed your double nil for the titles. I don't think that would make a difference, but just in case.

Can't see my alertView unless I comment out automatic dismission

I am implementing a simple alert view with activity indicator to show to the user while the app is performing the restore of the database. Everything is fine except that I can't see the alert view, unless I comment out the automatic dismiss line (last one here).
Ok, the restore of the database is quite quick, but still I would expect to see it even if for an instant, no? I can see the screen get's a little darker during animation but that's all. I also tried putting a for loop to extend the time, the time extends but still no alert view.
There is nothing wrong in the way the alert view is called, since if I comment the dismiss, I can see it...only forever. Anyone has an idea here?
Before anyone says it, I tried changing the delegate of the alertView to self as posted here, but it didn't help.
// First we prepare the activity indicator view to show progress indicator
UIActivityIndicatorView * activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[activityView setFrame:CGRectMake(121.0f, 50.0f, 37.0f, 37.0f)];
[activityView startAnimating];
// Then we put it in an alert view
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Loading" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
[alertView addSubview:activityView];
[alertView show];
// Finally we operate the restore
[[SQLManager sharedSQL] restoreDatabase:[restoreDatabaseURL path]];
// And we can dismiss the alert view with the progress indicator
[alertView dismissWithClickedButtonIndex:0 animated:NO];
Im not sure why this happens, but I encounter it occasionally as well. One way I have gotten something like this to work is by using performSelector:withObject:afterDelay: on the next call, like this:
// First we prepare the activity indicator view to show progress indicator
UIActivityIndicatorView * activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[activityView setFrame:CGRectMake(121.0f, 50.0f, 37.0f, 37.0f)];
[activityView startAnimating];
// Then we put it in an alert view
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Loading" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
[alertView addSubview:activityView];
[alertView show];
// Finally we operate the restore
// [[SQLManager sharedSQL] restoreDatabase:[restoreDatabaseURL path]];
[[SQLManager sharedSQL] performSelector:#selector(restoreDatabase:) withObject:[restoreDatabaseURL path] afterDelay:0.25];
// And we can dismiss the alert view with the progress indicator
[alertView dismissWithClickedButtonIndex:0 animated:NO];
performSelector Documentation
Sounds like it's dismissing itself almost instantaneously. If you add a for loop, it will likely freeze all other computations which means your activity view won't show up until the loop breaks.
You could try using a NSTimer to call another function to determine if the restore is complete or not;
// Setup our the timer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(checkRestore)
userInfo:nil
repeats:YES];
// Timer function checking for completion
-(void)checkRestore {
if (restore is complete) {
[alertView dismissWithClickedButtonIndex:0 animated:NO];
}
}

Showing alert while calling webservice

i have the following code.
UIActivityIndicator *activity = [[UIActivityIndicator alloc] initWithActivityIndicatorStyle:
UIActivityIndicatorStyleWhite];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Processing" delegate:self otherButtonTitles:nil];
[alert addSubview:activity];
[activity startAnimating];
[alert show];
WebServiceController *web = [[WebServiceController alloc]init];
NSDictionary *dict = [web getDetails];
The problem is that the alert is not getting displayed. The WebServiceController is an XML parser which gets details from a specified URL and returns them. The alert should be shown while the service is called as it takes time to get the details. But it displays the alert only after the service call is over. Why is that?
because [alert show] will require animation, since the service controller call is taking place on the main thread, the main thread is busy executing the service call, blocking the alert view animation to execute.
You need to perform the ServiceCall on backend thread, see NSOperation or PerformSelectorOnBackgroundThread, make sure you pass in delegate of the ViewController that has the AlertView to the backend thread, callback the delegate as soon as the service call is completed. Make sure you perform the call for the callback on the mainthread using performSelectorOnMainThread. All UI related calls should be executed on the main thread.
Adding to the above post, you have to write the alert like this:
UIActivityIndicator *activity = [[UIActivityIndicator alloc] initWithActivityIndicatorStyle:
UIActivityIndicatorStyleWhite];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Processing" delegate:self otherButtonTitles:nil];
[alert addSubview:activity];
[activity startAnimating];
[alert show];
[self performSelectorInBackground:#selector(doTheWork) object:nil];
You need to declare a function (doTheWork) which will take care of the web service call as follows:
-(void)doTheWork {
WebServiceController *web = [[WebServiceController alloc]init];
NSDictionary *dict = [web getDetails];
[alert dismissWithClickedButtonIndex:0 animated:YES]; //dismissing the alert
}

UIAlertViews disappear when calling UIActivityIndicatorView method

if I show an Alert and then call a method of an Activity Indicator, the alert disappears like the user has pressed OK.
Declaration of alert:
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error loading content" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
Declaration of Activity Indicator:
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
indicator.hidesWhenStopped = YES;
[indicator stopAnimating];
Method call which causes this problem:
[indicator stopAnimating];
What can I do?
Edit: I noticed that my app isn't working correct, if NSXML Parser is getting a wrong URL. At the moment I cannot say if the activity indicator is causing a error.
you could have the ActivityIndicator embedded in your alertview, looks neat and tidy:
http://kwigbo.com/post/394670170/iphone-sdk-uialertview-with-activity-indicator
Edit: Re-read your question, above solution might not be suitable for your needs.
Let me ask, what exactly are you trying to do with those two views?

UIAlertView shows up twice

My alertview appears twice and requires 2 user clicks to dismiss.
- (void) showAlert: (NSString *) message
{
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"You chose"
message: message
delegate: self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"ok",nil];
av.tag = SLIDER_ALERT;
[av show];
}
I am then releasing the alertview in the delegate method "alertView: (UIAlertView *) alertView clickedButtonAtIndex: (int) index"
I suspect the problem is that I have built my own view hierarchy programmaticaly. I have one parent view for the viewcontroller. Under that i have 2 other views (parentView -> subview1 and subview2). I've tried to call [self.view addSubview: av] but that does not work. Neither does bringToFrontSubView:
Any help would be much appreciated
Peyman
The Alert code is fine (other than the release, mentioned in the comments).
[av show] is all that's required to show a view. You don't add UIAlertViews as subviews.
Call it after a delay of 0.1 sec [self performSelector:#selector(showAlert:) withObject:nil afterDelay:0.10];
Hope this will solve your problem.
With using autorelease the Alert View seems to be twice or 3 times. And for iOS4 it needs to be autoreleased otherwise it will crash.