UIScrollViewをpagingで横スクロールする仕様にしつつ、その上にUITableViewを複数配置してそれぞれを縦にスクロール可能にする方法です。
1. UIScrollViewのサブクラスを用意します。
#import <UIKit/UIKit.h>
@interface PagingScrollView : UIScrollView
@end
2. 大元のViewControllerへPagingScrollViewをAddして、その上にtableViewを並べる。
#define TABLE_WIDTH 106.f
- (void)viewDidAppear:(BOOL)animated {
int numberOfTables = 5;
CGFloat height = self.view.bounds.size.height;
CGRect tableBounds = CGRectMake(0.0f, 50.f, TABLE_WIDTH, height);
PagingScrollView *scrollView = [[PagingScrollView alloc] initWithFrame:self.view.bounds];
scrollView.contentSize = CGSizeMake(TABLE_WIDTH * numberOfTables, height);
scrollView.pagingEnabled = YES;
scrollView.bounds = tableBounds; // scrollViewのページングをTABLE_WIDTH単位に。
scrollView.clipsToBounds = NO; // 非表示になっているtableBounds外を表示。
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];
// 5つのtableViewを横に並べる
CGRect tableFrame = tableBounds;
tableFrame.origin.x = 0.f;
for (int i = 0; i < numberOfTables; i++) {
UITableView *tableView = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStylePlain];
switch (i) {
case 0:
tableView.backgroundColor = [UIColor yellowColor];
break;
case 1:
tableView.backgroundColor = [UIColor blueColor];
break;
case 2:
tableView.backgroundColor = [UIColor brownColor];
break;
case 3:
tableView.backgroundColor = [UIColor greenColor];
break;
case 4:
tableView.backgroundColor = [UIColor cyanColor];
break;
default:
break;
}
[scrollView addSubview:tableView];
tableFrame.origin.x += TABLE_WIDTH;
}
}
※ テスト用として分かりやすいようにbackgroundColorを付けています。
3. ScrollViewのタッチ検出範囲を拡張する。
2の状態ではscrollView.boundsで指定した領域(赤い枠)外のタッチイベントに反応出来ないため、PagingScrollViewのhitTestをオーバーライドして対応します。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView *subview in self.subviews) {
if (CGRectContainsPoint(subview.frame, point))
return subview;
}
return self;
}
上のように[(UIView*)hitTest:withEvent:]をオーバーライドすることで、scrollView.bounds外のタッチイベントも受け取れるようになります。
第一引数のpointはtouchPointですので、その位置にサブビュー(この場合はtableView)があればそれを返してあげることでレスボンダチェーンをtableViewに返すことが出来ます。
通常、hitTestをオーバーライドする場合は以下のようにする場合が多いようです。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self)
return anotherView;
return hitView;
}
ですが、今回はscrollViewが全画面表示 + bounds指定されていることが前提ですので[super hitTest:...]を返してしまうとbounds外へのタップ時はhitViewがnilになり、レスポンダは最下部のmasterViewController.viewに移ってしまいます。
そこで前述のように、touchPoint上にtableViewがない場合はself(PagingScrollView)を返してあげることで、tableViewが配置されていない場所へのtouchでも横スクロールが可能になります。
補足
以上の形で指定サイズのpagingとtableViewの縦スクロールは可能になります。
ですが、UIScrollViewに一度にたくさんのUITableViewをAddすることはメモリの圧迫に繋がる可能性が高いです。
それを防ぐためには、UITableViewが持つ『Cellの再利用によってメモリ使用量を抑える仕様』をPagingScrollViewでも独自に実装する必要がありますのでご注意を!
また別の実装として、下地のScrollViewを90度回転させた横向きのUITableViewにして、そのcellに-90度回転させた縦向きのUITableViewをAddする。という方法もあります。
この場合は独自のリユース処理を書かなくても良くなるので楽になります。
また、iOS6以降であればUICollectionViewを使うという手も。