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

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

やり方

大雑把な手順としては

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

という3段階構成です。

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

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

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

で、これをjsでかくと

  document.documentElement.style.webkitTouchCallout = 'none';

です。つまり

[webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout = 'none';"];

とやれば長押ししてもきかなくなります

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

ここが少しめんどくさくて、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;
        };
    }
  }
})();

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

- (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];
    }
}

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

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


- (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;

}

_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>