Twitterのアプリなどが採用していた引っ張って更新するためのコントロールは、UIRefreshControl
としてiOS6からiOS SDKに正式採用されています。ところが、このUIRefreshControl
は下へ引っ張って更新するのには対応していますが、上へ引っ張って更新するのには対応していません。
上へ引っ張って更新が欲しくなるのは、コンテンツが上から下に並んでいて、追加で読み込むコンテンツを一番下に追加したいような場合です。例えば2chビューワーなんかがそうですよね。
上へ引っ張って更新する操作には標準のUIRefreshControl
が使えないので、自前で実装するかライブラリを使用するしか方法はありません。
自前で実装する
実はこれは試していません。先にライブラリ探しから始めてしまったので!
ざっと調べたところでは、UITableView
のfooterView
を使うとかなんとか、そんなような感じでした。スクロールを検知して追加コンテンツを読み込み「引っ張って更新」のViewの位置を再調整それば良さそうでした。
ライブラリを使う
デファクトスタンダードなライブラリがどれかわかりませんでした。もしそういうのがあったら教えて欲しいです。
僕が使ったのはこれ。
pilot34/MNMBottomPullToRefresh
MNMBottomPullToRefreshの導入
READMEにはありませんが、CocoaPodsで導入可能です。
# Podfile
pod 'MNMBottomPullToRefresh'
$ pod install
使い方
UITableViewを置いたUIViewControllerを例に実装を紹介。
ViewController.h
でMNMBottomPullToRefreshManagerClient
を継承します。
#import <MNMBottomPullToRefreshManager.h>
@interface ViewController: UIViewController<MNMBottomPullToRefreshManagerClient, UITableViewDelegate, UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@end
ViewController.m
ではちょっとした実装と、MNMBottomPullToRefreshManagerClient
のメソッドの定義を行います。
ViewController
のプロパティにMNMBottomPullToRefreshManager
を持つscrollViewDidScroll
scrollViewDidEndDragging
bottomPullToRefreshTriggered
@interface ViewController ()
@property (nonatomic, strong) MNMBottomPullToRefreshManager* refreshManager;
@end
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
// ここ
self.refreshManager = [[MNMBottomPullToRefreshManager alloc] initWithPullToRefreshViewHeight:60.0 tableView:self.tableView withClient:self];
}
# pragma mark - MNMBottomPullToRefreshManager
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self.refreshManager tableViewScrolled];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
[self.refreshManager tableViewReleased];
}
- (void)bottomPullToRefreshTriggered:(MNMBottomPullToRefreshManager *)manager {
[self performSelector:@selector(refresh) withObject:nil afterDelay:0.3f];
}
最後のbottomPullToRefreshTriggered
で更新処理を実行します。ここではperformSelector: withObject: afterDelay:
を用いて非同期実行しています。確かActivityIndicatorと同じく、そうしておかないと動かなかったような…。
そして更新メソッドの最後でself.refreshManager
に更新終了を通知します。
- (void)refresh
{
// データ更新
// 更新後に呼び出す
[self.refreshManager tableViewReloadFinished];
}
このライブラリにはもうひとつ注意があって、引っ張って更新するためのViewをTableの一番下に相当する位置に配置しているのですが、行が増えるたびに再配置し直さないとおかしな場所に配置されてしまうことになります。
そこでviewDidLayoutSubviews
をオーバーライドします。
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self.refreshManager relocatePullToRefreshView];
}
また場合によってはviewDidLayoutSubviews
が呼ばれないケースがありました(どのようなケースで起こるのかはわかりっていません)。そういう場合はデータ更新後にlayoutIfNeeded
を呼んであげることで再レイアウトさせることができるようです。
- (void)refresh
{
// データ更新 ...
// 再レイアウト
[self.view layoutIfNeeded];
// 更新後に呼び出す
[self.refreshManager tableViewReloadFinished];
}