实际开发中,一个非常常见的场景是UITableView的分页加载,滑动到底部时自动加载更多,大部分开发者的解决方案是监听UIScrollView的滑动事件,判断滑动到底部时加载下一页数据,同时为UITableView addTableFooterView以展示一个loading的效果(一般是放一个UIActivityIndicatorView)。

这种做法有两个缺点:

  1. 当加载完成显示新数据时,新的Cell会替代footer的位置,出现跳动的现象,给人的直接感觉是会“卡顿”一下,很影响体验;
  2. 这种方式必须在- (void)scrollViewDidScroll:(UIScrollView *)scrollView中判断当前的偏移量以决定什么时机开始加载下一页,这无疑增加了复杂度。

一个更好的解决方案是把loading more作为一个Cell,而不是footer,这样,以上两个问题就可以完全避免了。

Talk is cheap,show you code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#pragma mark - UITableView Datasource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (_datasource.count && _hasMore)
{
return 2; // 增加一个加载更多
}

return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0)
{
return _datasource.count;
}

// 加载更多
return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
// 普通Cell,代码省略
// ...

return cell;
}

// 加载更多
static NSString *CellIdentifierLoadMore = @"CellIdentifierLoadMore";

UITableViewCell *loadCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierLoadMore];
if (!loadCell)
{
loadCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifierLoadMore];
loadCell.backgroundColor = [UIColor clearColor];
loadCell.contentView.backgroundColor = [UIColor clearColor];

UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
indicator.tag = kLoadMoreIndicatorTag;
indicator.hidesWhenStopped = YES;
indicator.center = CGPointMake(loadCell.dt_width/2, loadCell.dt_height/2);
indicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|
UIViewAutoresizingFlexibleRightMargin|
UIViewAutoresizingFlexibleTopMargin|
UIViewAutoresizingFlexibleBottomMargin;
[loadCell.contentView addSubview:indicator];
}

return loadCell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
// 普通Cell的高度
// ...
}

// 加载更多
return 44;
}

#pragma mark - UITableView Delegate methods

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
return;
}

// 加载更多
UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)[cell.contentView viewWithTag:kLoadMoreIndicatorTag];
[indicator startAnimating];

// 加载下一页
[self loadNextPage];
}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
return;
}

// 加载更多
UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)[cell.contentView viewWithTag:kLoadMoreIndicatorTag];
[indicator stopAnimating];
}