UITableViewのスクロール位置を一番上に戻す

環境:iOS SDK 7.1

UITableView (UIScrollView のサブクラス)でスクロールの位置を一番上に戻すやり方。

方法1. UIScrollView のcontentOffsetを設定する

contentOffsetcontentInset.top分を差し引いたオフセット(iOS7なら-64px)を設定する

self.tableView.contentOffset = CGPointMake(0, -self.tableView.contentInset.top);

そもそもcontentOffsetとは、内容部の矩形の左上を0, 0として、そこから何ポイントずれたところが表示矩形の左上に現れるかを決めるプロパティのこと。

f:id:xyk:20140922193630p:plain

で、なぜcontentOffset-self.tableView.contentInset.topを設定しているかというとテーブルの表示時にコンテンツがステータスバーやナビゲーションバーの裏に隠れてしまわないようにコンテンツ上部部分に余白領域contentInset.topが自動的に追加されるから。
このcontentInset.topはステータスバー20pxとナビゲーションバー44pxを足した64pxになる。

以下の Apple のドキュメントにある図を見ると理解しやすい。

f:id:xyk:20140922193643p:plain

iOS Scroll View プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/UIScrollView_pg.pdf

ちなみにこのcontentOffsetの自動設定は UIViewController クラスのautomaticallyAdjustsScrollViewInsetsプロパティの設定で変更できる。デフォルトではYESになっている。

方法2. UIScrollView のscrollRectToVisible:animated:を使う
[self.tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];

scrollRectToVisible:animated:メソッドで指定の矩形領域を表示されるまでスクロールする。
CGRectZero を設定してしまうと動かない。

方法3. UITableView のscrollToRowAtIndexPath:atScrollPosition:animated:を使う
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];

セルを指定してスクロールさせるやり方。
1,2を書いてから気付いたけど UITableView に指定の場所にスクロールするためのメソッドが用意されていた。
UITableView ならこのやり方が一番わかりやすいかな。

UITableViewScrollPosition はその他にも以下のような enum で定義されている。

typedef NS_ENUM(NSInteger, UITableViewScrollPosition) {
    UITableViewScrollPositionNone,
    UITableViewScrollPositionTop,
    UITableViewScrollPositionMiddle,
    UITableViewScrollPositionBottom
};
※ 追記。一番下に移動する方法

方法1

int section = [self.tableView numberOfSections] - 1;
int row = [self.tableView numberOfRowsInSection:section] - 1;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.tableView scrollToRowAtIndexPath:indexPath
                      atScrollPosition:UITableViewScrollPositionBottom
                              animated:YES];

方法2

if (self.tableView.contentSize.height > self.tableView.frame.size.height
                                        - self.tableView.contentInset.top
                                        - self.tableView.contentInset.bottom) {
   CGPoint offset = CGPointMake(0, self.tableView.contentSize.height
                                - self.tableView.frame.size.height
                                + self.tableView.contentInset.bottom);
   [self.tableView setContentOffset:offset animated:YES];