( 可以訪問(wèn)github的話(huà)請(qǐng)移步 https://github.com/MRsummer/SMTableView )
本文講述了一個(gè) table view 的簡(jiǎn)單實(shí)現(xiàn)态鳖, 意在解釋 table view 是如何工作的
SMTableView原理概述
SMTableView 繼承自 UIScrollView吨瞎, 直接利用了ScrollView 滑動(dòng)的特性, 使得代碼省去了滑動(dòng)處理這一塊的邏輯拥峦。想了解更多ScrollView的原理,請(qǐng)參考 http://oleb.net/blog/2014/04/understanding-uiscrollview/
SMTableView邏輯大概可以分為這幾塊:計(jì)算cell位置、 對(duì)cell進(jìn)行布局翻斟、cell的重用吮廉、ScrollView滑動(dòng)的處理
- 計(jì)算cell位置
根據(jù)delegate獲取到cell的數(shù)量和每個(gè)的高度苞尝,加起來(lái)得到總高度,然后設(shè)置ScrollView的contentSize茧痕, 就確定了scrollView的滾動(dòng)區(qū)域
_numberOfCells = [_tableViewDelegate numberOfRowsInTableView:self];
_cellYOffsets = [NSMutableArray new];
_cellHeights = [NSMutableArray new];
float height = 0;
for (int i = 0 ; i < _numberOfCells; i ++) {
float cellHeight = [_tableViewDelegate heightForRow:i inTableView:self];
[_cellHeights addObject:@(cellHeight)];
height += cellHeight;
[_cellYOffsets addObject:@(height)];
}
CGSize size = CGSizeMake(CGRectGetWidth(self.frame), height);
[self setContentSize:size];
- 對(duì)cell進(jìn)行布局
首先獲取到顯示區(qū)域野来,然后獲取到顯示區(qū)域的cell,并設(shè)置相應(yīng)的frame
_displayRange = [self displayRange];
for (int i = (int)_displayRange.location ; i < _displayRange.length + _displayRange.location; i ++) {
SMTableViewCell* cell = [self _cellForRow:i];
[self addSubview:cell];
_visibleCellsMap[@(i)] = cell;
cell.frame = [self _rectForCellAtRow:i];
}
- cell的重用
對(duì)cell布局完成后踪旷,這個(gè)時(shí)候是回收不可見(jiàn)cell的較好時(shí)機(jī)曼氛,獲取不可見(jiàn)cell,并將其從visibleCells中移除令野,加入到cellCache中
NSDictionary* dic = [_visibleCellsMap copy];
NSArray* keys = dic.allKeys;
for (NSNumber* rowIndex in keys) {
int row = [rowIndex intValue];
if (! NSLocationInRange(row, range)) {
SMTableViewCell* cell = _visibleCellsMap[rowIndex];
[_visibleCellsMap removeObjectForKey:rowIndex];
[_cacheCells addObject:cell];
[cell removeFromSuperview];
}
}
- ScrollView滑動(dòng)的處理
當(dāng)ScrollView滑動(dòng)的時(shí)候舀患,檢測(cè)displayRange是否發(fā)生變化,如果發(fā)生變化重新進(jìn)行布局和回收cell等工作
NSRange range = [self displayRange];
if (! NSEqualRanges(_displayRange, range)) {
[self layoutNeedDisplayCells];
}
SMTableView的使用
SMTableView定義如下
@interface SMTableView : UIScrollView
@property (nonatomic, strong) id<SMTableViewDelegate> tableViewDelegate;
//獲取可重用的Cell
- (SMTableViewCell *) dequeueTableViewCellForIdentifier:(NSString*)identifier;
//刷新tableView
- (void) reloadData;
@end
SMTableViewDelegate定義如下
@protocol SMTableViewDelegate <NSObject>
- (NSInteger) numberOfRowsInTableView:(SMTableView *)tableView; //獲取行數(shù)
- (CGFloat) heightForRow:(NSInteger)row inTableView:(SMTableView *)tableView; //獲取每行行高
- (SMTableViewCell *) cellForRow:(NSInteger)row inTableView:(SMTableView *)tableView; //獲取每行的cell
@end
Sample
- (void)testTableView
{
SMTableView *tableView = [[SMTableView alloc] initWithFrame:self.view.bounds];
__weak ViewController *wself = self;
tableView.tableViewDelegate = wself;
[self.view addSubview:tableView];
[tableView reloadData];
}
- (NSInteger)numberOfRowsInTableView:(SMTableView *)tableView
{
return 1000;
}
- (CGFloat)heightForRow:(NSInteger)row inTableView:(SMTableView *)tableView
{
return 100;
}
- (SMTableViewCell *)cellForRow:(NSInteger)row inTableView:(SMTableView *)tableView
{
SMTableViewCell *cell = [tableView dequeueTableViewCellForIdentifier:@"SMTableViewCell"];
if (!cell) {
cell = [[SMTableViewCell alloc] initWithIdentifier:@"SMTableViewCell"];
UILabel *label = [[UILabel alloc] initWithFrame:cell.bounds];
label.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[cell addSubview:label];
}
UILabel *label = cell.subviews.firstObject;
label.text = [NSString stringWithFormat:@"我是第%@行", @(row)];
cell.backgroundColor = [self randomColor];
return cell;
}
參考
本文參考了 DZTableView( https://github.com/yishuiliunian/DZTableView ) 的實(shí)現(xiàn)气破,并對(duì)其進(jìn)行了精簡(jiǎn)和改進(jìn)聊浅。