Reputation: 2675
How can I detect UIWebView reaching the top or bottom? Because I need to trigger an action when I reach the bottom.
So is that doable?
Upvotes: 11
Views: 14060
Reputation: 2778
If you are using Wkwebview:
I am bit late, I implemented delegate of wkwebview to check scrollview reaches on top or bottom.I used to read javascript code through evaluateJavaScript function. Before use set scrollview delegate of your webview and implement scrollViewDidEndDragging delegate function of UIScrollViewDelegate
extension BaseWebViewController: UIScrollViewDelegate {
func scrollViewDidEndDragging(_ scrollView: UIScrollView,
willDecelerate decelerate: Bool) {
webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
if complete != nil {
self.webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
let bodyScrollHeight = height as! CGFloat
var bodyoffsetheight: CGFloat = 0
var htmloffsetheight: CGFloat = 0
var htmlclientheight: CGFloat = 0
var htmlscrollheight: CGFloat = 0
var wininnerheight: CGFloat = 0
var winpageoffset: CGFloat = 0
var winheight: CGFloat = 0
//body.offsetHeight
self.webView.evaluateJavaScript("document.body.offsetHeight", completionHandler: { (offsetHeight, error) in
bodyoffsetheight = offsetHeight as! CGFloat
self.webView.evaluateJavaScript("document.documentElement.offsetHeight", completionHandler: { (offsetHeight, error) in
htmloffsetheight = offsetHeight as! CGFloat
self.webView.evaluateJavaScript("document.documentElement.clientHeight", completionHandler: { (clientHeight, error) in
htmlclientheight = clientHeight as! CGFloat
self.webView.evaluateJavaScript("document.documentElement.scrollHeight", completionHandler: { (scrollHeight, error) in
htmlscrollheight = scrollHeight as! CGFloat
self.webView.evaluateJavaScript("window.innerHeight", completionHandler: { (winHeight, error) in
if error != nil {
wininnerheight = -1
} else {
wininnerheight = winHeight as! CGFloat
}
self.webView.evaluateJavaScript("window.pageYOffset", completionHandler: { (winpageOffset, error) in
winpageoffset = winpageOffset as! CGFloat
let docHeight = max(bodyScrollHeight, bodyoffsetheight, htmlclientheight, htmlscrollheight,htmloffsetheight)
winheight = wininnerheight >= 0 ? wininnerheight : htmloffsetheight
let winBottom = winheight + winpageoffset
if (winBottom >= docHeight) {
print("end scrolling")
} else if winpageoffset == 0 {
print("top of webview")
}
})
})
})
})
})
})
})
}
})
}
}
Upvotes: 0
Reputation: 6694
You could use KVO to detect any change in the contentOffset
property of your UIWebView
instance.
Warning : Be careful because this property is changing very often when scrolling, and the events in the example below will pop really frequently. Don't implement or call high cost function at each contentOffset change !
Here is a code example for doing it in a UIView
subclass containing a UIWebView
:
@IBOutlet weak var webView: UIWebView!
override func awakeFromNib() {
super.awakeFromNib()
webView.delegate = self
webView.scrollView.addObserver(self, forKeyPath: "contentOffset", options: [.New, .Old], context: nil)
}
deinit {
webView.scrollView.removeObserver(self, forKeyPath: "contentOffset", context: nil)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
guard let kp = keyPath else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
return
}
if kp == "contentOffset" {
self.scrollViewDidScroll(webView.scrollView)
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.size.height < 100 {
print("this is the end")
} else if scrollView.contentOffset.y < 100 {
print("this is the beginning")
}
}
As documented by Apple in UIWebview documentation, you should avoid placing your UIWebview instance in a UIScrollView to detect the end/beginning of scrolling.
Upvotes: 1
Reputation: 2267
First of all, you should not put your UIWebView
inside a UIScrollView
as mentionned in the UIWebView
documentation.
Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.
Instead, you can access the UIScrollView
through the UIWebView
properties. Your view controller can implement the UIScrollViewDelegate
.
@interface MyViewController : UIViewController<UIScrollViewDelegate>
@end
Then you can hook to the UIScrollView:
- (void)viewDidLoad
{
[super viewDidLoad];
// Set self as scroll view protocol
webView.scrollView.delegate = self;
}
Then finally detect the top and bottom being reached:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(scrollView.contentOffset.y
>=
(scrollView.contentSize.height - scrollView.frame.size.height)){
NSLog(@"BOTTOM REACHED");
}
if(scrollView.contentOffset.y <= 0.0){
NSLog(@"TOP REACHED");
}
}
Voila, you can detect when top and bottom are reached.
Upvotes: 27
Reputation: 16193
You could put UIWebView inside a UIScrollView (that has bouncing disabled, so the bounce behavior of the views does not conflict in any way) and then when UIWebView scrolls to bottom, the UIScrollView above will take over scrolling and you can listen to its scrolling events.
Seems that it's not that simple after all.
You can find the UIScrollView among UIWebView's subviews and tap into its scroll delegate methods.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(@"We scrolled! Offset now is %f, %f", scrollView.contentOffset.x, scrollView.contentOffset.y);
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIWebView *view = (UIWebView *)self.view;
for (UIView * subview in view.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
UIScrollView *webScrollView = (UIScrollView *)subview;
webScrollView.delegate = self;
break;
}
}
[view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://stackoverflow.com"]]];
}
As the UIWebView loads the web content, its UIScrollView should change its contentSize property. You can use that to determine whether you're at the bottom or at the top already or not.
Upvotes: 1
Reputation: 73588
You can give this a shot -
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(scrollView == yourWebView)
{
int percentScrolled = abs((int)ceil((scrollView.contentOffset.y/scrollView.contentSize.height)*100));
if(percentScrolled > 100 || percentScrolled < 0)
percentScrolled = 0;
if(percentScrolled > 90)
{
//scroll percent is 90. do your thing here.
}
}
return;
}
What is being done here is everytime user scrolls webView, calculate the % scrolled & when the % scrolled is coming to end (say anything > 90%) do whatever you planned to do.
Upvotes: 3
Reputation: 4270
If you're targeting iOS 5, UIWebView
has a read-only scrollView
property that allows you to access the underlying UIScrollView
.
From there, you can hook into the scrollView's scrollViewDidScroll:
delegate method to receive information on scroll events.
Upvotes: 2