iOS uiwebview goBack history control - iphone

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()
}

Related

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
});

Three20: cancel network request

I am using three20 and implement the model like the example of TTRemoteExamples. Now problem is: when I click and open a page, the TTURLRequest sent out, during fetch data from remote, I click to open another page. But the previous network request is still there messed my loaded data. So I want to know the way to cancel previous network request when I switch to another page. Or when I click button to do a new request in the same page.
Thanks~
To cancel a TTURLRequest, keep a reference to it (typically in an instance variable) then send it a "cancel" message. Like so:
[self.myRequest cancel];
If you don't want the delegate to be notified of the request being cancelled, do:
// I'm assuming self is the delegate here, that may not be true
[[self.myRequest delegates] removeObject:self];
[self.myRequest cancel];
You'll typically also want to do this in your view controller dealloc method. If a request continues after the viewController has been deallocated, it will try to send delegate messages to it, and you'll get a bad access crash.
As for the timing of when you cancel it, that's up to you. If you need it to stop when a user leaves your view controller, then implement UIViewController's viewWillDisappear: or viewDidDisappear: methods (don't forget to call super!).

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

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.

delay loading of UIWebView in UITabBar app

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.