2012年3月14日水曜日

UIWebViewのズーム


UIWebViewはスクロールビューの描画完了後はユーザーのピンチ操作によってズームが可能ですが、プログラムで行うことも可能です。
UIWebViewは様々なファイルフォーマットを表示できますが、フォーマットによって動作に相違があります。

UIWebView

scalesPageToFit (property)
YESにセットすると初期表示時に横幅がUIWebViewのframeに収まるようにリサイズし、その倍率が1にセットされ、ユーザーがピンチ操作でリサイズできるようになる。
デフォルトはNOなので、ピンチ操作によるズームを可能にするためには、YESをセットします。

UIScrollViewのメソッドによるズーム
-setZoomScale:animated:
-zoomToRect:animated:

UIWebViewのscrollViewに上記メッセージを送ることでズームが行えます。

UIWebView *webView;
float scaleValue;
[webView.scrollView setZoomScale:scaleValue animated:YES];
または
CGRect rect = CGRectMake((float)x, (float)y, (float)width, (float)height);
[webView.scrollView zoomToRect:rect animated:YES];

PDFの場合はanimated=NOでもシャープな画像に再描画されますが、Excelの場合はanimated=NOだと拡大後画像がボケたままとなります。

基本的には上記の方法でズームできるのですが、UIWebViewのデータロードが非同期で行われるため、ロード直後(UIWebViewのデリゲートメソッドのwebViewDidFinishLoad:が呼ばれる時点)ではスクロールビューのcontentSizeがまだ初期値のままのため、ズームが有効になりません。

私はwebViewDidFinishLoad:でNSTimerで別メソッドを呼び、スクロールビューのcontentSizeが大きくなるのを待つようにしました。1ページだけのときはcontentSizeに変化がない(かもしれない)ことに注意してください。 

5 件のコメント:

  1. 現在、storyboardを使用し、オリジナルブラウザを作成しています。webViewDidFinishLoad:でNSTimerで別メソッドを呼び、何かの処理をさせているのでしょうか?1ページだけのときはcontentSizeに変化がないので、困っています。
    1ページだけだとデバイス回転時に変化がありません。何かいい方法があれば教えて頂きたいのですが。。。

    返信削除
  2. こんな方法で対処してました。待ち時間の数値を変えて試してみてください。
    1ページだけの場合も少し待っています。
    デバイス回転時についてはよくわかりませんが、やはり気にならない程度に待てばよさそうですが...
    @implementation MyWebView
    {
    int cnt;
    int numberOfPages;
    NSTimer *timerWaiting;
    }

    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
    .....
    numberOfPages= CGPDFDocumentGetNumberOfPages(pdf);
    float interval = 0.01; //<--OSのスケジューリングに依存するので、実際はもっと長くなる

    //この時点ではまだスクロールビューの描画が完了していないため、timerでさらに描画完了を待つ。
    if (timerWaiting == nil) {
    cnt = 0;
    timerWaiting = [NSTimer scheduledTimerWithTimeInterval:interval
    target:self
    selector:@selector(didFinishExpandingScrollView)
    userInfo:nil
    repeats:YES];
    }
    }

    //スクロールビューのframeが拡大したら描画完了とみなす。
    //frame拡大後も描画は完了していないので、ページ数に応じてもう少しまつ。
    //1ページだけの場合も数回ループする。
    - (void)didFinishExpandingScrollView
    {
    cnt++;
    int n1 = 5 + numberOfPages / 5;
    int n2 = n1 + 5; //frame拡大後も数回ループする
    if (cnt < n1) {
    if (numberOfPages == 1
    || self.scrollView.contentSize.height >= pageHeight * (numberOfPages - 1) * initScale) {
    cnt = n1;
    }
    } else if (cnt > n2) {
    [timerWaiting invalidate];
    timerWaiting = nil;
    [self setZoomAnimated:NO];
    }
    }

    返信削除
  3. @interface を書きませんでしたが、このクラスはUIWebViewのサブクラスになってます。
    @interface MyWebView : UIWebView
    多分selfがUIWebView以外のクラスのため、self.scrollViewでエラーになるのでしょう。
    selfをUIWebViewオブジェクトに置き換えてみてください。
    setZoomAnimated:メソッドは自分で作ったものです。適当なメソッドを作り、そこでズームを実行してください。
    このメソッドではスームインする領域の位置、サイズを計算して、zoomToRect:animated:を実行しています。
    別メソッドにしてあるのはロードとローテイトの両方から呼び出すためです。

    返信削除
  4. numberOfPages=1固定だと待ち時間も固定になってしまいます。
    ループ内で次のようなことをやってみたらいいかがでしょう。
    scroolViewのサイズが変更されるのを待つ。
    サイズが変更しないこともあるのでタイムアウト(50~100msec程度?)を設ける。
    サイズが変更されてもループを継続し、同じサイズが数回連続するのを待つ。
    サイズが安定したら描画完了とみなす。
    サイズが安定してもScrollViewのZoomが有効になるまでもう少し時間がかかるかもしれないので、同じサイズが連続する回数で調整する。もしかしたら、サイズによって待ち時間を長くする必要があるかもしれません。

    返信削除
  5. PDFの場合はwebViewDidFinishLoad:が呼ばれてもまだ描画が完了していなかったのですが、htmlの場合は完了しているようです。htmlしか対象にしないのであれば、Timerによる処理はなくてもズームが行えました。(どんなに大きなサイズでも大丈夫かどうかはわかりませんが...)

    返信削除