テーブルビューを一番したまでスクロールさせると、勝手に数件分のデータを読み込み表示してくれる機能を実装したかった。
scrollViewDidScroll:
メソッドが想像以上に呼び出されるので、無駄な呼び出しをあの手この手で削ぎ落としていくのに頭を使った。
その部分に関してメモを残す。
やりたいこと(仕様)
-
UITableView
が下端までスクロールされたら(バウンス)、追加でn件分のデータを読み込んで表示したい。 - 読み込んだ後のセルの表示位置はそのままにしておき、テーブルビューの1行目に行ったりしない。
アプローチ
- UIScrollViewControllerのデリゲートメソッド
scrollViewDidScroll:
を使う - このメソッドは頻繁に呼びだされるのでバウンスされたときという条件を絞り込む
- バウンスされたときという条件下でも何度も呼び出されるので、実装を非同期実行・並列処理として不要な呼び出しをキャンセルする
サンプルコード
該当のビューコントローラにて
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
//バウンスの判定条件は、テーブルビューのサイズを超えて一番したまでスクロールされ、かつそれが ユーザの動作によって行われている場合とする
BOOL isBouncing = NO;
isBouncing = (self.tableView.contentOffset.y >= (self.tableView.contentSize.height - self.tableView.bounds.size.height)) & self.tableView.dragging;
if(isBouncing)
{
//どれか一つのスレッドが処理中なら、ほかのスレッドの処理は中断する。
if (self.isUpdating) { // isUpdating は このメソッドの処理が実行中かどうかを知らせる BOOL 値のプロパティ
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.isUpdating = YES;
/*
* 追加でn件分のデータを読み込む処理
*/
//処理が1秒続いたとする
[NSThread sleepForTimeInterval:1.0];
self.isUpdating = NO;
});
}
}
結果
上のコードを活用することで、やりたいことは実現できた。
その上で記載すべきこととしては2点。
1. EXC_BAD_ACCESS が出た
追加のデータを表示させた後に、このビューコントローラからひとつ前のビューコントローラに戻ると発生した。
[XCode]ARC使用中にエラー「EXC_BAD_ACCESS」「respondsToSelector」が出た場合の対処法」を参照させていただいたところ、テーブルビューのdelegate
を解放していないことが原因ということがわかった。そのため、dealloc
メソッドをオーバライドした。
該当のビューコントローラにて
- (void)dealloc
{
self.tableView.delegate = nil;
}
2. reloadDataの利用で表示はうまくいくか?
サンプルコードはないが、テーブルビューの更新はreloadData
を使っている。これを使うと一律でテーブルビューが常に1行目を表示してしまうようだと困ったのだが、そんなことはなかった。