スクロール追跡バナーを作ってみた

スマホに最適化されたWebサイトでよく見かけるスクロールについてくるあのバナーを、あえてネイティブで実装してみました。あくまで技術的なネタであってわざわざこういった実装をする目的はアレなので実際にAppStoreの審査でどうなるかは不明です。

方法としては、UIWebView(UIScrollView)とバナーのViewはそれぞれ独立させて配置し、スクロールをトラッキングするUIScrollViewDelegate#scrollViewDidScroll:(UIScrollView) で、scrollViewのoffsetを利用し、バナーのViewのoffsetを調整するようにします。

バナーを画面スクロールに追従させる

UIWebViewは全画面にADBannerViewは下部に配置します。


UIWebViewのscrollViewのdelegateを設定しハンドリングし、scrollViewのcontentOffset.yを調整するようにします。
これでUIWebViewのトラッキングについてbannerViewが移動するようになります。

    self.webView.scrollView.delegate = self;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGRect screen = [[UIScreen mainScreen] applicationFrame];
    CGFloat newOffsetY = screen.size.height - self.bannerView.frame.size.height - scrollView.contentOffset.y;
    self.bannerView.frame = CGRectMake(self.bannerView.frame.origin.x,
                                           newOffsetY,
                                           self.bannerView.frame.size.width,
                                           self.bannerView.frame.size.height);
}

この実装だけでは張り付いた状態のままなので、スクロールが止まった時点で初期位置に戻すようにします。

スクロール終了時に画面下部に移動させる

UIScrollViewDelegateのドラッグ終了、スクロール慣性の停止、タイトルバータップによるTOP移動アニメーション終了が分かれているので、それぞれで検知します。

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
     //ドラッグ終了
     if(!decelerate) {
        [self resetOffsetBannerView:scrollView];
     }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
     //スクロール慣性停止
    [self resetOffsetBannerView:scrollView];
}

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
     //TOP移動アニメーション終了
    [self resetOffsetBannerView:scrollView];
}

scrollViewDidEndDragging: willDecelerate: のdecelerateは慣性スクロールが働いているかのフラグです。ここでは慣性が働いていない時(decelerate=NO)に画面が止まったと判断して処理します。
これでWebサイトに乗っかってるかのようなあのいやらしいバナーの動きを再現できます(^^

サンプルコード

サンプルコードをgithubにアップしました。

https://github.com/hmori/AdChaseSample





scrollViewDidScroll: で移動するcontentOffsetの方向を判定すると、追跡するバナーの方向を上方向だけに限定することも可能ですが、ちょっとやり過ぎかもしれません。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (self.adjustOffset.y < scrollView.contentOffset.y) {
        CGFloat newOffsetY = [self offsetYBannerView] + self.adjustOffset.y - scrollView.contentOffset.y;
        self.bannerView.frame = CGRectMake(self.bannerView.frame.origin.x,
                                           newOffsetY,
                                           self.bannerView.frame.size.width,
                                           self.bannerView.frame.size.height);
    }
}

この方法は、標準アプリの「メモ」のような画面の上部にUISearchViewを配置しスクロールに合わせて上に隠れる仕掛けと多分同じです。