自己動手寫一個TableView

( 可以訪問github的話請移步 https://github.com/MRsummer/SMTableView

本文講述了一個 table view 的簡單實(shí)現(xiàn), 意在解釋 table view 是如何工作的

SMTableView原理概述

SMTableView 繼承自 UIScrollView, 直接利用了ScrollView 滑動的特性, 使得代碼省去了滑動處理這一塊的邏輯。想了解更多ScrollView的原理,請參考 http://oleb.net/blog/2014/04/understanding-uiscrollview/
SMTableView邏輯大概可以分為這幾塊:計(jì)算cell位置、 對cell進(jìn)行布局、cell的重用、ScrollView滑動的處理

  • 計(jì)算cell位置
    根據(jù)delegate獲取到cell的數(shù)量和每個的高度,加起來得到總高度,然后設(shè)置ScrollView的contentSize, 就確定了scrollView的滾動區(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];
  • 對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的重用
    對cell布局完成后,這個時候是回收不可見cell的較好時機(jī),獲取不可見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)ScrollView滑動的時候,檢測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),并對其進(jìn)行了精簡和改進(jìn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容