はりまや日記

なんか適当にいろいろ綴ったりなんかするところ

iphoneアプリ開発日記その8

webView にスクロールボタンをつけよう

その動機

最近スマホを使っていると、指が痛くなることがある。
果たして手の油が少ないのか皮膚が弱いのか

まあそんなこんなで、ボタンタップでスクロールした方が、
指に負担が少なかろうと考えました。


実現へのアプローチ

さて、ボタンスクロールをどうやって実現するのだろう。
当初は webview にそれっぽい API があるんじゃないかなーと考えていました。

実際調べてみると、どうも API なんぞ無い模様。
ただし、webview から javascript が実行できるようで、それを使ってスクロールが出来るようでした。
さらに調べてみると、webview で jquery を使えるらしい。
面白そうなんでこれにしよう


jquery を読み込む

まず、jquery 本体をダウンロード→ここ
バージョンは 1.10.1 だった。

次にここを参考にして、プロジェクトにjquery を登録

これで jquery が使えるようになった。

一応動作確認をしておく
以下のコードを、適当なボタンとかに設定して実行する

    // load jquery
    NSString *path = [[NSBundle mainBundle] pathForResource:@"jquery-1.10.1" ofType:@"js"];
    NSString* jqueryMain = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    [_webView stringByEvaluatingJavaScriptFromString:jqueryMain];

    NSString *script = @"alert($().jquery);";
    [_webView stringByEvaluatingJavaScriptFromString:script];

結果
f:id:harrymaya:20130613042805p:plain

ちゃんと動いているようだ。


jquery のロードタイミング

さて、ひとまず jquery が動いたわけだが、jquery の本体を毎回読み込む必要はないわけで。
どこかで一回読み込めばよいだろう。


とりあえず、viewDidLoad で読み込んでみる。
… あれ? 動かない。
なんだろう。スコープの関係だろうか。


jquery 本体のコードを static にしてみた
→ 動かない。


うーむ、毎回呼ばないとだめなのだろうか。
試しに、ボタンを複数用意して、片方だけに jquery 読み込み処理を書いてみた
jquery 読み込み処理を書いたボタンを押したあとなら、書いてない方も動く


ふむ、一度読み込んでしまえばOK なわけか。
ということは、読み込むタイミングが悪いということだな。

そうなると、読み込ませるタイミングは URL 読み込みが終わってからかなー
このへんか → webViewDidFinishLoad

試してみる


@interface WebViewController ()
@property NSString* jqueryMain;
@end

- (void)viewDidLoad
{
    [super viewDidLoad];

    // いろいろ初期化処理

    // load jquery
    NSString *path = [[NSBundle mainBundle] pathForResource:@"jquery-1.10.1" ofType:@"js"];
    self.jqueryMain = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    
    // load jquery
    [_webView stringByEvaluatingJavaScriptFromString:self.jqueryMain];
       
}

これで一度だけ読み込めばよくなった。


スクロール機能の実装

ここまで来たら、あとは jquery を使うだけである。
だが、jquery というか javascript はほとんどやってないので、これも調べながらになる。

このへん を参考に、どうするか考える。

見たところ、scrollTop で指定した位置に移動するようだ。
あとは以下の関数を組み合わせればよさげ

$('body').offset().top ページ全体の先頭位置
$('body').height() ページ全体の高さ
$(window).scrollTop() ウィンドウの先頭位置
$(window).height() ウィンドウの高さ

考え方としては、こんな感じ
「ページ全体の先頭位置 + 全体の高さ 」に移動すれば、一番下まで移動できる。
「ウィンドウの先頭位置 +ー ウィンドウの高さ」で移動すれば、上下に1画面分移動する

実装してみる

- (IBAction)pressUpward:(id)sender {
    NSString *script = @"$('body').animate({scrollTop:$(window).scrollTop() - $(window).height()});";
    [self execJQuery:script];
    
}

- (IBAction)pressDownward:(id)sender {

    NSString *script = @"$('body').animate({scrollTop:$(window).scrollTop() + $(window).height()});";
    [self execJQuery:script];
    
}

- (IBAction)pressPageEnd:(id)sender {
    NSString *script = @"$('body').animate({scrollTop:$('body').offset().top+$('body').height()}, 'fast')";
    [self execJQuery:script];
}

これでページスクロール機能が実装できました。
本当は、ページの上下限を考えて、オーバーするようなら0とか最大値を返そうと思っていたのですが、
特に対応しなくても問題なさそうでした。
webkitjquery 側でちゃんとやってくれているみたい。


scrollTo のセレクタについて

参考にしたページに書いてあったように、本来 scrollTo メソッドを使うときにはセレクタを指定します。
で、それを取得するのに $.browser.webkit みたいな関数? を使うのですが、こいつは jquery 1.9 で廃止されたとのこと →ここ

プラグインを入れれば使えるようですが、今回は ios オンリーなので 'body' ベタ書きで対応しときました。
クロスプラットフォームで使う気ないんでいいかなー