Load more than one URL in UIWebView one by one - iphone

Hi! I am developing an application. In that I am creating one webview. By using that webview I want to load the next url after completion of first one. I written the - (void)webViewDidFinishLoad:(UIWebView *)webView like this
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
alert_View=[[UIAlertView alloc]initWithTitle:#"\n\nData sent successfully!" message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
[alert_View show];
[alert_View release];
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(removeAlert:) userInfo:nil repeats:NO];
}
And the removeAlert method looks like
-(void)removeAlert:(id)sender
{
[alert_View dismissWithClickedButtonIndex:-1 animated:YES];
[web loadRequest:[arr objectAtIndex:1];
}
arr array contain all prepared requests. When ever this removeAlert method executed directly -(void)webView:didFailLoadWithError: method is fired. So please tell me how can I load another request after end of first one?

If you're using ARC, you should get rid of the [alert_View release]. The alert view may not be retained, and that could be contributing to the problem.
Also, try [self performSelector: #selector(removeAlert:) withObject:nil afterDelay:3.0]; and see if that works, instead of your NSTimer call.

One way you can do it is by delegating. So once your first request is processed and you get a response, you can send the next one.
If you arent familiar with delegates, here's a link to get you started.

Related

call a class level method in scheduledTimerWithTimeInterval

I make a class level method for Alert:
#interface TestAlert
#end
+ (void)showErrorAlert:(NSTimer *)message
{
.......
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:nil message:messageIn delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert show];
}
and I want to call it directly in scheduledTimerWithTimeInterval like:
[NSTimer scheduledTimerWithTimeInterval:0.001 target:TestAlert selector:#selector( showErrorAlert:) userInfo:error repeats:NO];
There have grammar error of course.
I know I can put showErrorAlert to a method:
- (void)showError:(NSTimer *)timer
{
//NSLog(#"show error %#", error);
[TestAlert showErrorAlert:(NSString *)[timer userInfo]];
}
Then
[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:#selector(showError:) userInfo:error1 repeats:NO];
But it will cause crash when showErrorAlert is called, because error message from showError method has been released.
Can i call showErrorAlert directly, If I can't, how should I avoid error message's release ?
Just use [TestAlert class] as a target instead of TestAlert.
How about trying this.
You can find all the types of provided performSelector: methods here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html
performSelector:
performSelector:withObject:
performSelector:withObject:withObject:
– performSelector:withObject:afterDelay:
performSelector:withObject:afterDelay:inModes:
performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:
performSelector:onThread:withObject:waitUntilDone:
performSelector:onThread:withObject:waitUntilDone:modes:
performSelectorInBackground:withObject:

performSelector:withObject:afterDelay: not making call

in a method, i want to call a method after n seconds:
self.toolBarState = [NSNumber numberWithInt:1];
[self changeButtonNames];
[self drawMap];
[self performSelector:#selector(showActionSheet) withObject:nil afterDelay:2];
i want to show the action sheet 2 seconds after drawMap is complete. when i use this performSelector, it never makes the call.
if i just put [self showActionSheet];, it works just fine. is there reason why the performSelector is not making the call?
EDIT: in another part of my code, i make the same call and it works:
HUD = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:HUD];
HUD.delegate = (id) self;
[HUD showWhileExecuting:#selector(drawMap) onTarget:self withObject:nil animated:YES];
[self performSelector:#selector(showActionSheet) withObject:nil afterDelay:6];
here, the showActionSheet gets called 6 seconds after drawMap has completed. i'm guessing there is something going on with the threads that i don't understand yet...
EDIT2:
-(void)showActionSheet
{
InspectAppDelegate *dataCenter = (InspectAppDelegate *) [[UIApplication sharedApplication] delegate];
if (dataCenter.fieldIDToPass == nil)
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Selected Boundary Options" delegate:(id) self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Analyze a Field",#"Retrieve Saved Analysi", #"Geotag Photos", #"Refresh the map",nil];
actionSheet.tag = 0;
[actionSheet showInView:self.view];
}
else
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Selected Boundary Options" delegate:(id) self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Analyze a Field",#"Retrieve Saved Analysi", #"Geotag Photos", #"Attribute the Field", #"Refresh the map",nil];
actionSheet.tag = 0;
[actionSheet showInView:self.view];
}
}
EDIT3:
ok, so the progress of method calls is:
-(void) foundDoubleTap:(UITapGestureRecognizer *) recognizer
{
[HUD showWhileExecuting:#selector(select) onTarget:self withObject:nil animated:YES];
}
-(void) select
{
[self changeButtonNames];
[self drawMap];
[self performSelector:#selector(showActionSheet) withObject:nil afterDelay:2];
}
showActionSheet never gets called. like i said, i'm pretty sure its a threading issue. if call it with [self showActionSheet], it will run. =/
I ran into this same issue, and by necessity I solve it slightly different from the accepted answer. Notice I wanted my delay and selectors to be variables? Using a block allows me to stay within my current method.
dispatch_async(dispatch_get_main_queue(), ^{
[self performSelector:loopSelector withObject:nil afterDelay:cycleTime];
});
By the way, this is definitely a threading issue. The documentation for performSelector:withObject:afterDelay: states that this will be performed on the current thread after the delay, but sometimes that thread's run loop is no longer active.
A more detailed discussion on the subject can be found here
Try using:
-(void) select {
[self changeButtonNames];
[self drawMap];
[self performSelectorOnMainThread:#selector(showActionSheet) withObject:nil waitUntilDone:YES];
}
-performSelector:withObject:afterDelay: schedules a timer on the same thread to call the selector after the passed delay.
Maybe this will work for you:
-(void) select {
[self changeButtonNames];
[self drawMap];
[self performSelectorOnMainThread:#selector(someA) withObject:nil waitUntilDone:YES];
}
-(void)someA {
[self performSelector:#selector(showActionSheet) withObject:nil afterDelay:2];
}
Does your class still exist in memory?
If your class goes away before the performSelector fires, do you wind up sending the message to nil (which would cause nothing to occur).
You could test this by dropping an NSLog into your dealloc()
You mentioned threads. If your performSelector isn't called from the MainThread it could cause issues (UI things should be done on the main thread).
I got the same problem when I call performSelector:withObject:afterDelay: in a background thread created by ReactiveCocoa.
If I execute the block in the ReactiveCocoa's way, the block will be called correctly:
[[[RACSignal empty] delay:2.0] subscribeCompleted:^(){
// do something
}];
I guess there is some magic in the ReactiveCocoa's threading model.

issue dismissing UIAlertView

I'm using UIAlertView with "please wait" and UIActivityIndicatorView. I show it using
{
...
[self performSelectorInBackground:#selector(sh) withObject:nil];
//i've tried also
//[NSThread detachNewThreadSelector:#selector(showWaiting) toTarget:self withObject:nil];
...
}
- (void)showWaiting {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[waitingAlert show];
[pool drain];
}
When alert appears, screen becomes darkened and inactive. And when time to dismiss alert comes I do
[waitingAlert dismissWithClickedButtonIndex:0 animated:NO];
After this alert disappears, but sometimes (chance is about 5%) screen remains as before darkened and inactive.
Has someone faced such problem?
Thanks!
This isn't the answer you're looking for... but it's the "correct" answer and will save you some time in the long run:
You're misusing UIAlertView. It shouldn't be used for progress indicators and "Please Wait" dialogs. It's designed to be dismissed by the user hitting a button -- a UIAlertView should never, ever just disappear on its own (correction: actually, it can; Apple's API supports dismissing of it. But such a thing should be used very carefully.)
If you continue to abuse UIAlertView that way, don't be surprised when your app is rejected during app store submission and you then have to redo it another way.
Please see also my answer to this question:
Multiple AlertViews - Remove the alertview that is behind another alertview
As occulus states avoid using UIAlertView like this,
I recommend MBProgressHUD, it's very easy to use, looks great and is designed for this purpose.
You have to create, show and release the alert outside the NSAutoreleasePool. For example, you make your call in background:
...
[NSThread detachNewThreadSelector:#selector(showWaiting) toTarget:self withObject:nil];
...
And the call has an autorelease pool:
- (void)showWaiting {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorInMainThread:#selector(showMyAlert) withObject:nil waitUntilDone:NO];
//[waitingAlert show];
[pool drain];
}
Then you must to show the alert using the main thread.
- (void)showMyAlert {
UIAlertView * myAlert = [[UIAlertView alloc] initWith...];
[myAlert show];
[myAlert release];
}
If you try to show the alert inside the autorelease pool and this is drained then the alert dissapears.
I hope this helps!

Update text in alert message

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).

iPhone memory management question and object livecycle

Here is the code I have:
Phone SDK undestanding cocoa object live cycle:
- (void) DismissWelcomeMessage: (UIAlertView *) view
{
[view dismissWithClickedButtonIndex:0 animated:YES];
}
- (void) ShowWelcomeMessage
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Blah" message:#"Blah Blah" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[self performSelector:#selector (DismissWelcomeMessage:) withObject: alert afterDelay: WELCOME_MESSAGE_DELAY];
[alert release];
}
ShowWelcomeMessage is called first.
Why DissmissWelcomeMessage works fine and does not crash even though alert object is released?
Is because Dismiss function uses copy of the object passed on stack as a parameter when function? But even then would not it be just a copy of the pointer pointed to the now deallocated object?
Or [alert release] just decriment reference counting and does not really do the same as
delete in C++?
performSelector retains the object, thus your release doesn't cause its retain count to go to zero.
See NSObject docs
This method retains the receiver and the anArgument parameter until after the selector is performed.
It's possible that performSelector is retaining the object passed in, which is why it's still valid when DismissWelcomeMessage is called.