iOS控制元件——UITableView詳解
iOS開發中經常會用到UITableView,我們平時使用的軟體中到處都可以看到它,比如微信、QQ、微博等軟體基本上隨處都是UITableView。最主要到還有iOS設定。
一 基本介紹
UITableView有兩種Style:UITableViewStylePlain和UITableViewStyleGrouped。從名字上可以看出:一個是普通樣式的,另一個是分組樣式的。具體上怎樣,可以看一下下面的圖片。
普通樣式(不分組) 分組樣式:
UITableView只有行沒有列,每一行都是一個UITableViewCell,如果我們檢視UITableViewCell的宣告檔案可以發現在內部有一個UIView控制元件(contentView,作為其他元素的父控制元件)、兩個UILable控制元件(textLabel
detailTextLabel)、一個UIImage控制元件(imageView),分別用於容器、顯示內容、詳情和圖片。這些控制元件並不一定要全部顯示,可以根據需要設定。
這是UITalbeViewCell的四種style。typedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, // Simple cell with text label and optional image view (behavior of UITableViewCell in iPhoneOS 2.x) UITableViewCellStyleValue1, // Left aligned label on left and right aligned label on right with blue text (Used in Settings) UITableViewCellStyleValue2, // Right aligned label on left with blue text and left aligned label on right (Used in Phone/Contacts) UITableViewCellStyleSubtitle // Left aligned label on top and left aligned label on bottom with gray text (Used in iPod). };
二 資料來源
UITableView需要一個數據源(dataSource)來顯示資料,UITableView會向資料來源查詢一共有多少行資料以及每一行顯示什麼資料等。沒有設定資料來源的UITableView只是個空殼。凡是遵守UITableViewDataSource協議的OC物件,都可以是UITableView的資料來源。
首先將UITableView對資料來源和View controller相連。如圖所示:
並且讓這個ViewController這個類實現UITableViewDataSource協議。
#import <UIKit/UIKit.h> @interface ViewController : UIViewController <UITableViewDataSource,UITableViewDelegate> @end
先看一下UITableViewDataSource協議:
@protocol UITableViewDataSource<NSObject>
@required //必須要實現的
第section分割槽一共有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
//建立第section分割槽第row行的UITableViewCell物件(indexPath包含了section和row)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
@optional //可選擇實現的
// 一共有多少個分割槽
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
//第section分割槽的頭部標題
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
//第section分割槽的底部標題
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
//某一行是否可以編輯(刪除)
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
//某一行是否可以移動來進行重新排序
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;
UITableView右邊的索引欄的內容
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView;
這些只是UITableView的顯示上面的設定,如果要做一些操作僅這些是不夠的。。。
三 代理(delegate)
通常都要為UITableView設定代理物件(delegate),以便在UITableView觸發一下事件時做出相應的處理,比如選中了某一行。凡是遵守了UITableViewDelegate協議的OC物件,都可以是UITableView的代理物件。一般會讓控制器充當UITableView的dataSource和delegate。
同樣需要將UITableView的delegate與UIViewController關聯起來。
看一些UITableViewDelegate協議的一些常用方法。
@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>
@optional
//選中了UITableView的某一行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
//某一行的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
//第section分割槽頭部的高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
//第section分割槽尾部的高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
//第section分割槽頭部顯示的檢視
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
//第section分割槽尾部顯示的檢視
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
//設定每一行的等級縮排(數字越小,等級越高)
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
四 程式碼示例
先看一下最基本的UITableView。
#pragma mark 這一組裡面有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 9;
}
#pragma mark 返回第indexPath這行對應的內容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
/*
四種style,分別是
Default : 不顯示detailTextLabel
Value1 : 在右邊顯示detailTextLabel
Value2 : 不顯示圖片,顯示detailTextLabel
Subtitle : 在底部顯示detailTextLabel
*/
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
cell.textLabel.text = [NSString stringWithFormat:@"金克斯-%ld", indexPath.row];
return cell;
}
通過設定:UITableViewCell的detailTextLabel和imgName的兩個屬性。
#pragma mark 返回第indexPath這行對應的內容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
cell.textLabel.text = [NSString stringWithFormat:@"金克斯-%ld", indexPath.row];
// 設定詳情文字
cell.detailTextLabel.text = [NSString stringWithFormat:@"英雄-%ld非常好玩!!!!!", indexPath.row];
// 設定圖片
NSString *imgName = @"jinkesi.png";//[NSString stringWithFormat:@"00%d.png", indexPath.row + 1];
cell.imageView.image = [UIImage imageNamed:imgName];
return cell;
}
上面的圖片中等內容看起來不是太好看。並且程式碼資料也有限制。我們做一些修改。#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_rowLanguage = [[NSMutableArray alloc] initWithObjects:NSLocalizedString(@"str_language_itemTitle_zh", nil)
,NSLocalizedString(@"str_language_itemTitle_en", nil)
,NSLocalizedString(@"str_language_itemTitle_ar", nil)
,NSLocalizedString(@"str_language_itemTitle_de", nil)
,NSLocalizedString(@"str_language_itemTitle_es", nil)
,NSLocalizedString(@"str_language_itemTitle_fa", nil)
,NSLocalizedString(@"str_language_itemTitle_fr", nil)
,NSLocalizedString(@"str_language_itemTitle_it", nil)
,NSLocalizedString(@"str_language_itemTitle_pt", nil)
,NSLocalizedString(@"str_language_itemTitle_ru", nil)
,NSLocalizedString(@"str_language_itemTitle_th", nil)
,nil];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
#pragma mark 這一組裡面有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 9;
}
#pragma mark 返回第indexPath這行對應的內容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
cell.textLabel.text = [_rowLanguage objectAtIndex:indexPath.row];
return cell;
}
現在雖然內容改起來水方便了點,但還是有一定的問題。不易於新增照片。
將資料使用模型封裝資料。建立模型類Heros
Heros.hs檔案
#import <Foundation/Foundation.h>
@interface Heros : NSObject
@property (nonatomic, copy) NSString *name; //名字
@property (nonatomic, copy) NSString *icon;//圖片
@property (nonatomic, copy) NSString *desc;//描述
+(Heros *)initWithName:(NSString *)name andicon:(NSString *)icon anddees:(NSString *)desc;
@end
Heros.m檔案#import "Heros.h"
@implementation Heros
//初始化方法
+(Heros *)initWithName:(NSString *)name andicon:(NSString *)icon anddees:(NSString *)desc{
Heros *hero = [[Heros alloc]init];
hero.name = name;
hero.icon = icon;
hero.desc = desc;
return hero;
}
@end
ViewController.m檔案#import "ViewController.h"
#import "Heros.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Heros *heros1 = [Heros initWithName:@"薇恩" andicon:@"vn.png" anddees:@"讓我們來獵殺那些陷入黑暗中的人吧。"];
Heros *heros2 = [Heros initWithName:@"男槍" andicon:@"nanqian.png" anddees:@"我與死亡同行。"];
Heros *heros3 = [Heros initWithName:@"賞金" andicon:@"shangjin.png" anddees:@"好運,不會眷顧傻瓜。"];
Heros *heros4 = [Heros initWithName:@"奎因" andicon:@"kuiyin.png" anddees:@"正義,展翅翱翔。"];
Heros *heros5 = [Heros initWithName:@"金克斯" andicon:@"jinkesi.png" anddees:@"規則,就是用來打破的。"];
Heros *heros6 = [Heros initWithName:@"奧巴馬" andicon:@"luxian.png" anddees:@"人終有一死,可有些人需要一點小小的幫助。"];
Heros *heros7 = [Heros initWithName:@"希維爾" andicon:@"xiweier.png" anddees:@"你有麻煩了,我有錢賺。"];
Heros *heros8 = [Heros initWithName:@"伊澤瑞爾" andicon:@"yizeruier.png" anddees:@"是時候表演真正的技術啦。"];
Heros *heros9 = [Heros initWithName:@"復仇之矛" andicon:@"fuchouzhi.png" anddees:@"我們的誓約,以血為契。"];
_arrayHeros = [[NSMutableArray alloc]initWithObjects:heros1,heros2,heros3,heros4,heros5,heros6,heros7,heros8,heros9, nil];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
#pragma mark 這一組裡面有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _arrayHeros.count;
}
#pragma mark 返回第indexPath這行對應的內容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
Heros *he = [_arrayHeros objectAtIndex:indexPath.row];
cell.textLabel.text = he.name; //設定名字
cell.detailTextLabel.text = he.desc; //設定描述
cell.imageView.image =[UIImage imageNamed:he.icon]; //設定圖片
return cell;
}
#pragma mark - 代理方法
#pragma mark 返回indexPath這行cell的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 45;
}
#pragma mark 選中一行響應事件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
@end
這樣就可以將資料和UITableView的設定分離開來,以後再加其他英雄也不用動下面的程式碼,只需要改_arrayHeros就可以了。
五 UITableViewCell
UITableView的每一行都是一個UITableViewCell,通過dataSource的tableView:cellForRowAtIndexPath:方法來初始化每一行
UITableViewCell是UIView的子類,內部有個預設的子檢視:contentView。contentView是UITableViewCell所顯示內容的父檢視,並負責顯示一些輔助指示檢視。輔助指示檢視的作用是顯示一個表示動作的圖示,可以通過設定UITableViewCell的accessoryType來顯示,預設是UITableViewCellAccessoryNone。
typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {
UITableViewCellAccessoryNone, // don't show any accessory view
UITableViewCellAccessoryDisclosureIndicator, // regular chevron. doesn't track
UITableViewCellAccessoryDetailDisclosureButton __TVOS_PROHIBITED, // info button w/ chevron. tracks
UITableViewCellAccessoryCheckmark, // checkmark. doesn't track
UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED // info button. tracks
};
檢視對應顯示。
#pragma mark 返回第indexPath這行對應的內容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
Heros *he = [_arrayHeros objectAtIndex:indexPath.row];
cell.textLabel.text = he.name; //設定名字
cell.detailTextLabel.text = he.desc; //設定描述
cell.imageView.image =[UIImage imageNamed:he.icon]; //設定圖片
if(indexPath.row == 0 ||indexPath.row ==1)
cell.accessoryType = UITableViewCellAccessoryNone;
else if (indexPath.row == 2 || indexPath.row == 3)
cell.accessoryType = UITableViewCellAccessoryCheckmark;
else if (indexPath.row == 4 || indexPath.row == 5)
cell.accessoryType = UITableViewCellAccessoryDetailButton;
else if(indexPath.row == 6 || indexPath.row == 7)
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
else
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;//有倆圖示。
return cell;
}
設定cell每行的背景顏色,通過cell的backgroundColor設定。
#pragma mark 返回第indexPath這行對應的內容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
Heros *he = [_arrayHeros objectAtIndex:indexPath.row];
cell.textLabel.text = he.name; //設定名字
cell.detailTextLabel.text = he.desc; //設定描述
cell.imageView.image =[UIImage imageNamed:he.icon]; //設定圖片
if(indexPath.row == 0 ||indexPath.row ==1){
cell.accessoryType = UITableViewCellAccessoryNone;
cell.backgroundColor = [UIColor blueColor];
}
else if (indexPath.row == 2 || indexPath.row == 3){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.backgroundColor = [UIColor yellowColor];
}
else if (indexPath.row == 4 || indexPath.row == 5){
cell.accessoryType = UITableViewCellAccessoryDetailButton;
cell.backgroundColor = [UIColor redColor];
}
else if(indexPath.row == 6 || indexPath.row == 7){
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.backgroundColor = [UIColor purpleColor];
}
else{
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;//有倆圖示。
cell.backgroundColor = [UIColor brownColor];
}
return cell;
}
設定背景
backgroundView cell.backgroundView
設定被選中時的背景檢視
selectedBackgroundView cell.selectedBackgroundView
selectionStyle屬性可設定UITableViewCell被選中時的背景顏色:
UITableViewCellSelectionStyleNone 沒有顏色
UITableViewCellSelectionStyleBlue 藍色(預設)
UITableViewCellSelectionStyleGray 灰色
UITableViewCell還有許多設定屬性,需要的可以看一下原始碼。
設定每行的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 40+3*index.row;
}
效果圖:
設定內容縮排
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath{
return indexPath.row;
}
效果:
選中一行響應事件,我們設定讓其彈一個對話方塊,顯示點選行的名字。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
message:nil
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"OK",nil];
alert.title = [[_arrayHeros objectAtIndex:indexPath.row] name];
[alert show];//顯示對話方塊
}
有時我們會想更改內容,這就需要重新整理我們的UITableView了。
[_tableView reloadData]; // 整體重新整理(每一行都會重新整理)
//只重新整理某行。
[_tableView reloadRowsAtIndexPaths:indexPath withRowAnimation:UITableViewRowAnimationLeft];
我們只需要在相遇重新整理的地方呼叫這個就可以重新載入UITableViewCell了。
六 分組UITableView
右側索引條,可以直接調整到對應位置。
程式碼如下:
HeroGroup.h
#import <Foundation/Foundation.h>
@interface HeroGroup : NSObject
//頭部
@property (nonatomic, copy) NSString *header;
//尾部
@property (nonatomic, copy) NSString *footer;
//名字
@property (nonatomic, copy) NSArray *names;
//圖示
@property (nonatomic, copy) NSArray *icons;
+ (HeroGroup *)heroGroupWithHeader:(NSString *)header footer:(NSString *)footer names:(NSArray *)names icons:(NSArray *)icons;
@end
HeroGroup.m檔案
#import "HeroGroup.h"
@implementation HeroGroup
+ (HeroGroup *)heroGroupWithHeader:(NSString *)header footer:(NSString *)footer names:(NSArray *)names icons:(NSArray *)icons{
HeroGroup *hg = [[HeroGroup alloc] init];
hg.header = header;
hg.footer = footer;
hg.names = names;
hg.icons = icons;
return hg;
}
@end
GroupTabViewController.h檔案
#import <UIKit/UIKit.h>
@interface GroupTabViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>{
UITableView *_tableView;
NSArray *_heroAry;
}
@end
GroupTabViewController.m檔案
#import "GroupTabViewController.h"
#import "HeroGroup.h"
@interface GroupTabViewController ()
@end
@implementation GroupTabViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGRect frame = self.view.bounds;
frame.origin.y = 20; //設定y座標起始位置,否則會和狀態列重合。
_tableView = [[UITableView alloc]initWithFrame:frame style:UITableViewStyleGrouped];
_tableView.dataSource = self;
_tableView.delegate = self;
[self.view addSubview:_tableView];
[self initData];
}
#pragma mark 初始化資料。
- (void)initData {
// HeroGroup hg = [HeroGroup heroGroupWithHeader:(NSString *)header footer:(NSString *)footer names:(NSArray *)names icons:(NSArray *)icons;
HeroGroup *top = [HeroGroup heroGroupWithHeader:@"上單" footer:@"上單很猛" names:@[@"劍姬",@"武器",@"人馬"] icons:@[@"jianji.png",@"wuqi.png",@"renma.png"]];
HeroGroup *jungle = [HeroGroup heroGroupWithHeader:@"打野" footer:@"打野帶動節奏" names:@[@"盲僧",@"蜘蛛",@"挖掘機"] icons:@[@"mangseng.png",@"zhizhu.png",@"wajueji.png"]];
HeroGroup *mid = [HeroGroup heroGroupWithHeader:@"中單" footer:@"中單爆發" names:@[@"妖姬",@"卡牌",@"發條"] icons:@[@"yaoji.png",@"kapai.png",@"fatiao.png"]];
HeroGroup *adc = [HeroGroup heroGroupWithHeader:@"ADC" footer:@"ADC持續輸出" names:@[@"薇恩",@"金克絲"] icons:@[@"vn.png",@"jinkesi.png"]];
HeroGroup *support = [HeroGroup heroGroupWithHeader:@"輔助" footer:@"輔助很肉" names:@[@"牛頭",@"石頭人"] icons:@[@"niutou.png",@"shitouren.png"]];
_heroAry = @[top,jungle,mid,adc,support];
}
#pragma mark 返回分組數
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return _heroAry.count;
}
#pragma mark 返回每組行數
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
HeroGroup *hg = _heroAry[section];
return hg.names.count;
}
#pragma mark 返回每行的單元格
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
HeroGroup *hg = _heroAry[indexPath.section];
UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text=[hg.names objectAtIndex:indexPath.row];
cell.imageView.image =[UIImage imageNamed:hg.icons[indexPath.row]];
return cell;
}
#pragma mark 返回每組頭標題名稱
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
HeroGroup *hg = _heroAry[section];
return hg.header;
}
#pragma mark 返回每組尾部說明
-(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{
HeroGroup *hg = _heroAry[section];
return hg.footer;
}
#pragma mark 返回每組標題索引
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
NSMutableArray *indexs=[[NSMutableArray alloc]init];
for(HeroGroup *hg in _heroAry){
[indexs addObject:hg.header];
}
return indexs;
}
#pragma mark - 代理方法
#pragma mark 設定分組頭部內容高度
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 40;
}
#pragma mark 設定每行高度(每行高度可以不一樣)
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 45;
}
#pragma mark 設定尾部說明內容高度
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return 40;
}
@end