你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS開發--自定義列表控件

iOS開發--自定義列表控件

編輯:IOS開發綜合

這兩天項目比較閒,在空余之際,嘗試自己實現列表控件。從動工到初步完成大概花了一天時間,目前實現了列表的簡單功能,後續將考慮加入cell重用機制、慣性特征以及刪除cell等功能。項目代碼已經放到了github上,地址:https://github.com/wanglichun/CustomTableView。

在實現之前,需要了解列表控件的運行原理,我之前的一篇博客《列表控件實現原理解析》中有介紹。去年由於項目需要,使用lua語言自定義過雙重列表(大列表嵌套小列表),這次改用objc實現,實現的思路和以前相同,只是換了個語言,因此速度比較快。下面分步驟介紹實現過程。

總體目錄結構:

\

目錄結構說明:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+MaGiVGFibGV3Vmlld8S/wryjukNTVGFibGVWaWV3o6hDdXN0b21UYWJsZVZpZXe1xMv10LSjqc6q19S2qNLltcTB0LHtv9i8/qOsQ1NUYWJsZVZpZXdDZWxszqrB0LHttcS1pdSqJiMyNjY4NDujuzwvcD4KPHA+MqGiUm9vdFZpZXdDb250cm9sbGVy1tCw/LqswcvSu7j2Q1NUYWJsZVZpZXe1xLLiytTA/dfToaM8L3A+CjxwPjxicj4KPC9wPgo8cD6yvdbo0rujusHQse21pdSqJiMyNjY4NDtDU1RhYmxlVmlld0NlbGzKtc/WPC9wPgo8cD4gICAgzqrBy73ayqHKsbzko6zO0rLJ08N4aWK1xLe9yr25ub2owctjZWxso6yyosfSY2VsbNbQ1ruw/Lqswcux6szi0MXPoqOs08O7p8jnufvP68q1z9a4tNTTtcRjZWxso6y/ydLUvMyz0ENTVGFibGVWaWV3Q2VsbMCp1bljZWxsoaPQ6NKq16LS4rXEysejrGNlbGzV4tK7suOyorK7tKbA7bSlw/7Kwrz+o6y2+MrHtKu13bj4uLi/2Lz+Q1NUYWJsZVZpZXe0psDtoaM8L3A+CjxwPrT6wuvI58/Co7o8L3A+CjxwPs23zsS8/qO6PC9wPgo8cD48cHJlIGNsYXNzPQ=="brush:java;">#import @interface CSTableViewCell : UIView + (CSTableViewCell *)csTableViewCell; - (void)setTitle:(NSString *)title; @end

實現文件:

#import "CSTableViewCell.h"

@interface  CSTableViewCell()

@property (weak, nonatomic) IBOutlet UILabel *titleLabel;

@end

@implementation CSTableViewCell

+ (CSTableViewCell *)csTableViewCell
{
    NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"CSTableViewCell" owner:nil options:nil];
    return views[0];
}

//將事件傳遞給父控件CSTableView處理
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    return NO;
}

- (void)setTitle:(NSString *)title
{
    self.titleLabel.text = title;
}

步驟二:列表控件CSTableView實現

關鍵步驟,如果對列表的運行原理非常了解,下面的代碼比較容易理解。需要注意,目前只是簡單的采用銷毀創建形式實現列表,未采用cell重用機制,後續有時間補上。DataSouce和Delegate參照UITableViewDataSouce和UITableViewDelegate定義,目前只是定義了幾個典型的方法。

頭文件:

#import 

@class CSTableView;
@class CSTableViewCell;

@protocol CSTableViewDataSource
@required
- (NSInteger)tableView:(CSTableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (CSTableViewCell *)tableView:(CSTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
@end

@protocol CSTableViewDataDelegate 
@optional
- (CGFloat)tableView:(CSTableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface CSTableView : UIView

@property (nonatomic,assign)id dataSource;
@property (nonatomic,assign)id delegate;

+ (CSTableView *)csTableView;
- (void)reloadData;

@end

實現文件:

#import "CSTableView.h"
#import "CSTableViewCell.h"

static const CGFloat kDefaultCellHeight = 27.0f;

@interface CSTableView()
{
    CGPoint _touchBeginPoint;
    CGPoint _touchMovePoint;
    CGPoint _touchEndPoint;
    
    NSInteger   _numberOfRows;
    CGFloat     _top;
    NSInteger   _startIndex;
    NSInteger   _endIndex;
    CGFloat     _startY;
    CGFloat     _dataTotalHeight; //CSTableView的邏輯高度
}

@end

@implementation CSTableView

+ (CSTableView *)csTableView
{
    NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"CSTableView" owner:nil options:nil];
    return views[0];
}

- (void)awakeFromNib
{
    //不能放在這裡,此時delegate和dataSource都為nil,之前犯錯誤了,後面移到了layoutSubviews
    //[self reloadData];
}

- (void)layoutSubviews{
    [self reloadData];
}

#pragma mark -- touches events

- (void)updateUIWithMoveDist:(CGFloat)moveDist
{
    //調控速度
    moveDist = 0.2 * moveDist;
    
    _top += moveDist;
    if (_top < 0) {
        _top = 0;
    }
    if (_dataTotalHeight > self.frame.size.height) {
        if (_top > _dataTotalHeight - self.frame.size.height) {
            _top = _dataTotalHeight - self.frame.size.height;
        }
        [self updateUI];
    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch=[touches anyObject];
    _touchBeginPoint = [touch locationInView:self];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch=[touches anyObject];
    _touchEndPoint = [touch locationInView:self];
    CGFloat moveDist = _touchBeginPoint.y - _touchEndPoint.y;
    [self updateUIWithMoveDist:moveDist];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch=[touches anyObject];
    CGPoint point = [touch locationInView:self];
    CGFloat moveDist =  _touchBeginPoint.y - point.y;
    [self updateUIWithMoveDist:moveDist];
}

#pragma mark -- reloadData

//計算顯示范圍
- (void)calculateStartIndexAndEndIndex
{
    _startIndex = -1;
    CGFloat totalHeight = 0;
    if ([self.delegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)]) {
        for (NSInteger i = 0; i < _numberOfRows; i++) {
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:1];
            CGFloat cellHeight = [self.delegate tableView:self heightForRowAtIndexPath:indexPath];
            totalHeight += cellHeight;
            if (totalHeight > _top && _startIndex == -1) {
                _startY = (totalHeight - cellHeight) - _top;
                _startIndex = i;
            }
            if (totalHeight > _top + self.frame.size.height || i == _numberOfRows - 1) {
                _endIndex = i;
                break;
            }
        }
    }else{
        for (NSInteger i = 0; i < _numberOfRows; i++) {
            totalHeight += kDefaultCellHeight;
            if (totalHeight > _top && _startIndex == -1) {
                _startY = (totalHeight - kDefaultCellHeight) - _top;
                _startIndex = i;
            }
            
            if (totalHeight > _top + self.frame.size.height || i == _numberOfRows - 1) {
                _endIndex = i;
                break;
            }
        }
    }
}

//更新UI
- (void)updateUI
{
    [self calculateStartIndexAndEndIndex];
    NSLog(@"startIndex = %ld", _startIndex);
    NSLog(@"endIndex = %ld", _endIndex);
    
    CGFloat totalHeight = 0.0f;
    for (NSInteger i = _startIndex; i <= _endIndex; i++) {
        //創建cell,如果用戶沒有自定義cell,則生成一個默認的cell
        CSTableViewCell *cell;
        if ([self.dataSource respondsToSelector:@selector(tableView:cellForRowAtIndexPath:)]) {
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:1];
            cell = [self.dataSource tableView:self cellForRowAtIndexPath:indexPath];
        }else{
            cell = [CSTableViewCell csTableViewCell];
            [cell setTitle:@"默認的cell"];
        }
        
        //設置cell的位置
        if ([self.delegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)]) {
                NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:1];
                CGFloat cellHeight = [self.delegate tableView:self heightForRowAtIndexPath:indexPath];
                totalHeight += cellHeight;
                cell.frame = CGRectMake(0, (totalHeight - cellHeight) + _startY, cell.frame.size.width, cell.frame.size.height);
        }else{
            cell.frame = CGRectMake(0, (i - _startIndex) *kDefaultCellHeight + _startY, cell.frame.size.width, cell.frame.size.height);
        }
        
        //將cell添加到CSTabelView
        [self addSubview:cell];
    }
}

//重新加載數據, 暫時沒有采用cell重用機制,後面有時間再加上
//刪除數據後,調用該函數,重新加載UI
- (void)reloadData
{
    for (UIView *subView in self.subviews) {
        [subView removeFromSuperview];
    }
    
    if ([self.dataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)]) {
        _numberOfRows = [self.dataSource tableView:self numberOfRowsInSection:1];
    }else{
        _numberOfRows = 10; //默認顯示10條數據
    }
    
    _dataTotalHeight = 0.0f;
    if ([self.delegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)]) {
        for (NSInteger i = 0; i < _numberOfRows; i++) {
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:1];
            CGFloat cellHeight = [self.delegate tableView:self heightForRowAtIndexPath:indexPath];
            _dataTotalHeight += cellHeight;
        }
    }else{
        _dataTotalHeight = kDefaultCellHeight * _numberOfRows;
    }
    
    //更新UI
    [self updateUI];
}

@end


步驟三:測試用例

#import "RootViewController.h"
#import "CSTableView.h"
#import "CSTableViewCell.h"

@interface RootViewController ()

@property (nonatomic, strong)CSTableView *csTableView;

@end

@implementation RootViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.csTableView = [CSTableView csTableView];
    self.csTableView.dataSource = self;
    self.csTableView.delegate = self;
    self.csTableView.frame = CGRectMake(0, 0, self.csTableView.frame.size.width, self.csTableView.frame.size.height);
    [self.view addSubview:self.csTableView];
}


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


#pragma mark -- CSTableViewDataSource
- (NSInteger)tableView:(CSTableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 50;
}

- (CSTableViewCell *)tableView:(CSTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CSTableViewCell *cell = [CSTableViewCell csTableViewCell];
    NSString *title = [[NSString alloc] initWithFormat:@"測試數據%ld", indexPath.row];
    [cell setTitle:title];
    return cell;
}

#pragma mark -- CSTableViewDataDelegate

- (CGFloat)tableView:(CSTableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row < 10) {
        return 20;
    }else if (indexPath.row >= 10 && indexPath.row < 20){
        return 30;
    }else{
        return 40;
    }
    
    //return 27;
}

@end




  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved