UIWebViewを長押しすると下からニュっとアクションシートが出てくるわけですが、これの中身を変えたくなりました。
ネット上で探したところ・・・・これとか解決策っぽいのはあったんですが、なんかもうやること多いし、UIWindowのサブクラスを作らなきゃいけないし、英語だし、なんか嫌だったので、もっと簡単な方法でハックしてみました。

ちょっと精度が悪かったり、反応しない時があったりとポンコツ気味ですが、簡易的に作るならこれぐらいがいいなーって感じです。

やり方

大雑把な手順としては

  1. 本来のリンク長押しの機能をオフにする
  2. jsでリンク長押しを検知してobj-cの方に教えてあげる
  3. 普通にアクションシートを出してあげる

という3段階構成です。

「本来のリンク長押しの機能をオフにする」と「jsでリンク長押しを検知してobj-cの方に教えてあげる」はどっちもjsなので、
具体的にはとあるjsを読み込んで、通知が来たらアクションシート出すだけというお手軽構成です。

本来のリンク長押しの機能をオフにする

css(style)で–webkitTouchCalloutをnone指定にしてあげるとでなくなります。
こういうのはSafari Web Content Guideに書いてあるっぽいです。

で、これをjsでかくと
[js]
document.documentElement.style.webkitTouchCallout = ‘none’;
[/js]
です。つまり
[objc]
[webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout = 'none';"];
[/objc]
とやれば長押ししてもきかなくなります

jsでリンク長押しを検知してobj-cの方に教えてあげる

ここが少しめんどくさくて、jsのタイマー使ってリンクの長押しを検知しーとかやらないといけないです。
といっても、中身がわからなくてもいいから使えればいいんだ!っていうなら簡単で、次のjsをロードすればいいだけです。
[js]
var NVTimer = {
currentUrl : null,
currentTitle : null,
start : function(url, title){
NVTimer.currentUrl = url;
NVTimer.currentTitle = title;
setTimeout("NVTimer.finish(‘" + url + "’)",750);
},
cancel : function(url){
NVTimer.currentUrl = null;
NVTimer.currentTitle = null;
},
finish : function(url){
if(NVTimer.currentUrl === url){
document.location = "nv://taphold";
}
}
};

(function(){

var elements = document.getElementsByTagName("a");
var length = elements.length;
var i = 0;
for(i = 0; i< length; i++){
if(elements[i].href !== undefined ){
elements[i].onselectstart = function(){
return false;
};

elements[i].ontouchstart=function(){
NVTimer.start(this.toString(), this.innerHTML);
return true;
};
elements[i].ontouchcancel=function(){
NVTimer.cancel(this.toString());
return true;
};
elements[i].ontouchend=function(){
NVTimer.cancel(this.toString());
return true;
};
elements[i].ontouchmove=function(){
NVTimer.cancel(this.toString());
return true;
};
}
}
})();

[/js]

これをロードすれば、リンクを長押しした時に「nv://taphold」といったURLに移動しようとするので、これをobj-c側で検知してあげます。
ロードするときは、最初の「本来のリンク長押しの機能をオフにする」の方のjsと一つにまとめて、xxx.jsって形でファイルに保存して
ページのロード完了時に読み込むのがおすすめです。
※jsファイルをそのままプロジェクトに追加しただけだとリソースファイル扱いになってないので注意

[objc]
- (void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *nvtimer = [_webView stringByEvaluatingJavaScriptFromString:@"typeof NVTimer"] ;
if([nvtimer isEqualToString:@"undefined"]){
NSString *touchhold = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"touchhold" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:touchhold];
[touchhold release];
}
}
[/objc]

普通にアクションシートを出してあげる

これはもう簡単で、こんな感じ。
[objc]

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
if([request.URL.scheme isEqualToString:@"nv"] && [request.URL.host isEqualToString:@"taphold"] ){
[_actionSheetTmpUrl release];
[_actionSheetTmpTitle release];
_actionSheetTmpUrl = [[_webView stringByEvaluatingJavaScriptFromString:@"NVTimer.currentUrl"]retain];
_actionSheetTmpTitle = [[_webView stringByEvaluatingJavaScriptFromString:@"NVTimer.currentTitle"]retain];
NSString *title = [NSString stringWithFormat:@"\"%@\"\n%@",_actionSheetTmpTitle,_actionSheetTmpUrl];
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:title
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"リンク先を開く",@"リンクをコピー",nil];
[sheet showInView:[UIApplication sharedApplication].keyWindow];
[sheet release];
return NO;
}
return YES;

}

[/objc]
_actionSheetTmpTitle と _actionSheetTmpUrl にそれぞれリンクのテキストとURLを保存しているので、アクションシートからのdelegateがきたらこれの情報を元に処理してあげればOK!



というわけで、簡易的なリンク長押しハックでした。
精度的には(人によるけど)50%ぐらいで、モックアップ程度ならこれでいいかな〜という感じです。
売り物だとちょっと精度が低い。。。かもしれません。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>