Manage the UIWebView loading process - iphone

I use UIWebView to present data, and a spinner to show the loading process. The data is an .mp3 file from my server.
I start the spinner when I start loading the webView. Then there is a delay until the audio file starts playing. I need to Stop the spinner quite at that moment.
Notta big deal, but just in case - the loading code:
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://blablabla.mp3"]]];
Question: how could I catch the event when the webView is ready to play the audio (when there is enough data to start playing) ? I need it to stop the spinner.
WebViewDidFinishLoad is the only delegate method I could use and it's not good for me, because it notifies when ALL data is loaded. Even if I use it, it is not getting called when the audio file finishes loading (maybe it's not finishing, I don't know, I just see the loading progress gets to the end while the mp3 is playing). Just in case - the error:
Error Domain=NSURLErrorDomain Code=-999
"The operation couldn’t be completed. (NSURLErrorDomain error -999.)"
UserInfo=0x1d39d0 {NSErrorFailingURLKey=http://blablabla.mp3,
NSErrorFailingURLStringKey=http://blablabla.mp3}
Any help/tut/link is appreciated. Thanks in advance!

For that kind of control I would not recommend doing it via a webView - you can't get that level of interaction.

You may try da very popular ASIHTTPRequest class.
There is a [didReceiveData:] delegate, maybe suitable for your testing.

Don't worry it's easy !
the -999 for WebView is a normal error that apply when you are loading another content without letting the page really gets fully loaded.
so must of the time do that :
if([error code] != NSURLErrorCancelled) return;
To your javascript event detection :
Execute Objective-C methods from JS
This is unfortunately slightly more complex, because there isn't the same windowScriptObject property (and class) that exists on Mac OSX allowing complete communication between the two.
However, you can easily call from javascript custom-made URLs, like:
window.location = yourscheme://callfunction/parameter1/parameter2?parameter3=value
load a jquery library and use the document.ready event :
$(document).ready(function() {
window.location = yourscheme://callfunction/parameter1/parameter2?parameter3=value
});
And intercept it from Objective-C with this:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *URL = [request URL];
if ([[URL scheme] isEqualToString:#"yourscheme"]) {
// parse the rest of the URL object and execute functions
}
}
This is not as clean as it should be (or by using windowScriptObject) but it works.
Last solution :
A Javascript Bridge
https://github.com/marcuswestin/WebViewJavascriptBridge
Enjoy

Instead of using the iOS activity indicator, implement the activity indicator inside the html. It is much simpler end also elegant solution. Just show/hide html element with animated gif image using the javascript.

Use a separate class to manage the NSUrlConnection for the retrieval and storage of the .mp3 file. You could use a NSNotificationCenter and the postNotification message or a delegate for when the file is ready to be played

Related

Show Progressbar while downloading file from Dropbox

In my application I want to show Progressbar while downloading file from Dropbox.
Which method I use to download file ?
1) ASIHTTPRequest (but it requires URL to download)
2) DBRequest (but it requires URLRequest)
3) [[self restClient] loadFile:file.path intoPath:localPath (but how to show progress bar?)
Thanks,
Because you mention loadFile, I assume you're using the Core SDK. In that case, you'll want to implement the loadProgress delegate method.
From DBRestClient.h:
- (void)restClient:(DBRestClient*)client loadProgress:(CGFloat)progress
forFile:(NSString*)destPath;

How to react to failed thumb image loading in TTThumbsViewController?

I'm currently using TTThumbsViewController in my project. I'm getting all the urls for TTPhotoVersionLarge and TTPhotoVersionThumbnail from the web so I can't tell that a url for the thumb images will work or not.
Currently the TTThumbsViewController will just display an empty image if the thumb url can't be loaded.
So I want to be notified if a thumb fails to load and do extra error handling when that happens like:
Try to load the url for TTPhotoVersionLarge
If that fails again display an error image (which is included in the bundle)
I have looked into the three20 code but can't find a proper place where I can implement this proper error handling.
The Three20 library is great, but I've found that it's often hard to change parts of their library such as this. For instance, TTThumbsViewController basically only allows you to set the dataSource and then takes care of the rest. If you want more control, perhaps look into code such as AQGridView: http://quatermain.tumblr.com/post/528737778/aqgridview-lives-for-my-ipad-dev-camp-hackathon
I've chosen to use that over Three20's equivalent because it gives you more control over what happens with your data.
Edit: In response to using TTThumbsViewController heavily, you may want to look at this method in TTTHumbsViewController.m:
- (NSString*)URLForPhoto:(id<TTPhoto>)photo {
if ([photo respondsToSelector:#selector(URLValueWithName:)]) {
return [photo URLValueWithName:#"TTPhotoViewController"];
} else {
return nil;
}
}
It looks like you should be able to specify a different value there so long as you can find something to add to the if statement determining if the initial loading failed from the dataSource.

Detect and Customize UIWebView's "Unable to Read Document" error

I'm testing a UIWebview with a number of different document types - .xlsx, .jpg, etc. - and it opens most of them just fine. From time to time, I open a local file and this message appears right in the web view:
Unable to Read Document
An error occurred while reading the document
I'm not concerned with "why" this error occurs - it happens, for instance, when I feed the UIWebView a garbage file (intentionally). The problem is that I can't figure out how to detect "when" this happens. It doesn't trigger webView:didFailLoadWithError, it doesn't trigger an NSException (via #try & #catch), and when I inspect the document in webViewDidFinishLoad, webView.request.HTTPBody is null.
Anyone know how to detect when UIWebView can't display content?
just call uiwebview delegate method
- (void)webViewDidStartLoad:(UIWebView *)webView
{
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *html = [webView stringByEvaluatingJavaScriptFromString:
#"document.body.innerHTML"];
if ([html rangeOfString:#"Unable to Read Document."].location == NSNotFound) {
NSLog(#"NO Error");
}
else
{
// NSLog(#"File contains Error");
}
}
If inspecting the log does not show anything out of the ordinary, run it on the actual device. As a bit of help, my log says
Cannot find data converter callback for uti public.data
Failed to generate preview
which means that the simulator is failing. After being frustrated with this same problem, I went thru the whole final certification and installation and provisioning process, and too confirm that Word 97 and Pages / Pages.zip files containing text do indeed display just fine on the actual device. SOLVED. The simulator itself is broken, which is very...troubling, that this little note didn't seem to make it into the release notes, and also complicates development a tad bit. However, the work around is to select the Device in Xcode and deploy out, and it should work.

iPhone UIWebView slow loading to local HTML files

I'm developing an app that requires caching web pages (completely) along with their CSS files and images after saving the entire HTML of the page (going through the links to store each file along with the HTML file).
While viewing the HTML file offline, UIWebView takes a long time to load the page, given that I'm already offline, and the file is on disk along with its CSS and images.
I'm using this code to load the file:
NSData *htmlData = [NSData dataWithContentsOfFile:htmlFilePath];
[wView loadData:htmlData MIMEType:#"text/html" textEncodingName:#"UTF-8" baseURL:[NSURL fileURLWithPath:self.htmlFolderPath isDirectory:YES]];
Is there any other means of loading the file to the UIWebView that can load it faster?
P.S: it loads very fast on simulator (offline) but on device it takes a long time (considering its an already offline cached file)
Thanks for help.
Is your solution actually cache things other than the html file, meaning pictures, css, etc, AND relinking them in the html page? I am guessing that you are downloading and caching the html page and external resources then the UIWebView is loading that html and still going out to load the external resources. That would explain the fast performance on the simulator and the slower performance on device.
Here is a library designed to do exactly what you are trying to do: ASIWebPageRequest.
It should also be noted that it could simply be a case of disk i/o bottlenecking. I know on my project, I was loading large images in a uitableview and even after they were cached I noticed quite a bit of lag when pulling them off the disk because they were so big.
I've found certain kinds of CSS can grind WebView rendering to a halt. For example:
body { -webkit-box-shadow:inset 0 0 100px #222; }
This works great in the simulator, looks nice too. But on the phone (even the iPhone 4, iOS 4.2), a simple page will take 10sec to render.
Since this probably just takes time because it needs to parse and render the page, you may consider firing up the UIWebView in the background; i.e. added as a subview, but not visible.
Maybe the UIWebView is smart enough to know it doesn't need to do anything, but I suspect that at least html and css parsing is done right away.
If it doesn't do anything without being visible, reduce size to 1x1 and set opacity=0, and put that pixel some place where it can't interfere with touch event handling.
Not perfect but it may work.
I'm almost sure you will never be able to do this. The UIWebView just needs some time to process your webpage even when it's a local page.
Keeping that in mind you can try to preload the page before it's being shown. For example if you show it after a user presses a button, preload the page when you show the button instead of when the user actually presses the button. The user doesn't notice the slow loading, because it's being handled in the background so when the user presses the button the page is already been loaded.
I can give you an idea about alternative ways of loading HTML from file into the UIWebView. A small project I've got uses Objective-C as a pure wrapper for UIWebView
The project is here, but the main code is below: http://github.com/robb1e/iWeb
- (void)viewDidLoad {
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"test" ofType:#"html" inDirectory:#"."]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
[super viewDidLoad];
}
I'm also exploring ways of improving the perceived performance by showing an image while the DOM is getting ready. Some answers I've had are here:
Displaying a background image on UIWebView
Just if in case someone happens the same to me...
I had a UIWebView that was loading a string html and every resource (js and css) was stored locally.
I've faced that loading the content with internet connection it was some kind of slow (1 or 2 seconds to load and appear the webview in my controller) but when I tried to load the same page WITHOUT internet connection it was fast, really fast.
I've remembered that my HTML template had this in the start <base href="http://somesite.com" /> that I've used to load some images whit relative paths in some contents.
Removing that work as charm. So that can be making your web view load slower even if you don't have any reference to extern content in your HTML.
Even in my case, while loading local html files to webview was taking too much time.
Try to load local html files as below, it worked for me:
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:#"website_and_mobile_tnc-1" ofType:#"html"];
NSString* htmlString = [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:nil];
[_TandCView loadHTMLString:[headerString stringByAppendingString:htmlString] baseURL:nil];
If you want to load using NSData, try to make baseUrl to "nil" in your code.
I have modified your code as below,
NSData *htmlData = [NSData dataWithContentsOfFile:htmlFilePath];
[wView loadData:htmlData MIMEType:#"text/html" textEncodingName:#"UTF-8" baseURL:nil];
I didn't tried this, but please try and let me know.

Using a UIWebView with loadHTMLString/loadData breaks the back and forward buttons, workaround?

There's a known problem with embedded UIWebViews that if you load data into them using loadHTMLString or loadData, the canGoBack/canGoForward properties and goBack/goForward methods don't work. These only work when using loadRequest.
Since Safari's normal app cache doesn't work in embedded UIWebViews, creating a native app that effectively caches otherwise live content becomes impossible/unusable. That is, I can cache the contents of the HTML, Javascript, images, etc. and load them via loadHTMLString or loadData, but then the back and forward buttons don't work.
I could also use loadRequest and specify a file URL, but that breaks when it comes to communicating with the live site -- even if I specify a tag (because of cookie domain issues).
I have a work around that involves basically re-implementing the app cache using local store (and not having the native app do any caching itself), which is OK, but not really ideal. Are there any other work arounds/something I missed?
I am using the UIWebView's canGoBack to check to see if I'm at the first page of the history. If I am then I just call the little method I used to load the first page (displayLocalResource sets up the HTMLString and loads it into the webView). Here is a snippet:
//Implementing a back button
- (void)backOne:(id)sender{
if ([webView canGoBack]) {
// There's a valid webpage to go back to, so go there
[webView goBack];
} else {
// You've reached the end of the line, so reload your own data
[self displayLocalResource];
}
}
So do you download the HTML yourself, then pass it to UIWebView as a string? Why so? Do you modify it on the fly or something?
Maybe a custom URL schema would help? You use loadRequest with a schema of your own, which in turn works with HTTP and then feeds the webview whatever data you want?
I had a same problem. I tried manage the history, but it is error prone. Now I have discovered a better solution of this.
What you want to do is simply add a loadRequest to about:blank and make that as a placeholder for you before you call loadHTMLString/loadData. Then you are totally free from monitoring the history. The webview.canGoBack and canGoForward will just work. Of course, you will need a hack to handle go back to the placeholder about:blank. You can do that in webViewDidFinishLoad. Here is the code highlight:
In the function when you call loadHTMLString:
[weakSelf.fbWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
[weakSelf.fbWebView loadHTMLString:stringResponse baseURL:url];
Code to handle goBack:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if ([webView.request.URL.absoluteString isEqualToString:#"about:blank"]
&& ![webView canGoBack] && [webView canGoForward]) {
[weakSelf.fbWebView loadHTMLString:stringResponse baseURL:url];
}
}
I think it is also possible expand this solution to handle those loadHTMLString that is not the first load. Just by having a Stack to record all the string response and insert an about:blank on each loadHTMLString. And pop the stack when each time go back to about:blank.
Could you fetch the content, save it to the local filesystem, point the webview to the local filesystem using file:// URLs, then intercept the link follows with shouldStartLoadWithRequest to fetch more to local fs, point webview at new local content, etc?
I've had good luck with UIWebView and file:/// URLs. Basically you'd be intercepting load requests, fetching stuff yourself, writing it to the local filesystem with rewritten URLs, then loading that into the browser.
There seems to be no way to load/save the browser history.
Loading the string into a temp file and using that as a URL request seems to cure this. It's something about loading the string directly that causes UIWebView not to see it as the home page you can navigate back to. This code worked for me:
//If you load the string like this, then "webView.canGoBack" never returns YES. It's documented frequently on the web.
//Loading the string as a URL instead seems to work better.
//[self.myWebView loadHTMLString:str baseURL:nil];
//write the string to a temp file
NSString *fileName = #"homepage.html";
NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]];
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[data writeToURL:fileURL atomically:NO];
//open that temp file in the UIWebView
[self.myWebView loadRequest:[NSURLRequest requestWithURL:fileURL]];
Use this to enable/disable the back button:
- (void)webViewDidFinishLoad:(UIWebView *)webView{
//this is to check if we're back at the root page.
if (webView.canGoBack == YES) {
self.backButton.enabled=YES;
}
else {
self.backButton.enabled=NO;
}
}