UITableView用法整理


UITableView是最常用的控件,這里整理下常用方法,outline如下(本文參考http://www.cnblogs.com/kenshincui/p/3931948.html,代碼下載地址為https://github.com/zanglitao/tableviewdemo

1:基本介紹

2:數據源

3:代理

4:修改,刪除,添加和排序

5:背景view

6:自定義cell

7:UITableViewController

8:UISearchdisplayController

9:下拉刷新

10:靜態tableviewcell

 

基本介紹

UITableView有兩種風格:UITableViewStylePlain和UITableViewStyleGrouped。這兩者操作起來其實並沒有本質區別,只是后者按分組樣式顯示前者按照普通樣式顯示而已。大家先看一下兩者的應用:

1>分組樣式

UITableViewStyleGrouped1      UITableViewStyleGrouped2

2>不分組樣式

UITableViewStylePlain1       UITableViewStylePlain2

大家可以看到在UITableView中數據只有行的概念,並沒有列的概念,因為在手機操作系統中顯示多列是不利於操作的。UITableView中每行數據都是一個UITableViewCell,在這個控件中為了顯示更多的信息,iOS已經在其內部設置好了多個子控件以供開發者使用。如果我們查看UITableViewCell的聲明文件可以發現在內部有一個UIView控件(contentView,作為其他元素的父控件)、兩個UILable控件(textLabel、detailTextLabel)、一個UIImage控件(imageView),分別用於容器、顯示內容、詳情和圖片。使用效果類似於微信、QQ信息列表:

UITableViewCell1      UITableViewCell2

當然,這些子控件並不一定要全部使用,具體操作時可以通過UITableViewCellStyle進行設置,具體每個枚舉表示的意思已經在代碼中進行了注釋:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
    UITableViewCellStyleDefault,    // 左側顯示textLabel(不顯示detailTextLabel),imageView可選(顯示在最左邊)
    UITableViewCellStyleValue1,        // 左側顯示textLabel、右側顯示detailTextLabel(默認藍色),imageView可選(顯示在最左邊)
    UITableViewCellStyleValue2,        // 左側依次顯示textLabel(默認藍色)和detailTextLabel,imageView可選(顯示在最左邊)
    UITableViewCellStyleSubtitle    // 左上方顯示textLabel,左下方顯示detailTextLabel(默認灰色),imageView可選(顯示在最左邊)
};

 

數據源

由於iOS是遵循MVC模式設計的,很多操作都是通過代理和外界溝通的,但對於數據源控件除了代理還有一個數據源屬性,通過它和外界進行數據交互。 對於UITableView設置完dataSource后需要實現UITableViewDataSource協議,在這個協議中定義了多種 數據操作方法,下面通過創建一個簡單的聯系人管理進行演示:

UserEntity包括姓名和電話兩個屬性

@interface UserEntity : NSObject

@property(nonatomic,strong)NSString *name;
@property(nonatomic,strong)NSString *phone;

-(id)initWithName:(NSString *)name Phone:(NSString *)phone;

@end

@implementation UserEntity

-(id)initWithName:(NSString *)name Phone:(NSString *)phone {
    self = [super init];
    if (self) {
        self.name = name;
        self.phone = phone;
    }
    
    return self;
}

@end

UserGroup代表一個分表,包括聯系人數組,分組代號以及分組描述

@interface UserGroup : NSObject

@property(nonatomic,strong)NSMutableArray *userEntities;
@property(nonatomic,strong)NSString *groupIdentifier;
@property(nonatomic,strong)NSString *groupIntro;

-(id)initWithEntities:(NSArray *)entities GroupIdentifier:(NSString *)groupIdentifier GroupIntro:(NSString *)groupIntro;

@end

@implementation UserGroup

-(id)initWithEntities:(NSMutableArray *)entities GroupIdentifier:(NSString *)groupIdentifier GroupIntro:(NSString *)groupIntro {
    self = [super init];
    if (self) {
        self.userEntities = entities;
        self.groupIdentifier = groupIdentifier;
        self.groupIntro = groupIntro;
    }
    return self;
}

@end

viewcontroller需要實現UITableViewDataSource協議,協議中包含了數據源相關方法,用來控制列表數據

@interface ZLTViewController ()<UITableViewDataSource> {
    UITableView *_tableView;
    NSMutableArray *_dataSource;
}

@end

@implementation ZLTViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //配置列表需要展示的數據,真實項目中這部分數據往往來自文件或者網絡
    [self initdata];
    
    _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStyleGrouped];
    //設置列表數據源
    _tableView.dataSource = self;
    
    [self.view addSubview:_tableView];
}

- (void)initdata {
    UserEntity *entity1 = [[UserEntity alloc] initWithName:@"user1" Phone:@"11111111111"];
    UserEntity *entity2 = [[UserEntity alloc] initWithName:@"user2" Phone:@"11111111112"];
    UserGroup *group1 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity1,entity2, nil] GroupIdentifier:@"1" GroupIntro:@"this is group1"];
    
    
    UserEntity *entity3 = [[UserEntity alloc] initWithName:@"user3" Phone:@"11111111113"];
    UserEntity *entity4 = [[UserEntity alloc] initWithName:@"user4" Phone:@"11111111114"];
    UserEntity *entity5 = [[UserEntity alloc] initWithName:@"user5" Phone:@"11111111115"];
    UserGroup *group2 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity3,entity4,entity5, nil] GroupIdentifier:@"2" GroupIntro:@"this is group2"];
    
    UserEntity *entity6 = [[UserEntity alloc] initWithName:@"user6" Phone:@"11111111116"];
    UserEntity *entity7 = [[UserEntity alloc] initWithName:@"user7" Phone:@"11111111117"];
    UserGroup *group3 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity6,entity7, nil] GroupIdentifier:@"3" GroupIntro:@"this is group3"];
    
    UserEntity *entity8 = [[UserEntity alloc] initWithName:@"user8" Phone:@"11111111118"];
    UserGroup *group4 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity8,nil] GroupIdentifier:@"4" GroupIntro:@"this is group4"];
    
    UserEntity *entity9 = [[UserEntity alloc] initWithName:@"user9" Phone:@"11111111119"];
    UserEntity *entity10 = [[UserEntity alloc] initWithName:@"user10" Phone:@"111111111110"];
    UserEntity *entity11 = [[UserEntity alloc] initWithName:@"user11" Phone:@"111111111111"];
    UserGroup *group5 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity9,entity10,entity11, nil] GroupIdentifier:@"5" GroupIntro:@"this is group5"];
    
    _dataSource = [NSMutableArray arrayWithObjects:group1,group2,group3,group4,group5, nil];
}

//返回列表分組數,默認為1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [_dataSource count];
}

//返回列表每個分組section擁有cell行數
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [((UserGroup *)_dataSource[section]).userEntities count];
}

//配置每個cell,隨着用戶拖拽列表,cell將要出現在屏幕上時此方法會不斷調用返回cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *identifier = @"mycell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
    }
    
    UserGroup *group = _dataSource[indexPath.section];
    UserEntity *entity = group.userEntities[indexPath.row];
    cell.detailTextLabel.text = entity.phone;
    cell.textLabel.text = entity.name;
    
    //給cell設置accessoryType或者accessoryView
    //也可以不設置,這里純粹為了展示cell的常用可設置選項
    if (indexPath.section == 0 && indexPath.row == 0) {
        cell.accessoryType = UITableViewCellAccessoryDetailButton;
    }else if (indexPath.section == 0 && indexPath.row == 1) {
        cell.accessoryView = [[UISwitch alloc] initWithFrame:CGRectZero];
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
    
    //設置cell沒有選中效果
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
    return cell;
}

//返回列表每個分組頭部說明
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    return [_dataSource[section] groupIdentifier];
}

//返回列表每個分組尾部說明
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
    return [_dataSource[section] groupIntro];
}

@end

大家在使用iPhone通訊錄時會發現右側可以按字母檢索,使用起來很方便,其實這個功能使用UITableView實現很簡單,只要實現數據源協議的一個方法,構建一個分組標題的數組即可實現。數組元素的內容和組標題內容未必完全一致,UITableView是按照數組元素的索引和每組數據索引順序來定位的而不是按內容查找。

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    NSMutableArray *array = [NSMutableArray array];
    [_dataSource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [array addObject:[obj groupIdentifier]];
    }];

    return array;
}

 

需要注意的是上面幾個重點方法的執行順序,請看下圖:

image 

 

代理

數據源足以用來顯示列表數據,但是如果我們需要改變sectionheader或者sectionfooter的高度,樣式 ,以及處理cell點擊事件還需要設置列表的代理UITableViewDelegate

@interface ZLTViewController ()<UITableViewDataSource,UITableViewDelegate> {
    UITableView *_tableView;
    NSMutableArray *_dataSource;
}

@end

@implementation ZLTViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //配置列表需要展示的數據,真實項目中這部分數據往往來自文件或者網絡
    [self initdata];
    
    _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStyleGrouped];
    //設置列表數據源
    _tableView.dataSource = self;
    //設置列表代理
    _tableView.delegate = self;
    
    [self.view addSubview:_tableView];
}

- (void)initdata {
    UserEntity *entity1 = [[UserEntity alloc] initWithName:@"user1" Phone:@"11111111111"];
    UserEntity *entity2 = [[UserEntity alloc] initWithName:@"user2" Phone:@"11111111112"];
    UserGroup *group1 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity1,entity2, nil] GroupIdentifier:@"1" GroupIntro:@"this is group1"];
    
    
    UserEntity *entity3 = [[UserEntity alloc] initWithName:@"user3" Phone:@"11111111113"];
    UserEntity *entity4 = [[UserEntity alloc] initWithName:@"user4" Phone:@"11111111114"];
    UserEntity *entity5 = [[UserEntity alloc] initWithName:@"user5" Phone:@"11111111115"];
    UserGroup *group2 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity3,entity4,entity5, nil] GroupIdentifier:@"2" GroupIntro:@"this is group2"];
    
    UserEntity *entity6 = [[UserEntity alloc] initWithName:@"user6" Phone:@"11111111116"];
    UserEntity *entity7 = [[UserEntity alloc] initWithName:@"user7" Phone:@"11111111117"];
    UserGroup *group3 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity6,entity7, nil] GroupIdentifier:@"3" GroupIntro:@"this is group3"];
    
    UserEntity *entity8 = [[UserEntity alloc] initWithName:@"user8" Phone:@"11111111118"];
    UserGroup *group4 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity8,nil] GroupIdentifier:@"4" GroupIntro:@"this is group4"];
    
    UserEntity *entity9 = [[UserEntity alloc] initWithName:@"user9" Phone:@"11111111119"];
    UserEntity *entity10 = [[UserEntity alloc] initWithName:@"user10" Phone:@"111111111110"];
    UserEntity *entity11 = [[UserEntity alloc] initWithName:@"user11" Phone:@"111111111111"];
    UserGroup *group5 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity9,entity10,entity11, nil] GroupIdentifier:@"5" GroupIntro:@"this is group5"];
    
    _dataSource = [NSMutableArray arrayWithObjects:group1,group2,group3,group4,group5, nil];
}

//返回列表分組數,默認為1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [_dataSource count];
}

//返回列表每個分組section擁有cell行數
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [((UserGroup *)_dataSource[section]).userEntities count];
}

//配置每個cell,隨着用戶拖拽列表,cell將要出現在屏幕上時此方法會不斷調用返回cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *identifier = @"mycell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
    }
    
    UserGroup *group = _dataSource[indexPath.section];
    UserEntity *entity = group.userEntities[indexPath.row];
    cell.detailTextLabel.text = entity.phone;
    cell.textLabel.text = entity.name;
    
    //給cell設置accessoryType或者accessoryView
    //也可以不設置,這里純粹為了展示cell的常用可設置選項
//    if (indexPath.section == 0 && indexPath.row == 0) {
//        cell.accessoryType = UITableViewCellAccessoryDetailButton;
//    }else if (indexPath.section == 0 && indexPath.row == 1) {
//        cell.accessoryView = [[UISwitch alloc] initWithFrame:CGRectZero];
//    } else {
//        cell.accessoryType = UITableViewCellAccessoryNone;
//    }
    
    //設置cell沒有選中效果
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
    return cell;
}

//返回列表每個分組頭部說明
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    return [_dataSource[section] groupIdentifier];
}

//返回列表每個分組尾部說明
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
    return [_dataSource[section] groupIntro];
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    NSMutableArray *array = [NSMutableArray array];
    [_dataSource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [array addObject:[obj groupIdentifier]];
    }];
    
    return array;
}

//設置cell的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 60;
}

//設置sectionheader的高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section == 0) {
        return 60;
    }
    return 40;
}

//設置sectionfooter的高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 40;
}

//設置cell的點擊事件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"title" message:@"message" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil];
    alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
    UITextField *text = [alertView textFieldAtIndex:0];
    
    UserGroup *group = _dataSource[indexPath.section];
    NSString *phone = [group.userEntities[indexPath.row] phone];
    [text setText:phone];

    [alertView show];
}

//自定義sectionheader顯示的view
//- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
//    UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
//    [view setBackgroundColor:[UIColor greenColor]];
//    return view;
//}

//自定義sectionfooter顯示的view
//- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
//    UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
//    [view setBackgroundColor:[UIColor yellowColor]];
//    return view;
//}
@end

 

由於實現了tableView:didSelectRowAtIndexPath: ,所以點擊某個cell后會alert出聯系人的屬性"phone"

 

修改,刪除,添加和排序

UITableView除了顯示數據外,往往還提供了更多的交互。

修改數據:當我們點擊cell后彈出alertview,在alertView中可以設置號碼屬性,修改完后調用reloadRowsAtIndexPaths對界面進行局部更新

//設置cell的點擊事件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    _indexPath = indexPath;
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"title" message:@"message" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定",nil];
    alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
    UITextField *text = [alertView textFieldAtIndex:0];
    
    UserGroup *group = _dataSource[indexPath.section];
    NSString *phone = [group.userEntities[indexPath.row] phone];
    [text setText:phone];

    [alertView show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (buttonIndex == 1) {
        UITextField *field = [alertView textFieldAtIndex:0];
        NSString *phone = [field text];
        
        UserGroup *group = _dataSource[_indexPath.section];
        UserEntity *entity = group.userEntities[_indexPath.row];
        [entity setPhone:phone];
        
        //[_tableView reloadData];
        [_tableView reloadRowsAtIndexPaths:@[_indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}

 除了局部indexpath的更新外,UITableview還提供了reloadData來進行整個列表的更新,但是這種更新肯定比局部更新更耗資源

 

刪除,添加數據:通過setEditing:YES可以讓列表處於編輯狀態,我們可以設置方法

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath

的返回值確定編輯風格是刪除還是添加,默認狀態下該方法返回的編輯風格是刪除風格UITableViewCellEditingStyleDelete

我們在根視圖上添加一個toolbar,toolbar上放置刪除和添加兩個按鈕,當點擊按鈕后設置相應的編輯風格,然后讓列表進入編輯狀態

@interface ZLTViewController ()<UITableViewDataSource,UITableViewDelegate> {
    UITableView *_tableView;
    NSMutableArray *_dataSource;
    NSIndexPath *_indexPath;
    UIToolbar *_toolbar;
    
    //判斷當前處於的狀態
    BOOL *_isAdd;
}

@end

@implementation ZLTViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //配置列表需要展示的數據,真實項目中這部分數據往往來自文件或者網絡
    [self initdata];
    
    _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStyleGrouped];
    //設置列表數據源
    _tableView.dataSource = self;
    //設置列表代理
    _tableView.delegate = self;
    //為了不遮擋toolbar,tableview的內容顯示區域向下移動44個像素
    _tableView.contentInset = UIEdgeInsetsMake(44, 0, 0, 0);
    
    
    [self.view addSubview:_tableView];
    [self setToolbar];
}

- (void)setToolbar {
    _toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    [self.view addSubview:_toolbar];
    
    UIBarButtonItem *deleteItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(delete)];
    
    UIBarButtonItem *flexibleItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
    
    UIBarButtonItem *addItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add)];
    _toolbar.items = @[deleteItem,flexibleItem,addItem];
}


- (void)initdata {
    UserEntity *entity1 = [[UserEntity alloc] initWithName:@"user1" Phone:@"11111111111"];
    UserEntity *entity2 = [[UserEntity alloc] initWithName:@"user2" Phone:@"11111111112"];
    UserGroup *group1 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity1,entity2, nil] GroupIdentifier:@"1" GroupIntro:@"this is group1"];
    
    
    UserEntity *entity3 = [[UserEntity alloc] initWithName:@"user3" Phone:@"11111111113"];
    UserEntity *entity4 = [[UserEntity alloc] initWithName:@"user4" Phone:@"11111111114"];
    UserEntity *entity5 = [[UserEntity alloc] initWithName:@"user5" Phone:@"11111111115"];
    UserGroup *group2 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity3,entity4,entity5, nil] GroupIdentifier:@"2" GroupIntro:@"this is group2"];
    
    UserEntity *entity6 = [[UserEntity alloc] initWithName:@"user6" Phone:@"11111111116"];
    UserEntity *entity7 = [[UserEntity alloc] initWithName:@"user7" Phone:@"11111111117"];
    UserGroup *group3 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity6,entity7, nil] GroupIdentifier:@"3" GroupIntro:@"this is group3"];
    
    UserEntity *entity8 = [[UserEntity alloc] initWithName:@"user8" Phone:@"11111111118"];
    UserGroup *group4 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity8,nil] GroupIdentifier:@"4" GroupIntro:@"this is group4"];
    
    UserEntity *entity9 = [[UserEntity alloc] initWithName:@"user9" Phone:@"11111111119"];
    UserEntity *entity10 = [[UserEntity alloc] initWithName:@"user10" Phone:@"111111111110"];
    UserEntity *entity11 = [[UserEntity alloc] initWithName:@"user11" Phone:@"111111111111"];
    UserGroup *group5 = [[UserGroup alloc] initWithEntities:[NSMutableArray arrayWithObjects:entity9,entity10,entity11, nil] GroupIdentifier:@"5" GroupIntro:@"this is group5"];
    
    _dataSource = [NSMutableArray arrayWithObjects:group1,group2,group3,group4,group5, nil];
}

//返回列表分組數,默認為1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [_dataSource count];
}

//返回列表每個分組section擁有cell行數
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [((UserGroup *)_dataSource[section]).userEntities count];
}

//配置每個cell,隨着用戶拖拽列表,cell將要出現在屏幕上時此方法會不斷調用返回cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *identifier = @"mycell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
    }
    
    UserGroup *group = _dataSource[indexPath.section];
    UserEntity *entity = group.userEntities[indexPath.row];
    cell.detailTextLabel.text = entity.phone;
    cell.textLabel.text = entity.name;
    
    //給cell設置accessoryType或者accessoryView
    //也可以不設置,這里純粹為了展示cell的常用可設置選項
//    if (indexPath.section == 0 && indexPath.row == 0) {
//        cell.accessoryType = UITableViewCellAccessoryDetailButton;
//    }else if (indexPath.section == 0 && indexPath.row == 1) {
//        cell.accessoryView = [[UISwitch alloc] initWithFrame:CGRectZero];
//    } else {
//        cell.accessoryType = UITableViewCellAccessoryNone;
//    }
    
    //設置cell沒有選中效果
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
    return cell;
}

//返回列表每個分組頭部說明
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    return [_dataSource[section] groupIdentifier];
}

//返回列表每個分組尾部說明
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
    return [_dataSource[section] groupIntro];
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    NSMutableArray *array = [NSMutableArray array];
    [_dataSource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [array addObject:[obj groupIdentifier]];
    }];
    
    return array;
}

//設置cell的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 60;
}

//設置sectionheader的高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section == 0) {
        return 60;
    }
    return 40;
}

//設置sectionfooter的高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 40;
}

//設置cell的點擊事件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    _indexPath = indexPath;
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"title" message:@"message" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定",nil];
    alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
    UITextField *text = [alertView textFieldAtIndex:0];
    
    UserGroup *group = _dataSource[indexPath.section];
    NSString *phone = [group.userEntities[indexPath.row] phone];
    [text setText:phone];

    [alertView show];
}

//自定義sectionheader顯示的view
//- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
//    UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
//    [view setBackgroundColor:[UIColor greenColor]];
//    return view;
//}

//自定義sectionfooter顯示的view
//- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
//    UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
//    [view setBackgroundColor:[UIColor yellowColor]];
//    return view;
//}

//設置編輯風格
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (_isAdd) {
        return UITableViewCellEditingStyleInsert;
    } else {
        return UITableViewCellEditingStyleDelete;
    }
}

//處理添加和刪除的代碼
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        UserGroup *group = _dataSource[indexPath.section];
        [group.userEntities removeObjectAtIndex:indexPath.row];
        
        
        [_tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
        
        if ([group.userEntities count] == 0) {
            [_dataSource removeObjectAtIndex:indexPath.section];
            [_tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationBottom];
        }
    } else if(editingStyle == UITableViewCellEditingStyleInsert) {
        UserGroup *group = _dataSource[indexPath.section];
        [group.userEntities insertObject:[[UserEntity alloc] initWithName:@"new user" Phone:@"137xxxxxxxx"] atIndex:indexPath.row];
        
        [_tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
    }
}


//刪除
- (void)delete {
    _isAdd = NO;
    [_tableView setEditing:!_tableView.isEditing animated:YES];
}


//添加
- (void)add {
    _isAdd = YES;
    [_tableView setEditing:!_tableView.isEditing animated:YES];
}


//修改
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (buttonIndex == 1) {
        UITextField *field = [alertView textFieldAtIndex:0];
        NSString *phone = [field text];
        
        UserGroup *group = _dataSource[_indexPath.section];
        UserEntity *entity = group.userEntities[_indexPath.row];
        [entity setPhone:phone];
        
        //[_tableView reloadData];
        [_tableView reloadRowsAtIndexPaths:@[_indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}


@end

 

 

排序:當實現- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath

方法后,列表處於編輯狀態時就可以移動cell

 

 

背景view

當我們刪除完列表中所有的數據,或者列表中本來就沒有數據時可以設置一個背景,比如顯示一個logo

UITableView有個屬性為backgroundView,我們可以通過這個屬性設置列表的背景視圖,當加載完數據或者對數據進行了刪除添加操作后判斷數據源中有沒有數據,如果沒有則在tableview中心處顯示"暫時沒有數據",如果有則不顯示

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //配置列表需要展示的數據,真實項目中這部分數據往往來自文件或者網絡
    [self initdata];
    
    _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStyleGrouped];
    //設置列表數據源
    _tableView.dataSource = self;
    //設置列表代理
    _tableView.delegate = self;
    //為了不遮擋toolbar,tableview的內容顯示區域向下移動44個像素
    _tableView.contentInset = UIEdgeInsetsMake(44, 0, 0, 0);
    
    UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    label.tag = 1;
    label.text = @"暫時沒有數據";
    label.textAlignment = NSTextAlignmentCenter;
    [label setTextColor:[UIColor lightGrayColor]];
    [view addSubview:label];
    _tableView.backgroundView = view;
    label.center = view.center;
    
    if ([_dataSource count] != 0) {
        [[_tableView.backgroundView viewWithTag:1] setHidden:YES];
    }
    
    [self.view addSubview:_tableView];
    [self setToolbar];
}

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        UserGroup *group = _dataSource[indexPath.section];
        [group.userEntities removeObjectAtIndex:indexPath.row];
        
        
        [_tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
        
        if ([group.userEntities count] == 0) {
            [_dataSource removeObjectAtIndex:indexPath.section];
            [_tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationBottom];
        }
    } else if(editingStyle == UITableViewCellEditingStyleInsert) {
        UserGroup *group = _dataSource[indexPath.section];
        [group.userEntities insertObject:[[UserEntity alloc] initWithName:@"new user" Phone:@"137xxxxxxxx"] atIndex:indexPath.row];
        
        [_tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
    }
    
    if ([_dataSource count] == 0) {
        [[_tableView.backgroundView viewWithTag:1] setHidden:NO];
    } else {
        [[_tableView.backgroundView viewWithTag:1] setHidden:YES];
    }
}

 

自定義cell

UITableViewCell是構建一個UITableView的基礎,在UITableViewCell內部有一個UIView控件作為其他內容的容器,它上面有一個UIImageView和兩個UILabel,通過UITableViewCellStyle屬性可以對其樣式進行控制。其結構如下:

UITableViewCellStuct

有時候我們會發現很多UITableViewCell右側可以顯示不同的圖標,在iOS中稱之為訪問器,點擊可以觸發不同的事件,例如設置功能:

UITableViewCellAccesoryType

要設置這些圖標只需要設置UITableViewCell的accesoryType屬性,這是一個枚舉類型具體含義如下:

typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {
    UITableViewCellAccessoryNone,                   // 不顯示任何圖標
    UITableViewCellAccessoryDisclosureIndicator,    // 跳轉指示圖標
    UITableViewCellAccessoryDetailDisclosureButton, // 內容詳情圖標和跳轉指示圖標
    UITableViewCellAccessoryCheckmark,              // 勾選圖標
    UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) // 內容詳情圖標
};

 

顯然系統提供的幾種樣式是無法滿足各種功能繁多的應用的,我們需要自定義自己的cell

自定義cell的方式有很多,有純代碼的實現,也有通過xib實現或者通過storyboard實現,這里介紹使用storyboard,這也是最方便的方式

1:新建兩個文件:CustomTableCellController(繼承UIViewController) CustomTableCell(繼承UITableViewCell)

2:在storyboard中新加入一個ViewController,file owner為CustomTableCellView

3:拖入一個tableView,設置一個prototype cell

4:在cell中拖入兩個label,並且設置cell的class為CustomTableCell,並設置一個identifier

5:CustomTableCell

@interface CustomTableCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UILabel *label1;
@property (weak, nonatomic) IBOutlet UILabel *label2;

-(void)setContent:(NSDictionary *)dic;
@end

@implementation CustomTableCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}

-(void)setContent:(NSDictionary *)dic {
    NSString *label1 = dic[@"label1"];
    NSString *lable2 = dic[@"label2"];
    [_label1 setText:label1];
    
    CGSize textSize=[lable2 boundingRectWithSize:CGSizeMake(226, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:17]} context:nil].size;
    
    _label2.frame = CGRectMake(_label2.frame.origin.x, _label2.frame.origin.y, _label2.frame.size.width, textSize.height);
    _label2.text = lable2;
}

@end

6: CustomTableCellController

@interface CustomTableCellController ()<UITableViewDelegate,UITableViewDataSource> {
    NSArray *_array;
}
@property (weak, nonatomic) IBOutlet UITableView *tableView;

@end

@implementation CustomTableCellController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    _array = @[@{@"label1": @"靜夜思",@"label2":@"床前明月光,疑是地上霜。舉頭望明月,低頭思故鄉。"},@{@"label1": @"早秋",@"label2":@"遙夜泛清瑟,西風生翠蘿。殘螢棲玉露,早雁拂金河。高樹曉還密,遠山晴更多。                             淮南一葉下,自覺洞庭波。"}];
    
    _tableView.delegate = self;
    _tableView.dataSource = self;
}

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

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 2;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"customcell" forIndexPath:indexPath];
    
    [cell setContent:_array[indexPath.row]];
    
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGSize textSize=[_array[indexPath.row][@"label2"] boundingRectWithSize:CGSizeMake(226, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:17]} context:nil].size;
    
    return textSize.height + 10;
}
@end

 

 

UITableViewController

很多時候一個UIViewController中只有一個UITableView,因此蘋果官方為了方便大家開發直接提供了一個UITableViewController,這個控制器 UITableViewController實現了UITableView數據源和代理協議,內部定義了一個tableView屬性供外部訪問,同時自動鋪滿整個屏幕、自動伸縮以方便我們的開發。當然UITableViewController也並不是簡單的幫我們定義完UITableView並且設置了數據源、代理而已,它還有其他強大的功能,例如刷新控件、滾動過程中固定分組標題等。

有時候一個表格中的數據特別多,檢索起來就顯得麻煩,這個時候可以實現一個搜索功能幫助用戶查找數據,其實搜索的原理很簡單:修改模型、刷新表格。下面使用UITableViewController簡單演示一下這個功能:

@interface CustomTableViewController : UITableViewController
@end


@interface CustomTableViewController ()<UISearchBarDelegate> {
    UISearchBar *_searchBar;
    //數據源
    NSArray *_datasource;
    
    //存放符合篩選條件的數據源
    NSMutableArray *_filterdatasource;
    
    //searcbar是否處於搜索狀態,這個布爾值決定使用_datasource還是_filterdatasource
    BOOL _isSearching;
}

@end

@implementation CustomTableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    _datasource = @[@[@"zanglitao",@"zang",@"li",@"tao"],@[@"male"]];
    _filterdatasource = [NSMutableArray array];
    
    
    _searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    _searchBar.delegate = self;
    _searchBar.showsCancelButton = YES;
    _searchBar.placeholder = @"輸入關鍵字";
    //_searchBar.keyboardType = UIKeyboardTypeAlphabet;//鍵盤類型
    //_searchBar.autocorrectionType = UITextAutocorrectionTypeNo;//自動糾錯類型
    //_searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;//哪一次shitf被自動按下
    self.tableView.tableHeaderView = _searchBar;
}

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

#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    if (_isSearching) {
        return 1;
    }
    return [_datasource count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (_isSearching) {
        return [_filterdatasource count];
    }
    return [_datasource[section] count];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    if (_isSearching) {
        cell.textLabel.text = _filterdatasource[indexPath.row];
    } else {
        cell.textLabel.text = _datasource[indexPath.section][indexPath.row];
    }
    
    
    return cell;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (_isSearching) {
        return @"搜索結果";
    }
    return [NSString stringWithFormat:@"%d",section];
}

#pragma mark - Search bar delegate
//searchbar開始處於編輯狀態時調用
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
    [_filterdatasource removeAllObjects];
    _isSearching = YES;
    [self.tableView reloadData];
}

//searchbar內容改變時調用
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
    [_filterdatasource removeAllObjects];
    [_datasource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [obj enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSString *str = [(NSString *)obj uppercaseString];
            if ([str rangeOfString:[searchText uppercaseString]].location != NSNotFound) {
                [_filterdatasource addObject:obj];
            }
        }];
    }];
    
    [self.tableView reloadData];
}

//點擊searchbar的cancel按鈕時調用
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar{
    [searchBar resignFirstResponder];
    [searchBar setText:@""];
    _isSearching = NO;
    [self.tableView reloadData];
}

//點擊搜索
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [searchBar resignFirstResponder];
}
@end

 

 

 

UISearchDisplayController

在上面的搜索中每次搜索完后都需要手動刷新表格來顯示搜索結果,我們要用一個tableView顯示兩種狀態的不同數據,提高了程序邏輯復雜度。為了簡化這個過程,我們可以使用UISearchDisplayController,UISearchDisplayController內部也有一個UITableView類型的對象searchResultsTableView,如果我們設置它的數據源代理為當前控制器,那么它完全可以像UITableView一樣加載數據。同時它本身也有搜索監聽的方法,我們不必在監聽UISearchBar輸入內容,直接使用它的方法即可自動刷新其內部表格。

@interface CustomSearchBarTableViewController : UITableViewController
@end

@interface CustomSearchBarTableViewController ()<UISearchDisplayDelegate,UISearchBarDelegate> {
    //數據源
    NSArray *_datasource;
    
    //存放符合篩選條件的數據源
    NSMutableArray *_filterdatasource;
    
    UISearchDisplayController *_searchController;
    UISearchBar *_searchBar;
}

@end

@implementation CustomSearchBarTableViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    _datasource = @[@[@"zanglitao",@"zang",@"li",@"tao"],@[@"male"]];
    _filterdatasource = [NSMutableArray array];
    
    _searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    _searchBar.delegate = self;
    _searchBar.showsCancelButton = YES;
    _searchBar.placeholder = @"輸入關鍵字";
    self.tableView.tableHeaderView = _searchBar;
    
    _searchController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar contentsController:self];
    _searchController.delegate = self;
    //設置內部tableView的delegate
    _searchController.searchResultsDelegate = self;
    //設置內部tableview的datasource
    _searchController.searchResultsDataSource = self;
}

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

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    if (tableView == _searchController.searchResultsTableView) {
        return 1;
    }
    return [_datasource count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (tableView == _searchController.searchResultsTableView) {
        return [_filterdatasource count];
    }
    return [_datasource[section] count];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    
    if (tableView == _searchController.searchResultsTableView) {
        cell.textLabel.text = _filterdatasource[indexPath.row];
    } else {
        cell.textLabel.text = _datasource[indexPath.section][indexPath.row];
    }
    
    
    return cell;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (tableView == _searchController.searchResultsTableView) {
        return @"搜索結果";
    }
    return [NSString stringWithFormat:@"%d",section];
}

#pragma mark - Search bar controller delegate
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString{
    
    
    [_filterdatasource removeAllObjects];
    [_datasource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [obj enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSString *str = [(NSString *)obj uppercaseString];
            if ([str rangeOfString:[searchString uppercaseString]].location != NSNotFound) {
                [_filterdatasource addObject:obj];
            }
        }];
    }];
    
    return YES;
}

@end

 

下拉刷新

下拉刷新是列表最常見的一種交互,IOS平台上第三方下拉刷新控件相當多,比較常用的有egorefresh,ios6之后新添加了UIRfreshControl控件,但是只能用在UITableViewController中

- (void)setRefrshController {
    self.refreshControl = [[UIRefreshControl alloc] init];
    [self.refreshControl setAttributedTitle:[[NSAttributedString alloc] initWithString:@"下拉刷新"]];
    
    [self.refreshControl addTarget:self action:@selector(refreshViewControlEventValueChanged) forControlEvents:UIControlEventValueChanged];
}

- (void)refreshViewControlEventValueChanged {
    //模擬重新加載數據
    [NSThread sleepForTimeInterval:3];
    
    //修改refreshControl狀態
    [self.refreshControl endRefreshing];
    //更新列表
    [self.tableView reloadData];
} 

 

靜態tableviewcell

有時候我們只想簡單顯示幾行數據,這時候可以借助storyboard使用靜態單元格

1:在storyboard拖出一個UITableViewController(只有UITableViewController支持靜態單元格),並設置UITableView中tableView的相關屬性

2:點擊tableviewcell后可以隨意的設置屬性,並且把控件添加上去

 

3:運行后直接顯示我們設置的靜態列表

 

說明:使用靜態列表不需要實現代理方法和數據源方法,但是一旦實現了代理方法和數據源方法,那么列表會與我們代理和數據源方法中設置的一致


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM