【iOS】アプリ内ブラウザを実装する

以下を参考にさせていただきました。

http://otiai10.hatenablog.com/entry/2013/12/24/234917

手順

Storyboardで、

  • ViewControllerを追加

  • WebViewをその上に追加

そしたら、

  • 新しいCocoaTcouchのクラスファイルを作成します

  • InternalWebViewController.hでさっきおいたUIWebViewとプログラムを結びつけます

以下の@propetyのとこの行のようになります。

#import <UIKit/UIKit.h>

@interface InternalWebViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIWebView *webView;

@end
  • HTTPリクエストを送る設定をする

リクエストを送って、UIWebViewに写す処理を書きます。

ViewController.mviewDidLoadメソッドを以下のようにすればいいと思います。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString* urlString = @"https://google.com";
    NSURL* anytimeURL = [NSURL URLWithString:urlString];
    NSURLRequest* myRequest = [NSURLRequest requestWithURL:anytimeURL];
    [self.webView loadRequest:myRequest];
}

modalで画面遷移

ViewControllerにxcodeからStoryborad IDをつけてあげます。

そしたら~メソッドでそちらに移動することができます。

そして詰まったのが、tagetの属性値を_blankにしてしまっているとUIWebViewのsafariでは無効化されてしまうということ。

http://niw.at/articles/2009/02/06/how-to-enable-the-popup-window-on-uiwebview/ja

JavaScriptで、target属性がblankのものを全てtaget属性のデフォルト値であるselfに変えてしまえばいけるんなじゃなかろーか。

ということでやってみる。

JavaScriptを以下のように書きました。

var links = document.getElementsByTagName("a");
for(var i=0; i<links.length; i++) {
    var link = links[i];
    var t = link.getAttribute("target");
    if (t === "_blank") {
        link.setAttribute("target", "_self");
    }
}

これをstringByEvaluatingJavaScriptFromStringメソッドの引数で打ち込めばokです。新しくリクエストを送ってページが読まれるたびにこいつを実行してtarget属性を調べて書き換える、ということをやればいいかなと思った。

コードは以下。

- (void)webViewDidStartLoad:(UIWebView *)webView{
    // UIWebViewではtarget属性値が_blankの場合挙動が変わってしまうので
    // 読み込み時にデフォルト値である_selfに書き換える
    NSString *js_blank_to_self =
       @"var links = document.getElementsByTagName('a');"
        "for(var i=0; i<links.length; i++){"
            "var link = links[i];"
            "var t = link.getAttribute('target');"
            "if (target='_blank') {"
               "link.setAttribute('target', '_self');"
            "}"
        "}";
    [webView stringByEvaluatingJavaScriptFromString:js_blank_to_self];
}

どうやらObjective-CにはRubyでいうヒアドキュメント的な文字列の定義の仕方はなくて、文字列を改行しまくるというちょっと良くわからない方法があるっぽかったので、JSのプログラムの可読性を上げるためにそっちを使いました。

画面を調整

ページの表示がなんかダサくなってたので調整します。

これはAutoLayoutを設定すればいけました。

閉じるボタン

modalなら以下で閉じてくれるので、buttonのIBActionのメソッドなどの中で呼び出せばok。

[self dismissModalViewControllerAnimated:YES];

タイトルを表示する

そのページのタイトルをNavigationBarのところに表示する設定をします。

.hで以下のようにnavigationBarとつなげます。

@property (weak, nonatomic) IBOutlet UINavigationItem *navigationItem;

そしたら.mで以下のようにjsを実行してページのタイトルを取得してから、navigationItemのtitleプロパティを書き換えます。

なんかviewDidLoadwebViewDidStartLoadでは一番最初だけうまくタイトルを挿入してくれなかったので、webViewDidFinishLoadメソッドで呼ぶようにしました。

@property (weak, nonatomic) IBOutlet UINavigationItem *navigationItem;

戻る/進むボタン

一応内部ブラウザなのであと最低限戻る、進む、閉じるボタンを実装してきます。

以下、akioさんの記事を参考にさせていただき、navigationBarに戻る/進むボタンを置いていきます。

http://akio0911.net/archives/27557

Buttonの色も指定します。

http://stackoverflow.com/questions/664930/uibarbuttonitem-with-color

最終的にボタンの生成部分はこんな感じになりました。

    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:self action:@selector(pressBackButton:)];
    [backButton setTintColor:[UIColor grayColor]];
    UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFastForward target:self action:@selector(pressNextButton:)];
    [nextButton setTintColor:[UIColor grayColor]];
    self.navigationItem.rightBarButtonItems = @[nextButton, backButton];
    // AppDelegateからViewControllerを参照できるようにする
    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    
    appDelegate.backButton = backButton;
    appDelegate.nextButton = nextButton;
    
    backButton.enabled = NO;
    nextButton.enabled = NO;
}

前/次の遷移先がない場合はボタンを無効化する。

http://stackoverflow.com/questions/3784112/how-to-disable-uibarbuttonitem

こんな感じで読み込みのたびに確認して切り替えるようにしました。

- (void)webViewDidStartLoad:(UIWebView *)webView{
    // AppDelegateからViewControllerを参照できるようにする
    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    // 戻る/進むボタンの有効・無効を切り替える
    if (webView.canGoBack) {
        appDelegate.backButton.enabled = YES;
    }else {
        appDelegate.backButton.enabled = NO;
    }
    if (webView.canGoForward) {
        appDelegate.nextButton.enabled = YES;
    }else {
        appDelegate.nextButton.enabled = NO;
    }
}

というとで、これで最低限の機能を持ったアプリ内ブラウザを実装できたかなと思います。