武之新
UIWebViewでリンク長押し時のActionSheet

UIWebViewはデフォルトで、リンクを長押ししてるとActionSheetが出てきます。選択肢がOpenとCopyしかないので、実質あまり意味がなかったりしますが。
なのでこのActionSheetのボタンをアプリでカスタマイズしたい諸兄も多いことでしょう。

そんなときのTips。

まず問題は長押しされたことの検知と、長押しされたリンクのURLの取得。相変わらず、普通にやるとActionSheetが表示されたことすら検知することができません。GestureRecognizerを当てれば長押しされたことだけはわかりますが、それだとリンクURLを抜き出すことが不可能(だと思う)なので、UIWebViewの仕組みを使いたいところです。

私がやったチカラ押しな方法は次の通り。

  1. NSObjectのrespondsSelectorをmethod swizzling。
    置き換えたメソッドの中でtouchCalloutEnabledを監視させる。 touchCalloutEnabledでActionSheetを出すべきかどうかを判断させているようなので、これが呼ばれた=ActionSheetの出番、という判断にできる。
  2. UIActionSheetのshowInView:をmethod swizzling。touchCalloutEnabledが呼ばれた直後のshowInView:はUIWebViewが出そうとしているので、それを出さない(捨てて)、独自にActionSheetを表示する。
    長押しされたリンクのURLはActionSheetのtitleになっているので、それを使う。

というゴリ押しな方法で、とりあえず独自ActionSheetが出せる。
注意点はshowInView:でUIWebViewが表示しようとしたActionSheetを捨てるところ。なにもしないまま放っておくと、ActionSheetが閉じるのを待っているのか、UIWebViewが動かなくなる。仕方ないのでキャンセルボタンが押されたかのように

  • p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo} actionSheet:clickedButtonAtIndex:
  •    p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo} actionSheet:willDismissWithButtonIndex:
  •   p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo} actionSheet:didDismissWithButtonIndex: 

を呼び出してあげると忘れてくれます。
相当ごり押しな方法ですが、なんとか目的達成。もっとスマートな方法があれば誰か教えてください… 

ちなみに、ActionSheetを出さないのは

[webView stringByEvaluatingJavaScriptFromString:@”document.documentElement.style.webkitTouchCallout = ‘none’;”]

とやるだけで簡単にブロックできます。

  1. takemasa5の投稿です