UIScrollView 上では UIViewController 上のボタンイベントを拾えない

電子書籍のおおまかな構造設計をしている途中なのですが、どうも腑に落ちない現象に出会ったのでメモ的に。
結論から言うと、タイトル通り、UIScrollView 上にある UIViewController のイベントは、手軽には拾えません…という話です。

■設計要件

まずは、次のように設計を考えました。

・ページ捲りの部分は、UIScrollView を使う。
 → スライドさせたいので、ページを横に並べてスクロールさせるとよい。
 → ページ捲りのアニメーションよりも、手軽に作れる、と思う。
 
・ページ単位は、View で作りたい。
 → 手軽にページを構成したいので、ページ単位で作りたい。
 → これは UIViewController にボタンなどを乗せるのがよいかなと。
 
・ページ間で共通のボタンがある。
 → ナビゲートボタンのように、ページ間で共通の場所にボタンがある。
 → 自動生成をするか、スクロールさせても移動させない、のどちらか。
 
最終的には、スクリプトを書いてページを構成するための objective-c のソースコードを出力させたいわけです。

■クラス構造

これを踏まえてクラス構造を考えて、素直に以下の構造にしました。

UIApplication
+ MainViewController : 最初の View
 + UIView
  + UIScrollView
   + UIViewController
    + UIView
     + Button : ボタン
     + Movie : 動画
     + Image : 背景画像 

なんか、ややこしそうに見えますが、

・MainView の上に ScrollView が乗っかる。
・ScrollView の上にページ単位となる ViewController が乗っかる。
・ページ単位の ViewController の上に Button などが乗っかる。

という感じです。

■Button のイベントは、ViewController で取れる筈

そんなわけで、Button を貼り付けて、UIViewScroller で取ろうとしたのですが、なぜか飛ばない。

UIApplication
+ MainViewController 
 + UIView
  + UIScrollView
   + UIViewController ←ここに飛んで欲しい
    + UIView
     + Button : ボタンのイベント
     + Movie 
     + Image  

試しに、Main の ViewController の上に Button を置くとうまくイベントを拾えます。

UIApplication
+ MainViewController : ←ここで拾える
 + UIView
  + Button : このイベントは
  + UIScrollView
   + UIViewController
    + UIView
     + Button 
     + Movie 
     + Image  

同じコードを打っているはずなのに何故に???ってことで調べていくと、どうやら、ScrollView のほうが先にタッチイベントを取ってしまうので、その上に乗っているコントロールにはイベントがいきわたらないのですね。なるほど。

# ボタンクリックのイベントは UIControlEventTouchUpInside で取得すればよいのですが、ScrollView のスクロールのために取られてしまうという現象。

■解決にはどうすればいいのか?

UIScrollView をサブクラス化して、なんらかの Touch イベントを拾えば解決できそうなんですが、ScrollView 内に複数の ViewController が乗ることになるので、これにはひと工夫必要そう。

なので、まだ未解決。まあ、ボタンなんかは MainViewController に貼り付けておいて、Main のほうでイベントを拾うってのが逃れ方なのでしょうけど、あまりスマートではないし。なにしろ Page 単位の ViewController が複数あるので、ちょっと考えないと、というところです。

カテゴリー: 開発, iPad パーマリンク

UIScrollView 上では UIViewController 上のボタンイベントを拾えない への6件のフィードバック

  1. masuda のコメント:

    UIScrollView のタッチイベントを取得する – happy lie, happy life
    http://d.hatena.ne.jp/spitfire_tree/20100707/1278470269

    タッチイベントは、こうやると取れるらしいんだが、これをそれぞれの ViewController へ振り分ける(最終的には、Button などの該当先の selector に振り分ける)方法がないものか。

  2. masuda のコメント:

    ちなみに、回避方法として、UIScrollView の上に Button を置くと期待通り反応します。
    UIScrollView -> UIViewController -> Button のように、UIScrollView の上に UIViewController を置くと touch イベントを UIScrollView に取られてしまう、という現象です。
    なので、擬似的に UIScrollView -> Button のように置きます。

    ただ、これって、UIViewController が多くなると破綻する気が、ってなわけ。

  3. johney のコメント:

    こんにちは。

    参考にさせていただいたおります。

    同じような実装で悩んでいるのですが、
    masudaさんは最終的にどうされましたか?

    私も同じように、UIScrollViewの上にUIViewControllerを置いてボタン等を管理使用と思っていたのですが、んまてよという感じなのです。。。。
    (対象のViewの表示領域が限らているので(iPad要件で300×300が使用できる表示領域。ただし、表示したいものは300×600なのです)、Button郡をControllerで管理してUIScrollView上にのせようかなと思ってましたが。。。)

    • masuda のコメント:

      最終的には…ちょっとそのままになってしまって、最終的に実装はしていないんですよね。申し訳ないです。
      先の例では、UIScrollView の上に複数枚のUIViewControllerを置く例なので、johney のような場合は、UIScrollView の上にひとつのUIViewControllerを置く形で良いんじゃないでしょうか?UIScrollView でイベントを拾ったものを、そのまま UIViewController に送るという形でどうでしょうか?
      複数の UIViewController 場合には、位置によって振り分けないといけないので、結構ややこしいのですが。

  4. 字引 淳 のコメント:

    うちは、オブジェクトに対する考え方を180度変えて、座標に依存する方法で対応しました。見た目、ほぼ同じ動作です。仮ですが。

    あなたの記事の問題は現在も悩み中です。

    • masuda のコメント:

      確かに、座標で決めたほうが良さそうです。Windows アプリの場合ウィンドウハンドルが基点となっているのでイベント絡みの取得がコントロール単位で制御できるので楽だったのですが、iOS の場合は違うみたいですね。が、適当にコントロール化してやれば、外部的にも同じように使えると思うので。

      この記事を書いたときに、UIScrollView を使うとページ制御は楽なのですが、画像容量などを考えると十数ページ分が限界です。それ以上になるとアプリ自体が 30MB とか大きくなっているので手軽にダウンロードができないってことになります。雑誌タイプはそれでもいいのですが、小説タイプのように百ページぐらいになる場合は、別な方法が必要ですよね~、と未だ思案中だったりします。

コメントは停止中です。