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
});
Related
A general question on UIWebViews. I have a single webview in my app. There are buttons on the left which simply load up different URLs into the webview.
Are there any ill consequences to loading a new URL into the webview without [webview stopLoading] the previous request first?
For example, this flow could happen:
User presses button1 to load urlA into the webview
While urlA is still loading, user presses button2 to load urlB into the webview
But would this better?
User presses button1 to load urlA into the webview
While urlA is still loading and user presses button2 for urlB
First call [webview stopLoading], then proceed to load urlB.
Are there any best practices for such a scenario? Does it matter either way?
Thanks!
According to the documentation, stopLoading has to be called before the view is going to disappear or be destroyed. This implies that an asynchronous event would just survive, potentially causing havoc.
However, no mention is made of your scenario. I would still call stopLoading just do be on the safe side. If the loading property of the web view is NO, this method simply does nothing.
I have a MBProgressHUD that shows when data is being pulled on the background asynchronously. Sometimes when the network is slow this will take forever. So as of now I am just hiding it after 30 seconds if it hasn't been dismissed. What is a good way to dismiss this HUD for a slow network connection?
I would say that the best solution is probably to keep the HUD up the whole time the data is loading so that the user knows that something is happening, and perhaps give them an option to cancel it if that is appropriate for your app. Alternatively, if it is possible for you to load and display the data piecemeal (i.e. before you have the entire set of data), then you should just display the HUD until you have enough data that you can start displaying something in the UI that the user can interact with.
Basically, what you want to avoid is a situation where it could appear to the user that nothing is happening and the UI is essentially blank with nothing for them to do.
Implement MBProgressHUD delegate
it will be called every time whether if it is fast or slow network connection,In case of slow network connection there will be a time out and this delegate will be fired,remove the hud from the superview in this delegate
-(void)hudWasHidden
{
[HUD removeFromSuperview];
}
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.
I am creating a tabbar application. One of the tabs is for an rss feed, which is a navigation application. but when i click the tab bat button, it is taking a while to load the view of that tab. It is because the application is waiting for the feed to be loaded from the server. Is there any way to load the view before the loading of that feed takes place. As of now, i'm giving the request in the viewDidLoad method. Thats what is creating the problem. To which part shall i move the code so that the view is loaded instantaneously when clicking the tabbar button.
I recommend this great article on this subject on iCodeBlog, it's a very elegant way of doing this. If you submit your rss feed loading as an NSOperation, it will take place nicely in the background without blocking your main thread.
use:
[self performSelector:#selector(performRSS:) withObject:<nil afterDelay:0.3f];
or
[NSThread detachNewThreadSelector:#selector(performRSS:) toTarget:self withObject:nil];
and place RSS feed related code in a separate function named "performRSS".
I also think that the problem is more that you don't use the HTTP request asyncronously (as Apple recommends). See this document. http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html
It worked for me in my applications.
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.