UIWebView内に表示されたリンクをクリックした場合に、UIWebView内で表示する代わりに、Safariを起動して表示する方法を紹介します。
ソースコード例
Empty ApplicationにUIWebViewを設置し、その中に外部リンク(Yahoo.co.jpへのリンク)を表示する例。httpから始まるリンクは、UIView内で表示するのではなく、Safariを起動して表示する。webView.delegate = selfの部分で警告が表示されるので、インターフェースにUIWebViewDelegateを追加しておく。
- (BOOL)application
:(UIApplication
*)application
didFinishLaunchingWithOptions
:(NSDictionary *)launchOptions
{
self.window
= [[UIWindow alloc
] initWithFrame
:[[UIScreen mainScreen
] bounds
]];
// Override point for customization after application launch.
self.window.backgroundColor
= [UIColor whiteColor
];
UIWebView
* webView
= [[UIWebView alloc
] initWithFrame
:CGRectMake
(0,
20,
320,
460)];
[webView loadHTMLString
:@"<a href='http://www.yahoo.co.jp'>Yahoo</a>" baseURL
:nil];
webView.delegate
= self;
[self.window addSubview
:webView
];
[self.window makeKeyAndVisible
];
return YES;
}
- (BOOL)webView
:(UIWebView
*)webView
shouldStartLoadWithRequest
:(NSURLRequest *)request
navigationType
:(UIWebViewNavigationType
)navigationType
{
if (navigationType
== UIWebViewNavigationTypeLinkClicked
|| navigationType
== UIWebViewNavigationTypeOther
)
{
NSString* scheme
= [[request URL
] scheme
];
if([scheme compare
:@"http"] == NSOrderedSame
) {
[[UIApplication sharedApplication
] openURL
: [request URL
]];
return NO;
}
}
return YES;
}
前提知識
iPhoneアプリ内にWeb(HTML)を表示するには、UIWebViewを使います。そして、UIWebViewの中のJavaScriptから、Objective-Cにデータを渡す場合は、location.hrefの値を変更します。UIWebView発生した画面遷移イベントをどうするかは、UIWebViewのdelegate先が判定します。data://で始まるなどといったルールを満たしているURLにアクセスした場合にそれをトラッピングすることで、JavaScriptからのメッセージをobjective-cに渡します。
JavaScript
function sendDataToObjc(data){
location.href=”data://” + data;
}
Objective-c
UIWebViewを継承したクラスの一部を抜粋
- (id)init
{
self
= [super init
];
if (self
) {
self.delegate
= self;
}
return self;
}
-(BOOL)webView
:(UIWebView
*)webView shouldStartLoadWithRequest
:(NSURLRequest *)request navigationType
:(UIWebViewNavigationType
)navigationType
{
NSString * urlStr
= [[request URL
] absoluteString
];
if([urlStr hasPrefix
:@"data://"]){
NSString * data
= [urlStr substringFromIndex
:7];
NSLog
(@"%@", data
);
return NO;
}else{
return YES;
}
}
<h2>問題<
/h2>
以下の
2行を実行すると、
1つめのデータは無視されhigehigeだけが渡される。<font color
="#CCC">おそらく画面遷移イベントが発生するまえにURLが上書きされてしまうのだろう。Webブラウザの動きとしては妥当だけれど・・・・<
/font>
[cc lang
="JavaScript"]
sendDataToObjc
("hogehoge");
sendDataToObjc
("higehige");
試してみた
location.href固有の問題かなーと思い、以下のように書き換えてみるものの、最後のデータしか渡されない状況は変わらず。。だめやん。おわった。諦めかけた。
<form id="form" method="post"></form>
<script>
function sendDataToObjc(data){
document.getElementById("form").action="data://" + data;
document.getElementById("form").submit();
}
</script>
閃いた
全部おくればえーやん。途中のデータ送信は飛んじゃうけど、最後のが必ず送信されるならこれで問題ないはず。少々汚い方法だけどこの方法なら問題ないはず。実際、問題なかった。
function sendDataToObjc(data){
location.href+= ”//:data://” + data;
}
この方法の欠点
受け側(Objective-C)の処理が増える
→1箇所実装すればおk。そんなに大変じゃない。
渡すデータの量が増える。
→今回の問題が発生しているアプリだとせいぜい数千バイト程度にしかならないので、気にすることはなさそう。
まとめ
iPhoneアプリでUIWebView内のJavaScriptからObjective-Cにデータを渡すときにlocation.hrefを使う方法があるが、location.hrefに連続で値を代入した場合に最後に代入した値しか送信されない。代入する代わりに、どんどん連接してしまえば、なんとかなる。
WebViewで開く対象は以下の3種類ある。
- Web上のコンテンツ
- アプリ内の静的なファイル
- アプリで動的に作成したデータ
インターネット上のWebページを開く
マニュアルや製品情報サイトを表示する場合に使えそうです。
[webView loadRequest
:
[[NSURLRequest alloc
] initWithURL
:
[NSURL URLWithString
:@"http://www.yahoo.co.jp"]]];
アプリ内の静的なファイルを開く
マニュアルをHTMLで作成した場合に便利。ディレクトリ階層のあるコンテンツをインポートするときは、フォルダ階層を維持してインポートしないとリンクが切れるので注意。”Create folder references for any added folders”を選べばよい。
[webView loadRequest
:
[NSURLRequest requestWithURL
:
[NSURL fileURLWithPath
:
[[NSBundle mainBundle
] pathForResource
:@"api" ofType
:@"html"]]]];
表示内容をObjective-Cで指定する
表示内容を動的に変化させる場合に便利です。baseURLはHTML内で相対パス指定を行ったときに計算元として使われるPathです。
[webView loadHTMLString:@"<h1>Hello</h1>" baseURL:nil];
↓参考になります
UIWebViewでloadHTMLStringするときの要点まとめ
iPhoneアプリ開発に関する情報を公開中 ソースコードもあるよ。