Block main thread until UIWebView finishes loading - iphone

I'm trying to block the main thread until a UIWebView finishes loading, but calling [NSThread sleepForTimeInterval:] after calling [UIWebView loadRequest:] makes the loading never complete: The webViewDidFinishLoad delegate is called after the sleep finishes.
Why is this happening? and How can I block the main thread?
I'm calling loadPage from [ViewController willRotateToInterfaceOrientation:], what I'm trying to do is to prevent the iOS interface to rotate until the webview with the other orientation is loaded.
- (void)loadPage {
UIWebView* webview2 = [[UIWebView alloc] initWithFrame:rect];
webview2.delegate = self;
NSString* htmlPath = ..... ; // Path to local html file
NSURL *newURL = [[[NSURL alloc] initFileURLWithPath: htmlPath] autorelease];
NSURLRequest *newURLRequest = [[[NSURLRequest alloc] initWithURL: newURL] autorelease];
[webview2 loadRequest:newURLRequest];
[NSThread sleepForTimeInterval:10.0f]; // Why this blocks the uiwebview thread ??
[self.view addSubview:webview2];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"Finished loading!");
}

I think you're trying to solve the wrong problem.
You don't want to block the main thread. Firstly, the entire UI is drawn on the main thread, so it will appear as though your app has hung. Secondly, a UIWebView is part of the UI (which is probably why what you're doing doesn't work).
So, what to do?
Have a look at the following method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
When you start loading your page you can return NO from this method. When it's finished loading you can return values normally. UIWebView has a delegate that tells you the status.

Have a look at the class reference for NSURLRequest, there is a sync method in there.
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error

Related

Press a Button and open a URL in another ViewController

I am trying to learn Xcode by making a simple app.
But I been looking on the net for hours (days) and I cant figure it out how I make a button that open a UIWebView in another ViewController :S
first let me show you some code that I have ready:
I have a few Buttons om my main Storyboard that each are title some country codes like UK, CA and DK.
When I press one of those Buttons I have an IBAction like this:
- (IBAction)ButtonPressed:(UIButton *)sender {
// Google button pressed
NSURL* allURLS;
if([sender.titleLabel.text isEqualToString:#"DK"]) {
// Create URL obj
allURLS = [NSURL URLWithString:#"http://google.dk"];
}else if([sender.titleLabel.text isEqualToString:#"US"])
{
allURLS = [NSURL URLWithString:#"http://google.com"];
}else if([sender.titleLabel.text isEqualToString:#"CA"])
{
allURLS = [NSURL URLWithString:#"http://google.ca"];
}
NSURLRequest* req = [NSURLRequest requestWithURL:allURLS];
[myWebView loadRequest:req];
}
How do I make this open UIWebview on my other Viewcontroller named myWebView?
please help a lost man :D
Well, you've firstViewController already designed and coded, now you've to create secondViewController with a UIWebView binded in IB it self, also it have a setter NSString variable like strUrl that you need to pass at the time of pushing or presenting secondViewController, and assign it to UIWebView in viewDidLoad of secondViewController. See my answer about how to pass a NSString? Also UIWebView has its delegates method (What's delegate & datasource methods? - Apple Doc) Which you can use to handle URL request.
These are the delegate of UIWebView, if you'll going to use it, you need to give UIWebView delegate to self.
- (void)webViewDidStartLoad:(UIWebView *)webView;
- (void)webViewDidFinishLoad:(UIWebView *)webView;
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
firstViewController.m
- (IBAction)ButtonPressed:(UIButton *)sender {
NSURL* allURLS;
//get your URL
secondViewControlelr *secondView=[[secondViewControlelr alloc]init];
second.urlToLoad=allURLS;
[self.navigationController pushViewController:secondView animated:YES];
}
secondViewControlelr.h
//declare url and set property
NSURL *urlToLoad;
secondViewControlelr.m
- (void)viewDidLoad
{
myWebView=[[UIWebView alloc]initWithFrame:CGRectMake(0, 0, 320, 460)];
[self.view addSubview:myWebView];
NSURLRequest *urlReq=[NSURLRequest requestWithURL:self.urlToLoad cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:10];
[myWebView loadRequest:urlReq];
}

iOS: Webview causes flicking while navigating to another controller

I use webview in my UIVIewController and load the local HTML file in it using following method.
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:filePath]]];
If I put this method in ViewDidLoad, I can see white flicking while navigating to my controller that doesn't look good.
I tried putting this method in ViewWillAppear like below. I am using webViewLoaded flag to make sure that webview has been loaded then only show the current view else it waits but it is going in infinite loop of waiting!
- (void)viewWillAppear:(BOOL)animated {
webViewLoaded = NO;
webView.scalesPageToFit = allowZoom;
webView.dataDetectorTypes = UIDataDetectorTypeNone;
webView.delegate = self;
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:filePath]]];
int count = 0;
while(webViewLoaded == NO)
{
NSLog(#"Waiting...");
[NSThread sleepForTimeInterval:0.1];
if(count++ > 10) break;
}
}
- (void)webViewDidFinishLoad:(UIWebView *)localwebView {
webViewLoaded = YES;
}
I have also tried the same thing in ViewDidLoad but still its going in infinite loop.
Please note that webViewLoaded is "volatile BOOL" but the webview delegate method is not getting called. Not sure what's going on!
Could anyone please help me to fix this. Thanks.
First : You're blocking your main thread and not giving WebView any chance to finish loading. Never block your main thread.
Second : There are two UIWebView delegate methods : webViewDidFinishLoad and webView:didFailLoadWithError: - make sure to listen to both.
If you're trying to wait until WebView completes loading and only show your controller afterwards - use delegation or blocks. Please note that this is not a proper way of doing things - i'm just modifying your example to make it work :
child controller (with WebView) :
-(void)startLoadingWithParent:(id)_parent {
self.parent = _parent;
NSURL * url = [[NSBundle mainBundle] URLForResource:#"resource" withExtension:#"html"];
[webView loadRequest:[NSURLRequest requestWithURL:url]];
}
-(void)webViewDidFinishLoad:(UIWebView *)webView {
[parent showMe:self];
}
-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
[parent showError:error];
}
master controller :
-(void)doSomething {
SecondController * ctrl; /// secondController should be created AND has its view loaded at this point
/// ctrl = [SecondController new];
/// ctrl.view;
[ctrl startLoadingWithParent:self];
/// show user that we're doing something - display activity indicator or something
}
-(void)showMe:(UIViewController*)me {
[self.navigationController pushViewControllerAnimated:me];
}
Try using [NSURL fileURLWithPath:filePath]
use Following code for
[self performSelectorInBackground:#selector(webViewLoaded) withObject:nil];
so that it will not affect your UI

NSThread for loading pdf files

I want to load a PDF file in a modal view controller.
Since the moment that I don't know the size of the file, I want to load it on a separate thread, so:
- (void)viewDidLoad {
[super viewDidLoad];
[[UISharedApplication sharedApplication] setNetworkActivityIndicatorVisibile:YES];
[NSThread detachNewThreadSelector:#selector(loadPDF) toTarget:self withObject:nil];
}
- (void)loadPDF {
NSAutoreleasePool *threadPool = [[NSAutoreleasePool alloc] init];
[self.webView loadRequest:[NSURLRequest requestWithURL:url]];
[threadPool release];
[self performSelectorOnMainThread:#selector(showPDF) withObject:nil waitUntilDone:NO];
}
- (void)showPDF {
[self.webView setHidden:NO];
[self.spinner stopAnimating];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
The method works pretty good in other files (at least I guess) where I parse some XML data, but here doesn't. Commenting:
[self performSelectorOnMainThread:#selector(showPDF) withObject:nil waitUntilDone:NO];
lets me to see the spinner and the activity indicator on the top. So I think that:
[self.webView loadRequest:[NSURLRequest requestWithURL:url]];
doesn't wait until finish and gives the control back to the caller. How can I fix it?
You can't/shouldn't (have got it to work once) update the UI on a separate thread, you should to it all on the main. As I believe webView is a UIWebView this is probably the reason.
Although you are changing the webview from hidden to visible on the main thread, all the updating and creating of the actual UIWebView is not.
Best thing to do is create the data from the URL then performSelectorOnMainThread and pass the data to a method on the main thread that updates the UIWebview with the data.
Implement the method webViewDidFinishLoad: of UIWebViewDelegate and call your showPDF there.

How to delete the cache from UIWebview or dealloc UIWebview

Every time I load a new page with UIWebView the before loaded page is shown for a short time.
How can I clear that cache? Another possibility would be to dealloc UIWebview. I tried that but than my UIWebView is always "empty". How should the alloc and dealloc be done in this case?
I noticed that the UIWebView is consuming about 10 MB RAM. Now the UIWebView is loaded together with the ViewController. And the view is autoreleased as well as the UIWebView is autoreleased. Wouldn't it be better to dealloc the WebView each time?
Solution:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CGRect frame = CGRectMake(0, 0, 320, 480);
self.webView = [[[UIWebView alloc]initWithFrame:frame] autorelease];
self.webView.scalesPageToFit = YES;
[self.view addSubview:self.webView];
}
- (void) viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.webView removeFromSuperview];
self.webView = nil;
}
I had nearly the same problem. I wanted the webview cache to be cleared, because everytime i reload a local webpage in an UIWebView, the old one is shown. So I found a solution by simply setting the cachePolicy property of the request. Use a NSMutableURLRequest to set this property. With all that everything works fine with reloading the UIWebView.
NSURL *url = [NSURL fileURLWithPath:MyHTMLFilePath];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[self.webView loadRequest:request];
Hope that helps!
My solution to this problem was to create the UIWebView programmatically on viewWillAppear: and to release it on viewDidDisappear:, like this:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.webView = [[[UIWebView alloc]initWithFrame:exampleFrame] autorelease];
self.webView.scalesPageToFit = YES;
self.webView.delegate = self;
self.webView.autoresizingMask = webViewBed.autoresizingMask;
[exampleView addSubview:self.webView];
}
- (void) viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.webView removeFromSuperview];
self.webView.delegate = nil;
self.webView = nil;
}
If you do this and your UIWebView doesn't get released you should check it's retain count and understand who is retaining it.
If you want to remove all cached responses, you may also want to try something like this:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
If I had to make a guess your property for webView is
#property (nonatomic, retain) IBOutlet UIWebView *webView;
Correct? The important part is the retain.
self.webView = [[UIWebView alloc]initWithFrame:frame];
The above code allocates a UIWebView (meaning retainCount = 1) and then adds it to self.webView, where it is retained again (retainCount = 2).
Change the code to the following, and your webView should be deallocated.
self.webView = [[[UIWebView alloc]initWithFrame:frame] autorelease];
addSubview: can only be called on UIViews.
//[self.navigationController addSubview:self.webView];
should be
[self.navigationController.view addSubview:self.webView];
I did something simpler, I just set the webView's alpha to 0 on loadRequest and 1 on webViewDidFinishLoad.
thewebview.alpha=0.0;
thewebview.backgroundColor=[UIColor whiteColor];
[thewebview loadRequest:[NSURLRequest requestWithURL:url(str)]];
and
- (void)webViewDidFinishLoad:(UIWebView *)webView {
thewebview.alpha=1.0;
}
(Actually I have a fade-in, as rendering f.ex. a PDF can take 1/5 seconds or so.)
You could also load #"about:blank" after the webview disappears, then it will be empty the next time you access it.

NSThread problem on iPhone

I'm using two threads in an iPhone app for the first time and I've run into a problem. In this prototype, I have a view controller that loads a local web page. I want an activity indicator to show until the page has finished loading. With the code below, the activity indicator starts, the page loads properly, but the activity indicator does not stop or hide. It doesn't look like the "loading" function ever gets called.
What am I doing wrong?
- (void)viewDidLoad {
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
[NSThread detachNewThreadSelector:#selector(getData) toTarget:self withObject:nil];
[super viewDidLoad];
}
- (void)getData {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[detailWebView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"page1" ofType:#"html"]isDirectory:NO]]];
[NSTimer scheduledTimerWithTimeInterval: (1.0/2.0) target:self selector:#selector(loading) userInfo:nil repeats:YES];
[pool release];
}
- (void)loading {
if(!detailWebView.loading){
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
}
There's an easier way to do this without creating your own thread.
- (void)viewDidLoad {
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
[detailWebView setDelegate:self];
[detailWebView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"page1" ofType:#"html"]isDirectory:NO]]];
[super viewDidLoad];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[activityIndicator stopAnimating];
}
You 'd betetr stop the activity indicator in the webview delegate method :
-(void)webViewDidFinishLoad:(UIWebView*)webView
Have you added debug output to loading to whether it gets called ? The code sure looks like that, it should get called after 0.5 seconds and I'd guess that detailWebView is still loading then.
Also, GUI stuff should be run on the main thread, so you may need to do:
- (void)loading {
if(!detailWebView.loading){
[activityIndicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:YES];
[activityIndicator performSelectorOnMainThread:#selector(removeFromSuperView) withObject:nil waitUntilDone:YES];
}
}
You're using a webview, which already provides a mechanism to know when a load has finished, and already loads data asynchronously - the use of a custom thread really isn't needed.
Register your object as the UIWebView's delegate. Call loadRequest on the UIWebView and start animating the progress indicator. Stop animating the progress indicator in:
-(void)webViewDidFinishLoad:(UIWebView*)webView
This method is defined by the UIWebViewDelegate protocol - make sure your object implements this protocol. You implement this method:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[activityIndicator stopAnimating];
}
http://developer.apple.com/iphone/library/documentation/uikit/reference/UIWebViewDelegate_Protocol/Reference/Reference.html