【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.m
のviewDidLoad
メソッドを以下のようにすればいいと思います。
- (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プロパティを書き換えます。
なんかviewDidLoad
やwebViewDidStartLoad
では一番最初だけうまくタイトルを挿入してくれなかったので、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; } }
というとで、これで最低限の機能を持ったアプリ内ブラウザを実装できたかなと思います。