synchronous UIWebview request - ios

I'm currently working on a epub application.
So i use UIWebView in order to render my epub (html).
But, for animations reason, i would like to load my request synchronously.
How can i do it ?
I tried to use sendSynchronousRequest, but without success (my request were still asynchronous).
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html

If your problem lays with the delay that UIWebView introduces when rendering the HTML, so that you do not want to show the user a blank page and only after a second or so the rendered page, the only way I have come up to deal with this is:
having initially the UIWebView hidden;
defining webViewDidFinishLoad so that when it fires the web view is made visible.
If you put your UIWebView into a contaning view and define its alpha properly, you can get nice transitions with this approach.

The only real difference in terms of user experience between synchronous and asynchronous is that in the former case the user interface freezes up. So if that is the effect you want, just set all the userInteractionEnabled properties to NO when you launch the request, and switch them back on once the request is finished in connectionDidFinishLoading:.

I think you can get the html as a string with this:
NSError * error;
NSURL * url = [NSURL URLWithString:#"http://www.yoururl.com"];
NSString * htmlContent = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
And the load the html:
[webView loadHTMLString:htmlContent baseURL:url];

Related

UIWebView serialization after content has been rendered

I have a very large HTML Document that I need to show in my app. It utilizes CSS Columns, and scrolls horizontally. What I try to do is archive the UIWebView that renders the document in its current state, so that I can unarchive it from CoreData and don't have to let the user wait for a few seconds until it's rendered. So inside my UIWebViewDelegate, I serialize using the following method when webViewDidFinishLoad is called (the content is loaded from string, not from external sources):
[NSKeyedArchiver archivedDataWithRootObject:self.webView];
I checked if webViewDidFinishLoad is called multiple times, but it isn't. This and the core data saving actually works, i.e. it does save and load properly the next time the app is launched. However, while it saves the UIWebView itself, it seems like the content isn't loaded into it, which makes the whole procedure kinda useless for my purpose. Is my understanding of saving an object this way wrong, or is it simply a question of implementation?
Many thanks!
I dont think you should be archiving the UIWebview. Instead you can save the html document fil with custom CSS elements in the documents directory of the app and render it using the filepath using the following code.
self.webview.scalesPageToFit= YES;
[self.webview loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]]];

webViewDidFinishLoad did finish loading but didn't finish showing content on screen

I'm using webViewDidFinishLoad a lot in my app and there's something about UIWebView that really bugs me, well, actually two things.
The first, when I load new content to a UIWebView I will see for half a second the last page that was loaded to the same UIWebView what will force me to "clean" the UIWebView using something like:
[_mainWebView stringByEvaluatingJavaScriptFromString:#"document.open();document.close();"];
before loading the new content.
The second issue I have and that's the main issue for this question is that if i'll load some new content to my UIWebView and do something like this:
[_mainWebView loadHTMLString:htmlString baseURL:nil];
...
- (void)webViewDidFinishLoad:(UIWebView *)webView {
_mainWebView.alpha = 1;
}
In some cases the UIWebView will show up white for half a second before showing up the content. I'm guessing that the content is already loaded into the UIWebView and that's why webViewDidFinishLoad:webView is firing but for small html pages showing to content takes loner than the actual load. Is there any workaround I can use to avoid the blank screen that is showing for a sec or so but still save that second?
I thought about animating the alpha from 0 to 1 but that solution feels kinda lame to me.
Try adding a javascript callback so you know when the web view contents have actually loaded: Javascript in UIWebView callback to C/Objective-C

Adding a lot of dynamic formatted text to iOS App

I have a big issue with my current development stage on a project that I'm working on.
I'm have a UIScrollView which holds 50 multiline UILabels with dynamic content loaded from a localizable.string. The labels are individually formatted (font, bold, italic, color).
The problem is that the App's real memory usage jumps to almost 70MB (live bytes 3MB) and that is just unacceptable and with my current concept I would have to use ~200 UILabels in order to achieve my goal.
What can I do ? Is there a way to lazy load the UILabels or reuse them ? Should I use UITextView or UIWebView ?
How can I do that ?
Thanks.
I would recommend using something like https://github.com/AliSoftware/OHAttributedLabel or a UIWebView OHAttributedLabel should have a much smaller memory foot print than a WebView But it depends how many web views you would actually need.
if you use a UIWebView
You'd draw a web view in your view, if you are using IB create and hook up outlets and then in your .m load the html formatted text into via loadHTMLString:baseURL:
Well guys with your help, I've came up with this solution which works just extraordinary !
I'm using a html "template" file with markers inside it and a UIWebView.
Basically what I'm doing is the following:
get the html file path.
create a string with the contents of html template.
replace the markers from the html with my strings (NSLocalized strings - lots of text).
load into the WebView contents of the newly created string using "loadHTMLString".
Result: from a memory footprint of 70MB, now I have a memory footprint of 12MB (and that with the equivalent of ~20 A4 pages of text).
Here's the code:
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:#"yourhtmlfile" ofType:#"html"];
NSURL *baseURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
[_webView setBackgroundColor:[UIColor clearColor]];
[_webView setOpaque:NO];
NSString *htmlBody = [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF16StringEncoding error:nil];
htmlBody = [htmlBody stringByReplacingOccurrencesOfString:#"//marker_1//" withString:NSLocalizedString(#"localizedKey_1", nil)];
htmlBody = [htmlBody stringByReplacingOccurrencesOfString:#"//marker_2//" withString:NSLocalizedString(#"localizedKey_2", nil)];
htmlBody = [htmlBody stringByReplacingOccurrencesOfString:#"//marker_3//" withString:NSLocalizedString(#"localizedKey_3", nil)];
[self.webView loadHTMLString:htmlBody baseURL:baseURL];
And now I can use the html goodies like text formatting :D !
I hope that the above will help a lot of people :) !
Thank you guys for support !

How can I detect the completion of view layout for a UIWebView

I want to allow a user to associate a zoom factor with a document and use it as a starting point when displaying that document inside a UIWebView. It seems, however, that webViewDidFinishLoad: only indicates the end of in-memory loading, not including rendering or layout. Here's sample code to demonstrate the problem:
- (void)viewDidLoad {
[super viewDidLoad];
UIWebView *webView = (UIWebView *)self.view;
webView.delegate = self;
webView.scalesPageToFit = YES;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSString *urlString = [[NSBundle mainBundle] pathForResource:#"Doc" ofType:#"pdf"];
NSURL *file = [NSURL fileURLWithPath:urlString];
[(UIWebView *)self.view loadRequest:[NSURLRequest requestWithURL:file]];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (!webView.isLoading) {
[webView.scrollView setZoomScale:1.5 animated:YES];
}
}
The call to setZoomScale: executes with no effect (i.e. the file is displayed with a zoom factor of 1.0), apparently because it happens before the scroll view is in some state where it can deal with it. If I change the final method above to what follows, everything works as I'd hoped.
- (void)delayedZoomMethod {
[((UIWebView *)self.view).scrollView setZoomScale:1.5 animated:YES];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[self performSelector:#selector(delayedZoomMethod) withObject:nil afterDelay:1.0];
}
This, of course, is a bad idea because the 1.0 delay is arbitrary, probably too long for the vast majority of cases, and will likely be too short under some unknown set of conditions.
The docs say, "Your application can access the scroll view if it wants to customize the scrolling behavior of the web view." Does anyone know of a notification, or property in the web view or its scroll view, that I could observe to be told when that statement becomes true?
If you have control of the html code this may help. I've been able to have html events trigger objC methods in the following manner.
html event triggers javascript that writes a string to window.location
the uiwebview then uses uiwebviewdelegate to receive the string with this function
webView:shouldStartLoadWithRequest:navigationType:
It's kind of hacky.
But I don't think there is a way to know the web view has finished rendering, unless you wrote the html page and can put a js function in to execute at the end of the page.
If you are using UIWebView to preview PDF files, then it may not be the best way. There is a QLPreViewController (from iOS 4.0) that is intended for this purpose. A Quick Look preview controller can display previews for the following items:
iWork documents
Microsoft Office documents (Office ‘97 and newer)
Rich Text Format (RTF) documents
PDF files
Images
Text files whose uniform type identifier (UTI) conforms to the
public.text type (see Uniform Type Identifiers Reference)
Comma-separated value (csv) files
If I change the final method above to what follows, everything works
as I'd hoped.
This, of course, is a bad idea because the 1.0 delay is arbitrary,
probably too long for the vast majority of cases, and will likely be
too short under some unknown set of conditions.
The docs say, "Your application can access the scroll view if it wants
to customize the scrolling behavior of the web view."
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (!webView.isLoading) //WEBDIDFINISHLOAD? {
[webView.scrollView setZoomScale:1.5 animated:YES];
}
}
look, the didFinishLoadMethod is called by an asynchronous notification from NSURLConnection,
its not necessarily in the main thread (since your UI update is not working)
The performSelector..after delay method is fine. The delay doesnt even really mean anything,
You are just jumping to the main thread to do your UI update and allowing the
NSURLConnection notification to complete.
It probably wouldnt matter if you said afterDelay:0.0
This is a common situation in iOS and OSX, you have to figure out what threads are capable of updating the UI, GCD, NSOperationQueues, and background runLoops on created threads all complicate this.
You found your solution, but you need to know what it means. Do the call to performSelector on mainThread, set a short delay and its fixed.

How to calculate scrollWidth of HTML page for fixed height without using UIWebView

We can use the following to calculate the scrollWidth of an HTML page in a UIWebView with a fixed height.
[webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.scrollWidth"]
Is it possible to do this evaluation on local HTML pages without loading a request into a UIWebView?
Would like this to occur on a separate thread.
No(sort of). The html would need to be rendered in some capacity to be able to get any kind of measurements. The reason for this is inherent in the way html/css/javascript work together (javascript modifies html/css properties, css modifies html). If you had to get the width without using a UIWebView, you would be stuck with one of two options:
Render the page yourself, whether it's with your custom code (thousands of man hours, don't do this) or some open source library. I doubt this will be any faster than UIWebView, and will likely introduce unexpected bugs.
Measure the page in the background, and store that value somewhere. This would basically be a form of caching for the size. If the pages are static from when you ship, just do it manually, and hard code it. If they are dynamic, write a class to measure them on startup and store the value. The dynamic measurement would require UIWebView
EDIT:
Are you trying to do this because UIWebView is blocking you're main thread, causing jerkiness in your app? If so, have you considered trying something like this?:
UIWebView *webview = [[UIWebView alloc] initWithFrame:CGRectMake(0,0,320,460)];
webview.delegate = self;
[webview loadRequest: [NSURLRequest requestWithURL:[NSURL urlFromString:#"/path/to/file.html"]]];
... and then later on...
- (void) webViewDidFinishLoad:(UIWebView *)webView
{
// run your code to get scroll width of page
}

Resources