delay loading of UIWebView in UITabBar app - iphone

I have an UITabBar based iPhone app with 4 different UIWebViews under every tab. Right now all the UIWebViews load on startup and it takes a little bit too long.
I would like to load the default tab's UIWebView first, then load the others in the background. What is the best way to do this?
I have seperate ViewControllers set up for each tab and have this in every .m file:
- (void)viewDidLoad {
NSString *urlAddress2 = #"http://google.com ";
//Create a URL object.
NSURL *url2 = [NSURL URLWithString:urlAddress2];
//URL Requst Object
NSURLRequest *requestObj2 = [NSURLRequest requestWithURL:url2];
//Load the request in the UIWebView.
[webView2 loadRequest:requestObj2];
}
Is there a simple way to tell the other 3 tabs to start loading a few seconds after launch instead of at launch? Would that be a good idea?
Thanks a lot!

I've gotten around this issue by implementing a model layer through which all requests pass. Requests are queued and serviced in priority order, generally one at a time. I've added specific methods to allow a controller to escalate the priority of requests so that, if necessary, two or more requests will be active at once. When a request finishes, it alerts it delegate (the WebView's controller) that data is ready to be loaded.
Depending on how you want to set things up, you can put a callback in "webViewDidFinishLoad" (or, perhaps, shouldStartLoadWithRequest or webViewDidStartLoad) that triggers the model layer to dequeue and service the next request. For safety, you'll also want a timeout in the model layer.
Note: you'll also need to add some custom code into shouldStartLoadWithRequest to differentiate between clicks and the model layer pushing data in. I.e. you'll want to return NO or YES depending on the navigationType.

If you use ASIHTTPRequest instead of NSURLRequest, you can fire a synchronous request for the first URL. Once that request is complete, you can then fire off the other three URL requests asynchronously (i.e., in the background).

You can use NSTimer, or do the loading in viewDidAppear or similar.

Use viewDidAppear. This will be sent to the controller after the view fully appears and animations end.

Related

iOS uiwebview goBack history control

I am loading a "webpage1" on an uiwebview and saving its first request on an instance variable "webviewHistory". Then, I must reuse that webview to load another page, "webpage2" (saved its first request to "webviewHistory" too), and history should start now from this request. Problem is if I execute goBack (button IBAction) from "webpage2", I can keep going back to "webpage1" history and following.
If I check request and compare with initial saved, works! but not with redirections, for example, if I trigger request to www.youtube.com, request is redirectioned to m.youtube.com and first one is not saved as navigation history! how to solve it?
if (![webViewSocial.request.URL.absoluteString isEqualToString:self.webviewHistory]){
[webViewSocial goBack];
}
UIWebviews have instance methods that allow you to go backwards and forwards with the history, without you having to track the last request. Is there a particular reason you are saving the last request specifically?
Sample code:
if ([webView canGoBack]) {
[webView goBack];
}
Here is a project that is a simple uiwebview browser if you want to play with it:
https://github.com/msencenb/UIWebView-Example
for swift 3 version of Msencenb answer:
if webView.canGoBack {
webView.goBack()
}

synchronously load html content in UIWebView

im using a UIWebView to show html content in my app, the app contains two arrows to navigate between topics just like a RSS reader app, but when the user hits up or down arrow, the next topic doesn't show up until the data come back and the user still able to interact with the UI which is a bit confusing,
My question: how to block the UI when user moves to the next/back topic ? in other words how to make loadHTMLString:baseURL: works as a synchronous calling ? thanks !
You can let the load happen asynchronously, but set the web view's userInteractionEnabled property to NO. (then back to YES, on the didFinishLoad callback).
Or you could put up a clear colored view (with userInteractionEnabled set to NO) above the web view that has an activity indicator and button that lets the user cancel the load.
An even better idea would be to place two other web views offscreen and start loading them for page N-1 and N+1. When the user presses a page arrow, swap frames with the corresponding prefetched web view.
Try this - https://github.com/gavrix/UISynchedWebView-demo
You don't actually want to block the UI. If you do that, there is a very high probability that Apple will reject your app once you send it in for app store submission. Anything that even remotely makes the application feel unresponsive will weigh heavily on you. Instead, create a background thread using GCD or performSelectorInBackground, handle your loading in that, and then once the loading is done, make all of the information available to your UIWebView all at once and alert it to render the display.
If you are in a pinch and have a UIPageViewController and still want to use a UISynchedWebView to ensure that a page has loaded before you run javascript, you can run a block on the main thread's event queue. Still has a slight delay while the javascript runs but won't cause recursion in the run loop.
dispatch_async(dispatch_get_main_queue(), ^{
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]]]; // self.webView is a UISynchedWebView
NSLog(#"url:%#", [self.webView stringByEvaluatingJavaScriptFromString:#"window.location.href;"]); // shows google.com instead of about:blank
});

Cocoa Touch begin command AFTER viewDidLoad

I have an application in which I am required to connect to the internet after a view is loaded. However, if I put this code in the viewDidLoad method the parent view freezes, and then unfreezes after the connection onto the new view. However, I would like the new view to load FIRST, and then to start the connection. I tried using viewDidAppear:, however I am getting the same issue.
Also, will any animations continue playing during the connection? Will the UI be responsive? If not, is multithreading the way to go?
Here is some of my code:
-(void)viewDidLoad {
[super viewDidLoad];
//Do some other view initialization
//Connect is a class I use to connect to the internet
[Connect getData:someString];
}
When I put the code in viewDidAppear the same thing happens.
Connection code:
NSMutableURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSHTTPURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
Also, I forgot to mention that I am running a regular expression as well after the connection.
As the name of the method says, the view has already been loaded when viewDidLoad executes.
Generally, be sure to use asynchronous connections to connect to the internet. Never block the main thread.
it is easier than you may think.
All you need is some thread management. On the view did Do:
[NSThread detachNewThreadSelector:#selector(yourMethod:) toTarget:yourTarget withObject:yourObject];
and later in another part do:
- (void)yourMethod:(id)sender{
//download the info but do not update the GUI
[self performSelectorOnMainThread:#selector(updatingTheGUI:) withObject:yourObject waitUntilDone:NO]
}
- (void)updatingTheGUI:(id)sender{
//Update your GUI
}
You will notice that the viewDidLoad method documentation of UIViewController states:
...Called after the controller’s view is loaded into memory.
This doesn't necessarily mean that it's called after the view is displayed on screen.
To answer your other questions, if you make your network request the way you have described, no, animations will not continue playing while the request is in progress and no, you can't guarantee that the UI will be responsive. This is because the network request will take an unknown amount of time. Therefore, if you make the request on the main thread, the main thread will be blocked for that period of time, however long it takes.
And, as for the last question, is multithreading the way to go? As others have stated, the easiest and probably most popular way of handling this is to initialize the NSURLConnection with initWithRequest:delegate:. The delegate being your UIViewController or Connect class, or whatever class you want to conform to the NSURLConnectionDelegate protocol and use the NSURLConnectionDelegate methods to process the downloaded data. NSURLConnection will do the work asynchronously and keep the main thread free to handle animations, displaying the UI, etc.
I know it sounds a bad idea for your app. performance but try giving a delay or sleep in between to check if it works that way. Later try to implement the asynchronous call as someone earlier stated..

vs. [UIWebView loadRequest:] - optimizing return from iAD to a view with a UIWebView

I have a view that includes a UIWebView as well as an iAD AdBannerView.
To optimize the experience and reduce bandwidth contention - I suspend the UIWebView's network activity when the iAd "detail view" is being loaded and resume it when the user returns from the ad. Currently, I simply do the following:
-(BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
if (self.webView.loading) {
[self.webView stopLoading];
self.loadingWasInterrupted = TRUE;
}
return YES;
}
-(void)bannerViewActionDidFinish:(ADBannerView *)banner
{
if (self.loadingWasInterrupted) {
//Should use loadRequest: instead?
[self.webView reload];
}
}
I'm trying to understand if it there is any difference between calling reload vs. loadRequest: a second time, and if so, which is more efficient.
I'm guessing reload simply just saves you having to hold onto the request object and really does the same thing but I'd like to know for sure. The docs and header don't offer any clue.
I understand I could pick apart the network activity to understand what's happening but would appreciate any insight from someone who has looked at this before or who generally understands if reload behavior differs from loadRequest at all. Thank you.
Okay, a fairly complicated solution but never the less one that I think might work for you:
Define a custom protocol handler for http:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html#//apple_ref/doc/uid/10000165i
Using NSURLProtocol as the subclass. The handler will start a NSURLConnection, and return the data as it comes in to the client (in this case this will be the UIWebView that initiated the connection). Have it add itself as an observer to NSNotificationCenter for a "Pause" notification.
When you would like to display an iAd, you can send your pause notification, this will cancel the NSURLConnection (but not the NSURLProtocol, which will remain open and loading, and thus your webview will continue to appear as if it were loading).
Then when the add is finished you can send a "resume" notification (much the same), except in this case any active NSURLProtocol handlers receiving the notification will create new NSURLConnections, using the Range: header to resume where they left off:
iphone sdk: pausing NSURLConnection?
Some caveats:
Only will work when browsing websites that support the resume header (otherwise you might have to start the connection anew, and just ignore data prior to the latest byte received).
Your NSURLRequests should be formed so that they don't have a timeout. (if you want a timeout then it should be in the NSURLProtocol handlers NSURLConnections).
I'm guessing here, but I believe the reload is doing a loadRequest: internally. If you are really intent on testing this you cold add a temporary subclass to UIWebView and override the reload and loadRequest methods just for the sake of logging.
- (void)reload
{
NSLog(#"reload called");
[super reload];
}
- (void)loadRequest:(NSURLRequest *)request
{
NSLog(#"loadRequest called");
[super loadRequest:request];
}
When you call the method "loadRequest", you call it like
[webview loadRquest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.ururl.com"]]];
But when u call "reload", u just instruct to reload whatever the current request is.
So basically in latter case , u are saving urself from creating a url from string and then a request from url and that makes it pretty convenient for use.
As per case of functionality, reload itself calls loadRequest so basically there is no difference in terms of efficiency and even in speed.
However basically for ur case and in many of my cases , the thing which we want but Apple has not given us is something like:-
[webview pauseLoading];
[webview resumeLoading];
So to sum up the whole thing , use any of them but if u r lazy like me to again specify the urlrequest just use "reload"
Apologies
Sorry guys I used to think that reload must be calling loadRequest but when I tried NWCoder's method, it doesnot call loadRequest on calling reload. But I still think reload and loadRquest follows the same method for loading the page

Preload UIView To Minimize Delay While Running Iphone App

I have an iphone app that has one view that needs to fetch a lot of data off of a variety of internet sites. Therefore, the amount of time required for it to load is unacceptable. I was wondering if there is any way to load the view during the 'applicationDidFinishLaunching' method so the delay is at the startup of the app instead of midway through navigation.
Thank you very much!
You want to load the view as quickly as possible, and then launch a background thread or asynch request to pull the data down.
Making your application sleep during initial load isn't advisable. I believe SpringBoard will terminate any application which takes longer than 30 seconds to finish loading.
It's a bad user experience to have the app do something without visible feedback to the user (animated UIActivityView for example)
Have you already considered loading the data asynchronously? While it's loading, the UI doesn't get blocked. For example you can show a nice loading-wheel when your app is loading the data. This is how all good apps do this.
If the initial view has a separate viewController than your 'data' view, you could add a reference to the dataView to the appDelegate and then do something like:
if (self.curAccountManager == nil) {
self.curAccountManager = [[accountManagerController alloc] initWithNibName:#"accountManager" bundle:[NSBundle mainBundle]];
if (![self.curAccountManager isViewLoaded]) {
UIView *tmpView = self.curAccountManager.view;
tmpView = nil;
}
}
This will load the view. But if it's doing a lot of loading, when the user switches to it, it might not respond well. I would suggest you follow the suggestion above and in your data view load the data asynchronously so you can at least show the user status or partial results.