UIRefreshControlを使わないでPullToRefeshとPushToRefresh?

PullToRefresh。
iOS6からはUIRefreshControlなるものが登場し実装しやすくなった反面、
旧来のやり方がググって出にくくなったのでメモ。

ついでにあまり使われないとは思うのだが、
PullToRefreshの逆(PushToRefreshとは言わないでしょうね)も実装してみる。
逆という事なので、下に引っぱって更新ではなくて、上に引き上げても更新ができるようなイメージ。

PullToRefreshは以下で実装する。
1.UITableViewのtableHeaderViewに上部リフレッシュの際に表示されるUIViewを格納する
2.そのままだとtableHeaderViewが常に表示されてしまうので、[UITableView setContentInset:(UIEdgeInset)]でスクロールされない位置に隠しておく
3.UIScrollViewDelegateの scrollViewDidScroll:(UIScrollView *)とscrollViewWillBeginDecelerating:(UIScrollView *)を実装して、スクロール位置に応じた処理をする

PushToRefreshも合わせると以下の通り。
1.UITableViewのtableHeaderViewに上部リフレッシュの際に表示されるUIViewを格納する
2.UITableViewのtableFooterViewに下部リフレッシュの際に表示されるUIViewを格納する
3.そのままだとtableHeaderViewが常に表示されてしまうので、[UITableView setContentInset:(UIEdgeInset)]でスクロールされない位置に隠しておく
4.UIScrollViewDelegateの scrollViewDidScroll:(UIScrollView *)とscrollViewWillBeginDecelerating:(UIScrollView *)を実装して、スクロール位置に応じた処理をする

下部スクロールでtableFooterViewを使う場合、
tableViewのコンテンツ数が少ないとtableFooterViewが表示されてしまうので、
その状態を回避する為にnumberOfRowsInSectionで実際のデータ数+1を返すようにする。
そしてcellForRowAtIndexPathでは+1のindexPathが来た場合はセルを非表示にしている。

//
//  ViewController.m
//
//  Copyright (c) 2015年 Brick. All rights reserved.
//

#import "ViewController.h"

#define CELL_HEIGHT 60.0f

#define REFRESHVIEW_HEIGHT 80.0f

#define REFRESH_MESSAGE_OFF @"今離しても更新されません。"
#define REFRESH_MESSAGE_ON @"今離すと更新!"
#define REFRESHING_MESSAGE @"更新中"

#define TOP_REFRESH 80.0f
#define BOTTOM_REFRESH 80.0f


typedef enum : NSUInteger {
    resNORefresh = 0,
    resTOPRefresh = 1,
    resBOTTOMRefresh = 2,
} CheckRefreshResult;

@interface ViewController (){
    __weak IBOutlet UITableView *_tableView;
    NSArray *_datalist;
    UIView *_topRefreshView;
    UIView *_bottomRefreshView;
    BOOL _topRefreshing;
    BOOL _bottomRefreshing;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //  Topの更新View
    _topRefreshView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, REFRESHVIEW_HEIGHT)];
    [_topRefreshView setBackgroundColor:[UIColor blueColor]];
    UILabel *topLabel = [[UILabel alloc] initWithFrame:_topRefreshView.frame];
    topLabel.tag = 1;
    topLabel.text = REFRESH_MESSAGE_OFF;
    [_topRefreshView addSubview:topLabel];
    [_tableView setTableHeaderView:_topRefreshView];
    
    
    //  Bottomの更新View
     _bottomRefreshView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.height, REFRESHVIEW_HEIGHT)];
    [_bottomRefreshView setBackgroundColor:[UIColor redColor]];
    UILabel *bottomLabel = [[UILabel alloc] initWithFrame:_bottomRefreshView.frame];
    bottomLabel.tag = 1;
    bottomLabel.text = REFRESH_MESSAGE_OFF;
    [_bottomRefreshView addSubview:bottomLabel];
    [_tableView setTableFooterView:_bottomRefreshView];
    
    _topRefreshing = NO;
    _bottomRefreshing = NO;
    
    [self reloadData:NO];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



- (void)reloadData{
    [self reloadData:YES];
}
- (void)reloadData:(BOOL)animated{
    int numRow = 15;
    _topRefreshing = NO;
    _bottomRefreshing = NO;
    NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:0];
    for (int i=0; i<numRow; i++) [arr addObject:[NSString stringWithFormat:@"Cell:%d", i]];
    _datalist = [[NSArray alloc] initWithArray:arr];
    //  Edge調整
    if (animated) {
        [UIView animateWithDuration:0.3f animations:^{
            [_tableView setContentInset:UIEdgeInsetsMake(REFRESHVIEW_HEIGHT * -1.0f, 0.0f, REFRESHVIEW_HEIGHT * -1.0f, 0.0f)];
        }];
    }else{
        [_tableView setContentInset:UIEdgeInsetsMake(REFRESHVIEW_HEIGHT * -1.0f, 0.0f, REFRESHVIEW_HEIGHT * -1.0f, 0.0f)];
    }
    [_tableView reloadData];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if (_topRefreshing || _bottomRefreshing) {
        return;
    }
    UILabel *topLabel = (UILabel*)[_topRefreshView viewWithTag:1];
    UILabel *bottomLabel = (UILabel*)[_bottomRefreshView viewWithTag:1];
    
    switch ([self checkRefresh:scrollView.contentOffset.y]) {
        case resTOPRefresh:{
            topLabel.text = REFRESH_MESSAGE_ON;
            bottomLabel.text = REFRESH_MESSAGE_OFF;
            break;
        }
        case resBOTTOMRefresh:{
            topLabel.text = REFRESH_MESSAGE_OFF;
            bottomLabel.text = REFRESH_MESSAGE_ON;
            break;
        }
        default:{
            topLabel.text = REFRESH_MESSAGE_OFF;
            bottomLabel.text = REFRESH_MESSAGE_OFF;
            break;
        }
    }
}

- (CheckRefreshResult)checkRefresh:(float)offsetY{
    if (offsetY < REFRESHVIEW_HEIGHT - TOP_REFRESH) {
        //  Top Refresh圏
        return resTOPRefresh;
    }
    
    if (CELL_HEIGHT * _datalist.count < _tableView.frame.size.height) {
        // 調整モードの場合
        if (offsetY > REFRESHVIEW_HEIGHT + BOTTOM_REFRESH) {
            return resBOTTOMRefresh;
        }
    }else{
        // 通常のケース
        if (offsetY > _datalist.count * CELL_HEIGHT - _tableView.frame.size.height + REFRESHVIEW_HEIGHT + BOTTOM_REFRESH) {
            return resBOTTOMRefresh;
        }
    }
    return resNORefresh;
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
    if (_topRefreshing || _bottomRefreshing) {
        return;
    }
    
    UILabel *topLabel = (UILabel*)[_topRefreshView viewWithTag:1];
    UILabel *bottomLabel = (UILabel*)[_bottomRefreshView viewWithTag:1];
    switch ([self checkRefresh:scrollView.contentOffset.y]) {
        case resTOPRefresh:{
            topLabel.text = REFRESHING_MESSAGE;
            _topRefreshing = YES;
            [UIView animateWithDuration:0.3f animations:^{
                [_tableView setContentInset:UIEdgeInsetsMake(0.0f, 0.0f, REFRESHVIEW_HEIGHT * -1.0f, 0.0f)];
            }];
            [self performSelector:@selector(reloadData) withObject:nil afterDelay:1.0f];
            break;
        }
        case resBOTTOMRefresh:{
            bottomLabel.text = REFRESHING_MESSAGE;
            _bottomRefreshing = YES;
            [UIView animateWithDuration:0.3f animations:^{
                [_tableView setContentInset:UIEdgeInsetsMake(REFRESHVIEW_HEIGHT * -1.0f, 0.0f, 0.0f, 0.0f)];
            }];
            [self performSelector:@selector(reloadData) withObject:nil afterDelay:1.0f];
            break;
        }
        default:{
            break;
        }
    }
    
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    if (CELL_HEIGHT * _datalist.count < _tableView.frame.size.height) {
        //  TableViewのコンテンツ高さが画面の高さより小さいと、FooterViewが見切れてしまうので、1つからのCellを挟む
        return _datalist.count + 1;
    }
    return _datalist.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    if (indexPath.row >= _datalist.count) {
        //  TableViewのコンテンツ高さを生める為のセルの高さをうめる
        return _tableView.frame.size.height - _datalist.count * CELL_HEIGHT;
    }
    
    return CELL_HEIGHT;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    NSString *identifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    
    if (indexPath.row >= _datalist.count) {
        [cell setHidden:YES];
        [cell setUserInteractionEnabled:NO];
    }else{
        [cell setHidden:NO];
        [cell setUserInteractionEnabled:YES];
        cell.textLabel.text = _datalist[indexPath.row];
    }

    return cell;
}


@end

Leave a reply

Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>