Why is UIWebView canGoBack=NO in iOS7? - ios

I'm embedding this web site into my app like this:
NSString *url = [NSString stringWithFormat:#"https://mobile.twitter.com/search?q=%#", #"#test OR #test"];
url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[self.twitterWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
self.twitterWebView.scalesPageToFit = YES;
And I have 2 buttons for going back and forward in this web site. I'm calling
[self.twitterWebView goBack]; and
[self.twitterWebView goForward]; accordingly.
This works fine on iOS 6 but on iOS 7, my web view's canGoBack and canGoForward properties are NO and thus my back and forward buttons do not work.
As a side note, when the app is installed the first time, and the page is loaded the first time, my buttons work. But when I run my app again, and when I tap on a link on the web site, my web view's canGoBack property begins returning always NO.
How can I solve this?
EDIT: I uploaded a mini test app that demonstrates my problem. You can download it from here. Please run the app on an iOS 7 simulator, see that the back button is working on the first installation of the app. Then quit, run the app again and you'll see that it'll stop working.
By the way the problem seems to be about the twitter mobile site. You can try another web site address and see that.

This seems to be related to HTML5's "Application Cache" functionality. On first launch, the site isn't cached and the UIWebView correctly detects if it can go forward or back. As soon as the cache is populated, new UIWebView instances decide that, even if the URL changes (which can be observed in UIWebViewDelegate's webView:shouldStartLoadWithRequest:navigationType:), going forward or back is not possible anymore. canGoForward and canGoBack will return NO and goForward and goBack won't do anything. This persists across restarts of the app, as long as the HTML5 cache for this specific site exists.
Maybe this problem is limited to web apps that modify the URL's Fragment identifier after the hashmark via JavaScript.
And yes, the UIWebView's behavior in this situation DID change between iOS 6 and iOS 7.
I haven't found a solution yet, and we'll probably have to wait for Apple to fix this in iOS 7.1 or so.
Edit
Other people have this problem, too:
If you are using Application Cache and also managing
states through hash or other technique, the history object will not
keep your navigation history, therefore history.back() will never work
and history.length stays in 1 forever.
(from http://www.mobilexweb.com/blog/safari-ios7-html5-problems-apis-review)
Edit 2
This problem exists in Safari 7.0 (9537.71, default in OS X 10.9 Mavericks), too. However, the most recent WebKit nightly build (r158339) seems to work correctly. It's most likely only a matter of time until the fix makes it to an iOS and OS X release.
Edit 3
This problem still exists in iOS 7.1 and and OS X 10.9.2.
Edit 4
This bug has been fixed in iOS 8 and Safari 7.1 (9537.85.10.17.1) for OS X!
Related:
history object doesn't keep navigation history ios7 safari

I had this issue too in iOS 7. What worked for me was moving the "canGoBack" code and "canGoForward" code to shouldStartLoadWithRequest as shown below. Before, I had it in webViewDidFinishLoad, which worked for iOS 6, but did not for iOS 7.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
if ([webView canGoBack])
{
[browserBackItem setEnabled:YES];
}
else
{
[browserBackItem setEnabled:NO];
}
if ([webView canGoForward])
{
[browserForwardItem setEnabled:YES];
}
else
{
[browserForwardItem setEnabled:NO];
}
return YES;
}

I had the same issue. I am able to resolve it with the following changes.
Implemented a new method updateButtons
- (void)updateButtons:(UIWebView*)theWebView {
if ([theWebView canGoBack])
{
self.backButton.enabled = YES;
}
else
{
self.backButton.enabled = NO;
}
if ([theWebView canGoForward])
{
self.forwardButton.enabled = YES;
}
else
{
self.forwardButton.enabled = NO;
}
}
Added Calling the above method in shouldStartLoadWithRequest, webViewDidFinishLoad, didFailLoadWithError events.
Now the tricky part comes. After making above changes, back and forward buttons are working as expected except in one scenario. When we are back to first page by hitting back button, it is not getting disabled. Because it will not fire any of the above events when the page is getting loaded by hitting back/forward button. it just loads from cache.
I have tried many approaches but only one that solved my problem.
Added an observer on WebHistoryItemChangedNotification.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(webViewHistoryDidChange:)
name:#"WebHistoryItemChangedNotification"
object:nil];
Called the same updatebuttons method in webViewHistoryDidChange.
- (void)webViewHistoryDidChange
{
[self updateButtons:self.webView];
}

After changing the property to "strong" reference, my problem disappeared.
before:
#property (nonatomic, weak) IBOutlet UIWebView *webView;
after changing the property to "strong":
#property (nonatomic, strong) IBOutlet UIWebView *webView;

Related

Flurry ads integration using SDK 6.0 creates performance issue app crashes

Hi I am using latest flurry SDK 6.0 in app. I need to show ads almost every where in my app. App is UINavigationControllerBased contains more viewControllers. I am using code given in flurry documentation.
I am using below code in each and every ViewController but some times I move fast just like push and pop before add received,so in that case callbacks are continuously receiving but that viewcontroller is not available in stack at that time app crash or recieve memory. I tried to set adDelegate to nil in viewWillDisappear but flurry documentation says don't set it as nil.
Anyone has any ideas regarading this?
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if(self.adBanner == nil){
self.adBanner = [[FlurryAdBanner alloc] initWithSpace:#"Flurry Banner"] ;
self.adBanner.adDelegate = self;
[self.adBanner fetchAndDisplayAdInView:self.view viewControllerForPresentation:self];
}
}
Perhaps you should split the code in two?
Fetch the Ad using fetchAdForFrame. This prevents Flurry from using the view you passed.
Then inside the delegate, use displayAdInView. Only call this if you know the view is visible. So in viewWillAppear set a flag to say visible, in viewWillDisappear set a flag to say not visible. This should let you cut off the rendering part.
Seems odd an API like this would have no cancelFetchAd method!

SKStoreProductViewController not working on iPad iOS 8

I'm using SKStoreProductViewController on iPad with iOS 8. Pressing a button opens an instance of it:
[self.storeProductViewController loadProductWithParameters:#{SKStoreProductParameterITunesItemIdentifier:appStoreIdNumber}
completionBlock:^(BOOL result, NSError *error) {...}];
[[self getPresentingViewController] presentViewController:self.storeProductViewController animated:YES completion:^{...}];
It pops up the first time fine. But after it is dismissed, pressing the button again to reopen it throws an exception and the pop up never appears.
<SKRemoteProductViewController: 0x15c1ec00> timed out waiting for fence barrier from com.apple.ios.StoreKitUIService
Anyone know what's going on or how to work around it?
I've also had some other problems with UIAppearance and StoreKit not working together (see StoreKit's SKStoreProductViewController leaving space between the nav bar and the view?)
I was having the same problem that you now have and removing all UIAppearance code fixed it, but I noticed that the SKStoreProductViewController won't load any iPad apps on an iPhone on iOS 8 and it will fail silently. Let me know if you have the same problem.
I had the same problem, where the first time I present the SKRemoteProductViewController it works fine, but subsequent presentations give the error as the OP described. Apparently the problem is related to presenting the same SKRemoteProductViewController more than once. If I dispose of the SKRemoteProductViewController after presenting it and then re-load another one each time I want it to present again then things work without issue.
Had this problem as well. Make sure that identifier is actually an NSString
#{ SKStoreProductParameterITunesItemIdentifier : identifier }
I was inadvertently passing an NSURL object, which resulted in the same error -- "timed out waiting for fence barrier from com.apple.ios.StoreKitUIService"
you should present SKStoreProductViewController before loading it

LibStatusBar icon disappears on 3rd-party app launch

I wrote a tweak for Cydia, it adds an icon to the status bar.
It works fine on the home screen and when SpringBoard is launched, also, if an app is already launched then it works fine,
however, if an app (such as Facebook or Twitter) is closed (completely) and the icon is showing, when launching the app, it will cause the icon to disappear.
The icon is displayed using libStatusBar using this code:
if(icon) // if icon needs to be removed
{
[icon release];
icon = nil;
}
...
// add the icon to the status bar
icon = [[%c(LSStatusBarItem) alloc] initWithIdentifier:[NSString stringWithFormat:#"muteIconLablabla"] alignment:StatusBarAlignmentRight];
icon.imageName = [NSString stringWithFormat:#"Mute"];
I also tried using the methods suggested in libStatusBar README file
[[UIApplication sharedApplication] addStatusBarImageNamed:#"ON_Mute"]; // and removeStatusBarImageNamed:...
I tried overriding -(id)init and updating the icon there, but the same result.
The code shown above is being called from a static void function. this function is being called several times, for example from -(void)applicationDidFinishLaunching:(id)application
under %hook SpringBoard and -(void)ringerChanged:(int)changed
All inside Tweak.xm.
The problem happens in iOS7 as well.
It's been a while since I've used libstatusbar, but if you are absolutely sure the LSStatusBarItem is not being released, it's possible it's being hidden by Springboard or another app. Consider setting icon.visible = YES explicitly. You also might want to consider setting timeHidden on LSStatusBarServer to NO explicitly by calling [item setHidesTime:NO].
Additionally, if you're not making any changes to the icon, set icon.manualUpdate = NO.
References:
Libstatusbar on the iPhoneDevWiki
LSStatusBarItem.mm source

Popover changes in iOS 5?

I have just developed an iPad app to the point of testing, but did it in iOS 4.3. Now I've updated to 5.0 in the simulator and also went through Apple's steps to test on my iPad which runs 5.0
In the 4.3 simulator all works fine. But in the 5.0 simulator and on the iPad all of my popovers that originate from UIButtons crash the app. I have a popover coming from a navbar button which works fine.
Each popover that crashes will display its contents (a UIWebview with a pdf file), but when I then click anywhere on the screen the app crashes (within the popover and outside).
I can post some code, but hope that this description helps give someone an idea. I don't manually dismiss the popover or check if it is open, but since this doesn't only occur by trying to touch its launching UIButton I don't think that's why it's happening. Plus it works as is under 4.3
Edit: This is solved now thanks to Stephen's comment. I added into the popOver's content viewController:
- (void) dealloc {
[webView release];
[super release];
}
Usually I would call [super dealloc] instead of [super release], but [super dealloc] didn't fix the exception (exc_bad_access). Hopefully I haven't put a sloppy patch on the problem!
I had a thread started to collect bugs like this, but the forum police quashed it. Suffice it to say that iOS 5 is riddled with incompatibilities.
I can't say with any certainty what your problem is, but there's a good chance it has to do with the changes to UINavigationController, which caused UIViewController's navigationController to be nil for popups, with parentViewController taking its place.
Unfortunately, parentViewController is new, so you must, eg, test respondsToSelector:#selector(parentViewController) and take parentViewController if it exists, otherwise navigationController.
Had to add this logic in about 30 places in an app we have.

UIWebView getting stuck after webViewDidStartLoad

Our iOS app has a Web View that is rendering pages from file: URLs. When the user touches an <A> element to switch pages, about 20% of the time, it gets stuck as follows:
User taps <A> link
We get WebView.shouldStartLoadWithRequest callback
We get WebView.webViewDidStartLoad callback
nothing happens after this
The screen still shows the original page with the link on it. We can break the logjam in two ways:
Rotate the device
Tap the screen
At that point, the page will immediately finish loading.
We used the recipe from here:
Javascript console.log() in an iOS UIWebView
to allow us some insight into the page load. We put the javascript-side stuff right in the first script file we load on the page, and it doesn't print its message until after we do the rotate-or-tap workaround.
So it appears that it is getting stuck somewhere between starting to load the page, and starting to evaluate the stuff on the page.
We have tried a number of work-around, none of which helped:
Setting location.href instead of using the tag
Setting location.href from javascript timeout
In the didStartLoad callback, created a thread that called setNeedDisplay on the webView over and over
Any idea what we might be doing wrong?
You could use gdb within XCode to debug the problem. I think canidates for messages that could have break points are:
- (void)webViewDidStartLoad:(UIWebView *)webView
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
I'd also add a break in whatever UIViewController is showing your UIWebView for:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
Then you could step through and hopefully catch what's wrong with your UIWebView subclass.
If you don't want to add breakpoints you could just run your app in the XCode debugger and hit the "pause execution" button above the console window when your app becomes unresponsive while loading.
It's hard to know what's going on without any code to go off of but I bet Xcode can help you find the issue quickly.
Good luck!
Found the solution for this weird behavior, at least in my case.
In my App i've created a subclass of UIWebView. I know that Apples documentation notes that you should not subclass UIWebView. But it's "necessary" in my App.
The problem was, that i've overwritten scrollViewDidEndDragging:willDecelerate: without calling super.
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
// my code
}
becomes to
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
[super scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
// my code
}
Now it always loads the web site when tapping on a html link.

Resources