UIWebView webViewDidStartLoad Sometimes Not Firing - iphone

I have a UIWebView in my view controller and have set its delegate to the view controller via code (i.e. not through IB). I have also setup the appropriate delegate methods: shouldStartLoadWithRequest, webViewDidStartLoad, webViewDidFinishLoad and didFailLoadWithError.
In my view controller's viewDidLoad method I load an appropriate URL with this code:
[self.webView loadRequest:reqURL];
95% of the time everything works great and the page loads in the UIWebView object and displays as expected. Occasionally, however, the page doesn't load.
After stepping through my code I realized that on the times that it doesn't work the shouldStartLoadWithRequest delegate method fires, but webViewDidStartLoad doesn't.
Does anyone have any idea what's going on here? I couldn't find anything on Stack Overflow that specifically addressed this unique issue I'm having and am slowly reaching my breaking point. Thanks in advance!

You should make sure that your shouldStartLoadWithRequest implementation returns YES for all conditions at which you need your webView to load.

Related

UIWebView's shouldStartLoadWithRequest doesn't called sometimes

I am using a UIWebView in my app and I am overriding the shouldStartLoadWithRequest message to detect what kind of link is being clicked. If it's a "special" link, I push a UIViewController onto the stack and return NO from this method. This works just dandy most of the time.
Sometimes, however, I click on a link and my shouldStartLoadWithRequest never gets called. Now what's weird is that the UIViewController which houses the UIWebView is in a UITabBarController and when I click on another tab, the UIWebView finally gets its shouldStartLoadWithRequest called. Until I click this other tab, I do NOT get a call to shouldStartLoadWithRequest. The other interesting bit is that the failure case never happens the first time I click on a link; it's always on a subsequent time.
Has anyone seen this? To me, this sounds like the UIWebView is, sometimes, not getting a touchEnded event and by switching tabs, the underlying framework is forcing a touchEnded event which in turn causes my shouldStartLoadWithRequest to get called.
Are you implementing the
– webView:didFailLoadWithError:
method from the UIWebViewDelegate? Perhaps you're getting an error on the request and then you're never seeing it? This delegate should get thrown when you have issue such as timeouts and other things.

UIWebView: webViewDidStartLoad/webViewDidFinishLoad delegate methods not called when loading certain URLs

I have basic web browser implemented using a UIWebView. I've noticed that for some pages, none of the UIWebViewDelegate methods are called.
An example page in which this happens is: http://www.youtube.com/user/google. Here are the steps to reproduce the issue (make sure you insert NSLog calls in your controller's UIWebViewDelegate methods):
Load the above youtube URL into the UIWebView
[notice that here, the UIWebViewDelegate methods do get called when the page loads]
Touch the "Uploads" category on the page
Touch any video in that category
[issue: notice that a new page is loaded, but none of the UIWebView delegates are called]
I know that this is not an issue of UIWebView's delegate not being set properly, since the delegate methods do get invoked when loading other links (e.g. if you try clicking on a link that takes you outside of youtube, you'll notice the delegate methods getting called).
My gut feeling initially was that it might be because the page is loaded using AJAX, which may not invoke the delegate method. But then when I checked the iPhone's Safari, it did not exhibit this problem, so it must be something on my side.
I've also noticed that Three20's TTWebController has the exact same issue as I'm having.
But the problem that arises from this issue is that without the delegate methods called, I'm unable to update the UI to enable/disable the back and forward browsing buttons when new requests are loaded.
And idea why this is happening or how can I work around it to update the UI when a new request is made?
This isn't an iOS bug - the page isn't actually reloading. The UIWebView delegates are triggered following new page requests, but that page doesn't do that.
Look very carefully at what happens in desktop Safari when you click the video link on that page as you describe. Make sure you pay attention to the address bar. The address will change, but critically the page will not reload.
This is all handled by JavaScript, not by reloading the page. Simply put, the page never reloads, so there's no reason for the UIWebView delegates to be called.
If you don't believe me, to conclusively prove this try repeating the steps you describe with JavaScript disabled. You'll notice the page behaves completely differently.
this is not good solution but im using NSTimer for updating status of buttons:
- (BOOL)webView:(UIWebView *)_webview shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (!_timer && [request.URL.absoluteString rangeOfString:#"youtube.com"].length != 0) {
_timer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(checkNavigationStatus)
userInfo:nil
repeats:YES];
}
return YES;
}
//....
//....
//....
- (void)checkNavigationStatus
{
// Check if we can go forward or back
backButton.enabled = self.webView.canGoBack;
forwardButton.enabled = self.webView.canGoForward;
}
Looks like this got fixed in iOS 4.2. It works in iOS 4.2.

Where to put cleanup code in UIViewController?

I have a UIViewController subclass that loads a bunch of images for each cell in a tableview asynchronously which is handled by a separate download class. I keep a list of all of these download requests in a dictionary which is keyed to the index of the cell that is requesting the image.
My question is i where should i put the code that cancels the image download if the viewcontroller is popped off the navcontroller? I need to do this because if the user hits back while there are still images being downloaded (which could take a while) then when they are finished downloading the viewcontroller has already been released.
I cant put it in the viewWillDisappear method because i do not want to stop the download if the user clicks on a separate tab and only when the hit the back button. For now i put this code in the viewcontrollers dealloc method which works fine although it doesnt seem right for some reason. I thought of using the viewDidUnload method but it seems this is only called when there is a low memory warning?
Any ideas?
dealloc is the perfect place for this as the view controller gets deallocated and you are responsible to clean your stuff up.
You may want to consider putting the code in viewWillDisappear: anyway, after all if the screen you are navigating to needs anything loaded it will be slowed by the background image load...

UIWebView doesn't update

I am using UIWebView loadRequest to request a new URL. The UIWebView is inside a TabBar View. When I place the request inside awakeFromNib it works. When a make a subsequent call the view does not change.
[webDisplay loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]]];
If I place the initial request inside viewDidLoad instead of awakeFromNib it also doesn't seem to work.
I note that if I track request by delegating shouldStartLoadWithRequest this method also doesn't seem to be called by this loadRequest call.
Tabbar view controllers are loaded only once i.e when a particular tab is selected for the first time. So, viewDidLoad and awakeFromNib are only called once.
You need to call loadrequest in viewDidAppear method for the webview to be reloaded with latest content whenever that particular tab is selected.

Deferring viewWillAppear until webViewDidFinishLoad

I have an application that uses UIWebViews in several view controllers. The UIWebViews are used to render locally generated html, no slow network access required.
To save memory I only load these on demand as prompted by the viewcontroller viewWillAppear callback. (And unload offscreen instances in response to didReceiveMemoryWarning messages.)
The problem is that the user gets to see the html being rendered, sometimes accompanied by flashes of styling and other assorted unpleasant artifacts. I would much rather the rendering be done offscreen, and reveal the fully rendered view when its ready.
It would be very tidy to be able to have the viewWillAppear not return until the UIWebView is fully rendered. But how?
I tell the UIWebView what to render by sending it a loadHTMLString:baseURL: message. This is asynchronous, and some time (soon) later the webview's delegate gets called back webViewDidFinishLoad.
I experimented with running a runloop inside viewWillAppear, running either the NSDefaultRunLoopMode or UITrackingRunLoopMode. This works in the simulator (it complains to the log
[CATransaction synchronize] called within transaction
but does work) but on a device it deadlocks, with webViewDidFinishLoad never being called.
(Also, it seems like the UIWebView loading property doesn't work. At least, after I call loadHTMLString:baseURL: and before getting the callback it's not true.)
Lots of solutions here I think. A quick one is to load your UIWebView with it's hidden property set to YES. Then set your UIViewController as the UIWebViews delegate and implement:
- (void)webViewDidFinishLoad:(UIWebView *)webView
where you set the property back to NO.
A thing to note is that webViewDidFinishLoad will fire more than once if you have framed/embedded content. So you have to keep track of this. Shouldn't really be a problem if you are loading local content.
I like monowerker's solution best, but another solution would be to hold onto the already-rendered UIWebView all the time (in some more permanent object than the view controller). I'd only do that if the look of monowerker's solution is too disruptive.