iOS開發 - 最常用控制項 UITableView詳解

來源:互聯網
上載者:User

iOS開發 - 最常用控制項 UITableView詳解
UITableView掌握點

設定UITableView的dataSource、delegate
UITableView多組資料和單組資料的展示
UITableViewCell的常見屬性
UITableView的效能最佳化(cell的迴圈利用)
自訂Cell

如何展示資料

UITableView需要一個資料來源(dataSource)來顯示資料
UITableView會向資料來源查詢一共有多少行資料以及每一行顯示什麼資料等
沒有設定資料來源的UITableView只是個空殼
凡是遵守UITableViewDataSource協議的OC對象,都可以是UITableView的資料來源

UITableViewUITableViewDataSource
@property (nonatomic, assign) id  dataSource;
//調用資料來源的下面方法得知一共有多少組資料- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;//調用資料來源的下面方法得知每一組有多少行資料- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;//調用資料來源的下面方法得知每一行顯示什麼內容- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
MVC設計思想

MVC是一種設計思想,貫穿於整個iOS開發中,需要積累一定的項目經驗,才能深刻體會其中的含義和好處

MVC中的三個角色

//M:Model,模型資料//V:View,視圖(介面)//C:Control,控制中心

MVC的幾個明顯的特徵和體現:
View上面顯示什麼東西,取決於Model
只要Model資料改了,View的顯示狀態會跟著更改
Control負責初始化Model,並將Model傳遞給View去解析展示

UITableViewCell簡介

UITableView的每一行都是一個UITableViewCell,通過dataSource的tableView:cellForRowAtIndexPath:方法來初始化每一行

UITableViewCell內部有個預設的子視圖:contentView,contentView是UITableViewCell所顯示內容的父視圖,可顯示一些輔助指示視圖

輔助指示視圖的作用是顯示一個表示動作的表徵圖,可以通過設定UITableViewCell的accessoryType來顯示,預設是UITableViewCellAccessoryNone(不顯示輔助指示視圖),其他值如下:

UITableViewCellAccessoryDisclosureIndicatorUITableViewCellAccessoryDetailDisclosureButtonUITableViewCellAccessoryCheckmark

還可以通過cell的accessoryView屬性來自訂輔助指示視圖(比如往右邊放一個開關)

UITableViewCell的contentView

contentView下預設有3個子視圖
其中2個是UILabel(通過UITableViewCell的textLabel和detailTextLabel屬性訪問)
第3個是UIImageView(通過UITableViewCell的imageView屬性訪問)
UITableViewCell還有一個UITableViewCellStyle屬性,用於決定使用contentView的哪些子視圖,以及這些子視圖在contentView中的位置

UITableViewCell結構

Cell的重用原理

iOS裝置的記憶體有限,如果用UITableView顯示成千上萬條資料,就需要成千上萬個UITableViewCell對象的話,那將會耗盡iOS裝置的記憶體。要解決該問題,需要重用UITableViewCell對象

重用原理:當滾動列表時,部分UITableViewCell會移出視窗,UITableView會將視窗外的UITableViewCell放入一個對象池中,等待重用。當UITableView要求dataSource返回UITableViewCell時,dataSource會先查看這個對象池,如果池中有未使用的UITableViewCell,dataSource會用新的資料配置這個UITableViewCell,然後返回給UITableView,重新顯示到視窗中,從而避免建立新對象

還有一個非常重要的問題:有時候需要自訂UITableViewCell(用一個子類繼承UITableViewCell),而且每一行用的不一定是同一種UITableViewCell,所以一個UITableView可能擁有不同類型的UITableViewCell,對象池中也會有很多不同類型的UITableViewCell,那麼UITableView在重用UITableViewCell時可能會得到錯誤類型的UITableViewCell

解決方案:UITableViewCell有個NSString *reuseIdentifier屬性,可以在初始化UITableViewCell的時候傳入一個特定的字串標識來設定reuseIdentifier(一般用UITableViewCell的類名)。當UITableView要求dataSource返回UITableViewCell時,先通過一個字串標識到對象池中尋找對應類型的UITableViewCell對象,如果有,就重用,如果沒有,就傳入這個字串標識來初始化一個UITableViewCell對象

Cell的重用代碼
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    // 1.定義一個cell的標識      static NSString *ID = @"mjcell";    // 2.從緩衝池中取出cell      UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];    // 3.如果緩衝池中沒有cell      if (cell == nil) {        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];    }    // 4.設定cell的屬性...      return cell;}
使用xib封裝一個view的步驟建立一個xib檔案描述一個view的內部結構(假設叫做XXXCell.xib) 建立一個自訂的類 (自訂類需要繼承自系統內建的view, 繼承自哪個類, 取決於xib根對象的Class) 建立類的類名最好跟xib的檔案名稱保持一致(比如類名就叫做XXXTgCell) 將xib中的控制項 和 自訂類的.m檔案 進行連線 提供一個類方法返回一個建立好的自訂view(屏蔽從xib載入的過程) 提供一個模型屬性讓外界傳遞模型資料 重寫模型屬性的setter方法,在這裡將模型資料展示到對應的子控制項上面Delegate的使用場合
對象A內部發生了一些事情,想通知對象B對象B想監聽對象A內部發生了什麼事情對象A想在自己的方法內部調用對象B的某個方法,並且對象A不能對對象B有耦合依賴對象A想傳遞資料給對象B……以上情況,結果都一樣:對象B是對象A的代理(delegate)

先搞清楚誰是誰的代理(delegate)

定義代理協議,協議名稱的命名規範:控制項類名 + Delegate

定義代理方法
代理方法一般都定義為@optional
代理方法名都以控制項名開頭
代理方法至少有1個參數,將控制項本身傳遞出去

設定代理(delegate)對象 (比如myView.delegate = xxxx;)
代理對象遵守協議
代理對象實現協議裡面該實現的方法

在恰當的時刻調用代理對象(delegate)的代理方法,通知代理髮生了什麼事情
(在調用之前判斷代理是否實現了該代理方法)

通過代碼自訂cell(cell的高度不一致)

1.建立一個繼承自UITableViewCell的類
2.重寫initWithStyle:reuseIdentifier:方法
添加所有需要顯示的子控制項(不需要設定子控制項的資料和frame, 子控制項要添加到contentView中)
進行子控制項一次性的屬性設定(有些屬性只需要設定一次, 比如字型\固定的圖片)
3.提供2個模型
資料模型: 存放文字資料\圖片資料
frame模型: 存放資料模型\所有子控制項的frame\cell的高度
4.cell擁有一個frame模型(不要直接擁有資料模型)
5.重寫frame模型屬性的setter方法: 在這個方法中設定子控制項的顯示資料和frame
6.frame模型資料的初始化已經採取懶載入的方式(每一個cell對應的frame模型資料只載入一次)

UITableView執行個體一: 城市列表的展示

資料來源plist檔案:

#import "ViewController.h"
@interface ViewController ()@property (weak, nonatomic) IBOutlet UITableView *tableView;@property (weak, nonatomic) IBOutlet UIToolbar *toolBar;@property NSArray *provinces;@property NSArray *cities;@end
@implementation ViewController- (void)viewDidLoad{    [super viewDidLoad];    // 載入資料    NSBundle *mainbundle = [NSBundle mainBundle];    //如果類型不給,需要在resource參數中給出尾碼名    self.provinces = [NSArray arrayWithContentsOfFile:[mainbundle pathForResource:@"provinces.plist" ofType:nil]];    self.cities =[NSArray arrayWithContentsOfFile:[mainbundle pathForResource:@"cities.plist" ofType:nil]];    //處理toolBar的按鈕事件綁定    NSArray *items=self.toolBar.items;    for (UIBarButtonItem *item in items) {        if (item.title) {            NSLog(@"當前的item: %@",item.title);            [item setAction:@selector(toolBarItemClick:)];        }    }}#pragma mark -  UITableViewDataSouce 資料來源方法/** *  Section的個數,一共有多少組資料 */-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView{    return self.provinces.count;}/** *  第section組有多少行 */-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return [self.cities[section] count];}/** *  每一行顯示的內容(cell) */-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    NSLog(@"TableView來擷取內容:section:%d,row:%d",indexPath.section,indexPath.row);    UITableViewCell *cell =[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];    //取cities中的城市數組    NSArray *city = self.cities[indexPath.section];    cell.textLabel.text =city[indexPath.row];    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];    //如果當前的cell是被選中的,則設定其選中的accessoryType    NSArray *selpaths =[tableView indexPathsForSelectedRows];    if ([selpaths containsObject:indexPath]) {        cell.accessoryType = UITableViewCellAccessoryCheckmark;    }    return cell;}/** *  添加索引 */- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{    return self.provinces;}/** *  顯示第section組的頭部標題 */-(NSString*) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{    return self.provinces[section];//每個省的名稱}#pragma mark - UITableViewDelegate 代理方法/** *  即將被選中時調用 */-(NSIndexPath *) tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath{    NSLog(@"section:%d,row:%d即將被選中",indexPath.section,indexPath.row);    return indexPath;}/** *  當前行已經被選中時調用 */-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    NSLog(@"section:%d,row:%d已經被選中",indexPath.section,indexPath.row);    //設定其accessoryType 為CheckMark    UITableViewCell *cell=[tableView cellForRowAtIndexPath:indexPath];    [cell setAccessoryType:UITableViewCellAccessoryCheckmark];}/** *  當前行即將被取消選中時調用 */-(NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath{    NSLog(@"section:%d,row:%d即將被取消被選中",indexPath.section,indexPath.row);    return indexPath;}/** *  當前行被取消選中時調用 */-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];    [cell setAccessoryType:UITableViewCellAccessoryNone];    NSLog(@"section:%d,row:%d已經被取消被選中",indexPath.section,indexPath.row);}#pragma mark - 事件方法/** *  點擊按鈕 */- (void)toolBarItemClick:(UIBarButtonItem *)sender{    NSLog(@"按鈕: %@被點擊了",sender.title);    if (sender.tag==3) {        NSArray * selIndexLst = [self.tableView indexPathsForSelectedRows];        NSLog(@"selIndex:%@",selIndexLst);        return;    }}@end


UITableView執行個體二: 單組資料模型展示

資料來源plist檔案:

//模型類#import @interface Hero : NSObject@property(nonatomic,copy) NSString * name;@property(nonatomic,copy) NSString * icon;@property(nonatomic,copy) NSString * intro;+ (instancetype)heroWithDict:(NSDictionary *)dict;- (instancetype)initWithDict:(NSDictionary *)dict;@end
#import "Hero.h"@implementation Hero+ (instancetype)heroWithDict:(NSDictionary *)dict{    return [[self alloc]initWithDict:dict];}- (instancetype)initWithDict:(NSDictionary *)dict{    if (self==[super init]) {        [self setValuesForKeysWithDictionary:dict];    }    return self;}@end
#import "ViewController.h"#import "Hero.h"@interface ViewController ()@property(nonatomic,strong) NSArray* heros;@property (weak, nonatomic) IBOutlet UITableView *tableView;@end
@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];}/** *  隱藏標題列 */- (BOOL)prefersStatusBarHidden{    return YES;}//初始化- (NSArray *)heros{    if (_heros==nil) {        //1.獲得plist的全路徑        NSString * path=[[NSBundle mainBundle]pathForResource:@"heros.plist" ofType:nil];        //2.載入數組        NSArray * dictArray=[NSArray arrayWithContentsOfFile:path];        //3.將dictArray裡面的所有字典轉成模型對象,放到新的數組中        NSMutableArray *heroArray=[NSMutableArray array];        for (NSDictionary *dict in dictArray) {            //3.1建立模型對象            Hero *hero=[Hero heroWithDict:dict];            //3.2添加模型對象到數組中            [heroArray addObject:hero];        }        //4.賦值        _heros=heroArray;    }    return _heros;}#pragma mark - 資料來源方法- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return self.heros.count;}/** *  知識點一: cell的效能最佳化 * 1.通過一個標識去緩衝池中尋找可迴圈利用的cell * 2.如果緩衝池找不到可迴圈利用的cell,就會建立一個新的cell,給cell貼個標識 * 3.給cell設定新的資料 *//** *  每當有一個cell進入視野範圍內,就會調用 */- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    //static修飾局部變數:可以保證局部變數只分配一次儲存空間(只初始化一次)    static NSString * ID=@"hero";    //1.通過一個標識去緩衝池中尋找可迴圈利用的cell,dequeue:出列(尋找)    UITableViewCell * cell=[tableView dequeueReusableCellWithIdentifier:ID];    //2.如果沒有可迴圈利用cell    if (cell==nil) {        cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];        NSLog(@"------緩衝池中找不到,所以建立了cell- %ld",(long)indexPath.row);    }    //3.取出模型    Hero *hero=self.heros[indexPath.row];    //設定cell的資料    cell.textLabel.text=hero.name;    cell.detailTextLabel.text=hero.intro;    cell.imageView.image=[UIImage imageNamed:hero.icon];    //設定cell右邊指標的類型    cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;    //設定圖片背景    UIImageView *bgView=[[UIImageView alloc]init];    bgView.image=[UIImage imageNamed:@"buttondelete"];    cell.backgroundView=bgView;    //設定選中背景    UIView *selectedbgView=[[UIView alloc]init];    selectedbgView.backgroundColor=[UIColor greenColor];    cell.selectedBackgroundView=selectedbgView;    return cell;}#pragma mark - 代理方法//控制與狀態列之間的高度- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{    return 60;}- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    //1.取得被點擊這行對應的模型    Hero *hero=self.heros[indexPath.row];    //彈框    UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"資料展示" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil];    //設定對話方塊的類型    alert.alertViewStyle=UIAlertViewStylePlainTextInput;    //取得唯一的那個文字框,顯示英雄的名稱    [alert textFieldAtIndex:0].text=hero.name;    [alert show];    //綁定行號到alertView上    alert.tag=indexPath.row;}//- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath//{//    // Deselect : 取消選中//    NSLog(@"取消選中了第%d行", indexPath.row);//}#pragma mark - alertView的代理方法- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{    if (buttonIndex==0) return;    //1.取得文字框最後的文字    NSString *name=[alertView textFieldAtIndex:0].text;    //2.修改模型屬性    int row=alertView.tag;    Hero *hero=self.heros[row];    hero.name=name;    /**     * 知識點二: 資料的重新整理     *  reloadData:tableView會向資料來源重新請求資料,重新調用資料來源的相應方法取得資料     * 重新調用資料來源的tableView:numberOfRowsInSection:獲得行數     * 重新調用資料來源的tableView:cellForRowAtIndexPath:得知每一行顯示怎樣的cell     */    //3.讓tableView重新載入模型資料    //全部重新整理    //[self.tableView reloadData];    //局部重新整理    NSIndexPath *path=[NSIndexPath indexPathForItem:row inSection:0];    [self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationBottom];}@end


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.