So, I figured it out.
First I set the delegate of the webview, so that I get scroll events and can check, if the webview is scrolled to top or bottom:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if([scrollView isEqual:webView.scrollView]) {
float contentHeight = scrollView.contentSize.height;
float height = scrollView.frame.size.height;
float offset = scrollView.contentOffset.y;
if(offset == 0) {
webViewScrolledToTop = true;
webViewScrolledToBottom = false;
} else if(height + offset == contentHeight) {
webViewScrolledToTop = false;
webViewScrolledToBottom = true;
} else {
webViewScrolledToTop = false;
webViewScrolledToBottom = false;
}
//NSLog(@"Webview is at top: %i or at bottom: %i", webViewScrolledToTop, webViewScrolledToBottom);
}
}
Then I registered additional swipe gesture recognizers at the webview's scrollview:
swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeUp)];
swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
swipeUp.delegate = self;
[self.webView.scrollView addGestureRecognizer:swipeUp];
[self.webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:swipeUp];
swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeDown)];
swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
swipeDown.delegate = self;
[self.webView.scrollView addGestureRecognizer:swipeDown];
[self.webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:swipeDown];
Notice the calls to [self.webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:swipeUp];
. Those are absolutely necessary, because without them, the pan gesture recognizer of the webview would always consume the events, before they reach the swipe gesture recognizer. Those calls change the priorities.
In the swipeUp and swipeDown methods, I calculate the position of the next "page" and scroll the parent scroll view to this position, if there actually is a next page.
Last thing is, to check, if the webview is scrolled to top or bottom and only accept the gestures in that case. Therefor you have to implement the delegate of the gesture recognizer:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if(gestureRecognizer == swipeUp) {
return webViewScrolledToBottom;
} else if(gestureRecognizer == swipeDown) {
return webViewScrolledToTop;
}
return false;
}
You might also have to disable the scroll bouncing to make this work with webpages, which are so small, that they are not scrolled at all:webView.scrollView.bounces = false;
As UIWebView
internally implements UIScrollViewDelegate
Methods. Its is unlikely that the UIScrollViewDelegate
will get reassigned to other objects except the current UIWebView object.
So, I found a small work around. You can subclass UIWebView implement the UIScrollViewDelegate
methods in that class and don't forget to forward the method to super or else unexpected behavior may occur.
This is how I solved the above problem:
/////in WebView.h file
//////WebView is subclass of UIWebView
#import <UIKit/UIKit.h>
@interface WebView : UIWebView
@property(nonatomic,weak) NSObject <UIScrollViewDelegate> *mScrollDelegate;
@end
//// in WebView.m file
#import "WebView.h"
@synthesize mScrollDelegate;
@implementation WebView
- (id)init
{
self = [super init];
if (self) {
self.scrollView.delegate = self;
}
return self;
}
/////scrollViewDidScroll Method, Also I am forwarding it to super class UIWebView
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
[super scrollViewDidScroll:scrollView];
if([self.mScrollDelegate respondsToSelector:@selector(scrollViewDidScroll:)]){
[self.mScrollDelegate scrollViewDidScroll:scrollView];
}
NSLog(@"scrollViewDidScroll");
}
@end
And make sure your self.webView
must be of type WebView
.
in SDDefinitionViewController.m
self.webView.mScrollDelegate = self;
Now your scrollViewDidScroll method in SDDefinitionViewController.m will get called
Best Answer
Implement the deinit method in your view controller and set the scroll views delegate to nil: