Skip to content

Instantly share code, notes, and snippets.

@melke
Created January 29, 2014 08:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save melke/8684172 to your computer and use it in GitHub Desktop.
Save melke/8684172 to your computer and use it in GitHub Desktop.
Moving away a view smoothly above a UIWebView using insets and auto layout
//
// ViewController.m
// webviewscroll
//
// Created by Mats Melke on 2014-01-28.
// Copyright (c) 2014 Mats Melke. All rights reserved.
//
#import "ViewController.h"
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
@interface ViewController ()
@property(strong, nonatomic) IBOutlet UIWebView *webView;
@property(strong, nonatomic) IBOutlet UIView *adView;
@property(nonatomic, strong) id <UIScrollViewDelegate> originalScrollViewDelegate;
@property(nonatomic, strong) UIScrollView *webViewScrollView;
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *adViewTopMarginConstraint;
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *webViewTopMarginConstraint;
@end
@implementation ViewController {
CGFloat statusBarHeight;
CGFloat topViewHeight;
}
- (void)viewDidLoad {
[super viewDidLoad];
topViewHeight = self.adView.frame.size.height;
self.adView.translatesAutoresizingMaskIntoConstraints = NO;
self.webView.translatesAutoresizingMaskIntoConstraints = NO;
// Shift views in >=IOS7 to prevent the top view to appear beneath the status bar
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
statusBarHeight = 20.0;
} else {
statusBarHeight = 0.0;
}
self.adViewTopMarginConstraint.constant = statusBarHeight;
self.webViewTopMarginConstraint.constant = 0.0;
// Get hold of the scrollview in the webview, and replace the scrollview delegate
for (UIView *subView in self.webView.subviews) {
if ([subView isKindOfClass:[UIScrollView class]]) {
_webViewScrollView = (UIScrollView *) subView;
_originalScrollViewDelegate = _webViewScrollView.delegate;
_webViewScrollView.delegate = self;
}
}
self.webViewScrollView.contentInset = UIEdgeInsetsMake(topViewHeight, 0.0, 0.0, 0.0);
// Load an url
NSURLRequest *requestObj = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.macrumors.com"]];
[self.webView loadRequest:requestObj];
}
#pragma mark UIGestureRecognizerDelegate impl
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
#pragma mark UIScrollViewDelegate impl
// We must propagate all scrollview delegate calls, because we don't really know what Apple's UIWebView
// implementation does with the callback, now and in the future
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewDidScroll:scrollView];
NSLog(@"webview contentoffset = %f, contentSize = %@", scrollView.contentOffset.y, NSStringFromCGSize(scrollView.contentSize));
if (scrollView.contentOffset.y >= statusBarHeight) {
// When the webview content has reached the top, we should always place the adView offscreen
self.adViewTopMarginConstraint.constant = -1.0 * topViewHeight;
} else if (scrollView.contentOffset.y <= -1.0 * topViewHeight) {
// The adView should never be moved further down than just below the status bar
self.adViewTopMarginConstraint.constant = statusBarHeight;
} else {
// Move the adView along with the webview scrolling
self.adViewTopMarginConstraint.constant = -1.0 * (topViewHeight + scrollView.contentOffset.y) + statusBarHeight;
NSLog(@"adview margin = %f", self.adViewTopMarginConstraint.constant);
}
}
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewDidZoom:scrollView];
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewWillBeginDragging:scrollView];
}
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
}
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewWillBeginDecelerating:scrollView];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewDidEndDecelerating:scrollView];
}
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewDidEndScrollingAnimation:scrollView];
}
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
if (scrollView == self.webView.scrollView) {
return [self.originalScrollViewDelegate viewForZoomingInScrollView:scrollView];
} else {
// If this controller is used as delegate for any other scrollview than the webview, you should return your zoomable view here
return nil;
}
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewWillBeginZooming:scrollView withView:view];
}
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewDidEndZooming:scrollView withView:view atScale:scale];
}
}
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
// We need to return YES for the webview (status bar tapping)
if (scrollView == self.webView.scrollView) {
return YES;
} else {
return NO;
}
}
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
if (scrollView == self.webView.scrollView) {
[self.originalScrollViewDelegate scrollViewDidScrollToTop:scrollView];
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment