In this tutorial, inspired by user feedback, I will show how to add gesture recognizers as alternatives to the “back” and “forward” buttons on the toolbar and also how to suppress spurious error messages.
The starting point for this tutorial is the completed code from part 3:
WebBrowser3.zip
Adding Gesture Recognizers
Gesture recognizers were added to iOS in version 3.2 and dramatically reduce the amount of code required to support user interface gestures. All gesture recognizers are subclasses of theUIGestureRecognizer
class. Currently the following subclasses exist:
UILongPressGestureRecognizer
UIPanGestureRecognizer
UIPinchGestureRecognizer
UIRotationGestureRecognizer
UISwipeGestureRecognizer
UITapGestureRecognizer
The basic procedure for using gesture recognizers is the same for all types. You first allocate the recognizer specifying a target/action to be taken when the gesture is recognized, next you set properties to specify the details of the gesture you want to respond to and finally you add the recognizer to a view.
Open WebBrowserViewController.m
and add the following code at the end of your viewDidLoad
.
// Add gesture reconizers UISwipeGestureRecognizer* leftSwipe = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(backGesture:)] autorelease]; leftSwipe.numberOfTouchesRequired = 1; leftSwipe.direction = UISwipeGestureRecognizerDirectionLeft; [self.webView addGestureRecognizer:leftSwipe];
This code allocates a UISwipeGestureRecognizer
that will call a method backGesture:
(see below) on the view controller, it then specifies that the swipe should a single touch swipe to the left, and adds the newly created recognizer to the web view. Since the base code of this tutorial does not use Automatic Reference Counting (ARC) you should autorelease
the recognizer. If you are trying to use this code in an ARC-enabled project you simply omit the autorelase
.
The code for the “forward” recognizer is similar:
UISwipeGestureRecognizer* rightSwipe = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(forwardGesture:)] autorelease]; rightSwipe.numberOfTouchesRequired = 1; rightSwipe.direction = UISwipeGestureRecognizerDirectionRight; [self.webView addGestureRecognizer:rightSwipe];
When the gesture recognizer recognizes a gesture it calls the specified method with a single argument, itself. In the above code you associated backGesture:
with the left swipe gesture and forwardGesture:
with the right swipe gesture. You must now provide implementations of these methods. At the following methods to WebBrowserViewController.m
:
// MARK: - Gesture Recognizers - (void)backGesture:(UISwipeGestureRecognizer*)recognizer { [self.webView goBack]; } - (void)forwardGesture:(UISwipeGestureRecognizer*)recognizer { [self.webView goForward]; }
The implementations, as you can see, are very straightforward; they simply call the relevant UIWebView
methods.
At this point you should be able to compile and run the project and the new gesture should be working. If you are speedy with you swipes a UIAlertView
may pop-up reporting “Error: The operation couldn’t be completed. (NSURLErrorDomain error -999.)”. The next section explains how to deal with this.
Suppressing Spurious Error Messages
If a user clicks a link within aUIWebView
or performs some other action that causes a new page to load before the current page has completed loading, the view’s webView:didFailLoadWithError:
delegate method will be called with error code NSURLErrorCancelled
(-999
).
Constant UIAlertView
s popping up reporting this “error” are not very useful to the user. The error is benign and informing the user serves no purpose, therefore alter your webView:didFailLoadWithError:
as shown below.
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; [self updateButtons]; if([error code] != NSURLErrorCancelled) { [self informError:error]; } }
More About Gestures
For a more in-depth tutorial on UIGestureRecognizer I highly recommend Ray Wenderlich’s excellent “UIGestureRecognizer Tutorial in iOS 5: Pinches, Pans, and More!“.Download the Completed Tutorial
You can download the completed project for this tutorial here: WebBrowser4.zip
Thank you for posting this. I will go back and see if I can figure out what went wrong with my code ๐ I think I have found a new favorite learning resource.
So my error was that I was adding the gesture recognizer to the view controller not specifically to the UIWebView. Silly silly errors.
Hey Caldwell,
It’s always nice to hear that a tutorial is useful to someone!
As for a new favorite learning resource, you are too kind. This is something I do in my spare time and there never seems to be enough of that to cover all the topics I would like to. If you don’t find coverage of the topic here Ray Wenderlich’s site is excellent and for specific questions Stack Overflow is good too.
If you know more iOS programmers though, please spread the word about this site and consider liking us on Facebook http://www.facebook.com/pages/iOS-Developer-Zone/164355533627750 and following us on twitter @iOSDevZone.
Hey! I’m new to Xcode, so I don’t know the answers to these questions:
Storyboards have been out for a while; is there an advantage to the .xib approach over storyboards? Is there a reason for foregoing Automatic Reference Counting? Why would I not create the top bar and its elements visually instead of programmatically?
There’s a error generated when the user clears the address bar and taps return — how I would I trap the error and ignore the input?
Great tutorial, by the way!
Hi Don,
Welcome to the site!
When I started this series of tutorials storyboards were not available. That being said, storyboards are most useful when there are transitions between screens and since this tutorial does not involve transitions, even if I was rewriting it today, I would not use storyboards. (You can think of a .xib as a single screen in a storyboard.)
Again, when I started this tutorial series automatic reference counting (ARC) was either not available or was only just coming out, if I recall correctly. If I was rewriting the series today I would use ARC. It’s fairly easy to convert an non-ARC project to ARC. The other way around is not so easy.
The top bar was created programmatically because it was not (and I think is still not) possible to create the end result using the visual editor. It was possible to create the nav bar and later alter programmatically, but describing how to do that would have been much more difficult in a tutorial. It’s really hard and tedious to describe how to create things in the interface builder.
To avoid the error you see when the user tries to visit an empty URL you could check for an empty string in -[WebBrowserController loadAddress:event]. A more elegant solution would be to disable the return button when the address field is empty; that’s a little bit too much to cover in a comment.
Hope this helps,
idz
Thanks! I thought the timeline had something to do with the approach.
Followed, liked, and subscribed โ Iโm dialed in to iDZ, now.
Thanks! I thought the timeline had something to do with the approach.
Followed, liked, and subscribed — I’m dialed in to iDZ, now.