LoginSignup
67

More than 5 years have passed since last update.

UIScrollView(横)に複数のUITableViewを配置してpagingする。

Last updated at Posted at 2013-02-03

UIScrollViewをpagingで横スクロールする仕様にしつつ、その上にUITableViewを複数配置してそれぞれを縦にスクロール可能にする方法です。

1. UIScrollViewのサブクラスを用意します。

PagingScrollView.m
#import <UIKit/UIKit.h>

@interface PagingScrollView : UIScrollView
@end

2. 大元のViewControllerへPagingScrollViewをAddして、その上にtableViewを並べる。

MasterViewController.m

#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をオーバーライドして対応します。

PagingScrollView.m
- (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をオーバーライドする場合は以下のようにする場合が多いようです。

hitTestOverrideView.m

- (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を使うという手も。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
67