天天看點

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

在ios開發中uitableview可以說是使用最廣泛的控件,我們平時使用的軟體中到處都可以看到它的影子,類似于微信、qq、新浪微網誌等軟體基本上随處都是uitableview。當然它的廣泛使用自然離不開它強大的功能,今天這篇文章将針對uitableview重點展開讨論。今天的主要内容包括:

<a href="http://www.cnblogs.com/kenshincui/p/3931948.html#introduction">基本介紹</a>

<a href="http://www.cnblogs.com/kenshincui/p/3931948.html#datasource">資料源</a>

<a href="http://www.cnblogs.com/kenshincui/p/3931948.html#delegate">代理</a>

<a href="http://www.cnblogs.com/kenshincui/p/3931948.html#performance">性能優化</a>

<a href="http://www.cnblogs.com/kenshincui/p/3931948.html#uitableviewcell">uitableviewcell</a>

<a href="http://www.cnblogs.com/kenshincui/p/3931948.html#operate">常用操作</a>

<a href="http://www.cnblogs.com/kenshincui/p/3931948.html#uitableviewcontroller">uitableviewcontroller</a>

<a href="http://www.cnblogs.com/kenshincui/p/3931948.html#mvc">mvc模式</a>

uitableview有兩種風格:uitableviewstyleplain和uitableviewstylegrouped。這兩者操作起來其實并沒有本質差別,隻是後者按分組樣式顯示前者按照普通樣式顯示而已。大家先看一下兩者的應用:

1&gt;分組樣式

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

2&gt;不分組樣式

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

大家可以看到在uitableview中資料隻有行的概念,并沒有列的概念,因為在手機作業系統中顯示多列是不利于操作的。uitableview中每行資料都是一個uitableviewcell,在這個控件中為了顯示更多的資訊,ios已經在其内部設定好了多個子控件以供開發者使用。如果我們檢視uitableviewcell的聲明檔案可以發現在内部有一個uiview控件(contentview,作為其他元素的父控件)、兩個uilable控件(textlabel、detailtextlabel)、一個uiimage控件(imageview),分别用于容器、顯示内容、詳情和圖檔。使用效果類似于微信、qq資訊清單:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

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

由于ios是遵循mvc模式設計的,很多操作都是通過代理和外界溝通的,但對于資料源控件除了代理還有一個資料源屬性,通過它和外界進行資料互動。 對于uitableview設定完datasource後需要實作uitableviewdatasource協定,在這個協定中定義了多種 資料操作方法,下面通過建立一個簡單的聯系人管理進行示範:

首先我們需要建立一個聯系人模型kccontact

kccontact.h

kccontact.m

為了示範分組顯示我們不妨将一組資料也抽象成模型kccontactgroup

kccontactgroup.h

kccontactgroup.m

然後在viewdidload方法中建立一些模拟資料同時實作資料源協定方法:

kcmainviewcontroller.m

運作可以看到如下效果:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

大家在使用iphone通訊錄時會發現右側可以按字母檢索,使用起來很友善,其實這個功能使用uitableview實作很簡單,隻要實作資料源協定的一個方法,建構一個分組标題的數組即可實作。數組元素的内容群組标題内容未必完全一緻,uitableview是按照數組元素的索引和每組資料索引順序來定位的而不是按内容查找。

效果如下:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

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

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

值得指出的是生成單元格的方法并不是一次全部調用,而是隻會生産目前顯示在界面上的單元格,當使用者滾動操作時再顯示其他單元格。

注意:随着我們的應用越來越複雜,可能經常需要調試程式,在ios中預設情況下不能定位到錯誤代碼行,我們可以通過如下設定讓程式定位到出錯代碼行:show the breakpoint  navigator—add exception breakpoint。

上面我們已經看到通訊錄的簡單實作,但是我們發現單元格高度、分組标題高度以及尾部說明的高度都需要調整,此時就需要使用代理方法。uitableview代理方法有很多,例如監聽單元格顯示周期、監聽單元格選擇編輯操作、設定是否高亮顯示單元格、設定行高等。

在ios中點選某聯系個人就可以呼叫這個聯系人,這時就需要監聽點選操作,這裡就不示範呼叫聯系人操作了,我們示範一下修改人員資訊的操作。

kcmainviewcontrller.m

在上面的代碼中我們通過修改模型來改變ui顯示,這種方式是經典的mvc應用,在後面的代碼中會經常看到。當然ui的重新整理使用了uitableview的reloaddata方法,該方法會重新調用資料源方法,包括計算分組、計算每個分組的行數,生成單元格等重新整理整個uitableview。當然這種方式在實際開發中是不可取的,我們不可能因為修改了一個人的資訊就重新整理整個uitableviewview,此時我們需要采用局部重新整理。局部重新整理使用起來很簡單,隻需要調用uitableview的另外一個方法:

前面已經說過uitableview中的單元格cell是在顯示到使用者可視區域後建立的,那麼如果使用者往下滾動就會繼續建立顯示在螢幕上的單元格,如果使用者向上滾動傳回到檢視過的内容時同樣會重新建立之前已經建立過的單元格。如此一來即使uitableview的内容不是太多,如果使用者反複的上下滾動,記憶體也會瞬間飙升,更何況很多時候uitableview的内容是很多的(例如微網誌展示清單,基本向下滾動是沒有底限的)。

前面一節中我們曾經提到過如何優化uiscrollview,當時就是利用有限的uiimageview動态切換其内容來盡可能減少資源占用。同樣的,在uitableview中也可以采用類似的方式,隻是這時我們不是在滾動到指定位置後更改滾動的位置而是要将目前沒有顯示的cell重新顯示在将要顯示的cell的位置然後更新其内容。原因就是uitableview中的cell結構布局可能是不同的,通過重新定位是不可取的,而是需要重用已經不再界面顯示的已建立過的cell。

當然,聽起來這麼做比較複雜,其實實作起來很簡單,因為uitableview已經為我們實作了這種機制。在uitableview内部有一個緩存池,初始化時使用initwithstyle:(uitableviewcellstyle) reuseidentifier:(nsstring *)方法指定一個可重用辨別,就可以将這個cell放到緩存池。然後在使用時使用指定的辨別去緩存池中取得對應的cell然後修改cell内容即可。

上面的代碼中已經列印了cell的位址,如果大家運作測試上下滾動uitableview會發現滾動時建立的cell位址是初始化時已經建立的。

這裡再次給大家強調兩點:

-(uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)方法調用很頻繁,無論是初始化、上下滾動、重新整理都會調用此方法,所有在這裡執行的操作一定要注意性能;

可重用辨別可以有多個,如果在uitableview中有多類結構不同的cell,可以通過這個辨別進行緩存和重新;

uitableviewcell是建構一個uitableview的基礎,在uitableviewcell内部有一個uiview控件作為其他内容的容器,它上面有一個uiimageview和兩個uilabel,通過uitableviewcellstyle屬性可以對其樣式進行控制。其結構如下:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

有時候我們會發現很多uitableviewcell右側可以顯示不同的圖示,在ios中稱之為通路器,點選可以觸發不同的事件,例如設定功能:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

要設定這些圖示隻需要設定uitableviewcell的accesorytype屬性,這是一個枚舉類型具體含義如下:

例如在最近通話中我們通常設定為詳情圖示,點選可以檢視聯系人詳情:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

很明顯ios設定中第一個accessorytype不在枚舉之列,右側的通路器類型是uiswitch控件,那麼如何顯示自定義的通路器呢?其實隻要設定uitableviewcell的accessoryview即可,它支援任何uiview控件。假設我們在通訊錄每組第一行放一個uiswitch,同時切換時可以輸出對應資訊:

最終運作效果:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

注意:

由于此時我們需要兩種uitableviewcell樣式,考慮到性能我們需要在緩存池緩存兩種cell。

uiswitch繼承于uicontrol而不是uiview(當然uicontrol最終也是繼承于uiview),繼承于uicontrol的控件使用addtarget添加對應事件而不是代理,同時有“是否可用”、“是否高亮”、“是否選中”等屬性;

上面代碼中如果有些uitableviewcell的uiswitch設定為on當其他控件重用時狀态也是on,解決這個問題可以在模型中設定對應的屬性記錄其狀态,在生成cell時設定目前狀态(為了盡可能簡化上面的代碼這裡就不再修複這個問題);

雖然系統自帶的uitableviewcell已經夠強大了,但是很多時候這并不能滿足我們的需求。例如新浪微網誌的cell就沒有那麼簡單:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

沒錯,這個界面布局也是uitableview實作的,其中的内容就是uitableviewcell,隻是這個uitableviewcell是使用者自定義實作的。當然要實作上面的uitableviewcell三言兩語我們是說不完的,這裡我們實作一個簡化版本,界面原型如下:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

我們對具體控件進行拆分:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

在這個界面中有2個uiimageview控件和4個uilabel,整個界面顯示效果類似于新浪微網誌的消息内容界面,但是又在新浪微網誌基礎上進行了精簡以至于利用現有知識能夠順利開發出來。

在前面的内容中我們的資料都是手動建構的,在實際開發中自然不會這麼做,這裡我們不妨将微網誌資料存儲到plist檔案中然後從plist檔案讀取資料構模組化型對象(實際開發微網誌當然需要進行網絡資料請求,這裡隻是進行模拟就不再示範網絡請求的内容)。假設plist檔案内容如下:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

接下來就定義一個kcstatustableviewcell實作uitableviewcell,一般實作自定義uitableviewcell需要分為兩步:第一初始化控件;第二設定資料,重新設定控件frame。原因就是自定義cell一般無法固定高度,很多時候高度需要随着内容改變。此外由于在單元格内部是無法控制單元格高度的,是以一般會定義一個高度屬性用于在uitableview的代理事件中設定每個單元格高度。

1.首先看一下微網誌模型kcstatus,這個模型主要的方法就是根據plist字典内容生成微網誌對象:

kcstatus.h

kcstatus.m

2.然後看一下自定義的cell

kcstatustableviewcell.h

kcstatustableviewcell.m

這是我們自定義cell這個例子的核心,自定義cell分為兩個步驟:首先要進行各種控件的初始化工作,這個過程中隻要将控件放到cell的view中同時設定控件顯示内容的格式(字型大小、顔色等)即可;然後在資料對象設定方法中進行各個控件的布局(大小、位置)。在代碼中有幾點需要重點提示大家:

對于單行文本資料的顯示調用- (cgsize)sizewithattributes:(nsdictionary *)attrs;方法來得到文本寬度和高度。

對于多行文本資料的顯示調用- (cgrect)boundingrectwithsize:(cgsize)size options:(nsstringdrawingoptions)options attributes:(nsdictionary *)attributes context:(nsstringdrawingcontext *)context ;方法來得到文本寬度和高度;同時注意在此之前需要設定文本控件的numberoflines屬性為0。

通常我們會在自定義cell中設定一個高度屬性,用于外界方法調用,因為cell内部設定cell的高度是沒有用的,uitableviewcell在初始化時會重新設定高度。

3.最後我們看一下自定義cell的使用過程:

kcstatusviewcontroller.m

這個類中需要重點強調一下:cell的高度需要重新設定(前面說過無論cell内部設定多高都沒有用,需要重新設定),這裡采用的方法是首先建立對應的cell,然後在- (cgfloat)tableview:(uitableview *)tableview heightforrowatindexpath:(nsindexpath *)indexpath;方法中設定微網誌資料計算高度通知uitableview。

最後我們看一下運作的效果:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

uitableview和uitableviewcell提供了強大的操作功能,這一節中會重點讨論删除、增加、排序等操作。為了友善示範我們還是在之前的通訊錄的基礎上示範,在此之前先來給視圖控制器添加一個工具條,在工具條左側放一個删除按鈕,右側放一個添加按鈕:

在uitableview中無論是删除操作還是添加操作都是通過修改uitableview的編輯狀态來改變的(除非你不用uitableview自帶的删除功能)。在删除按鈕中我們設定uitableview的編輯狀态:

點選删除按鈕會在cell的左側顯示删除按鈕:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

此時點選左側删除圖示右側出現删除:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

用過ios的朋友都知道,一般這種cell如果向左滑動右側就會出現删除按鈕直接删除就可以了。其實實作這個功能隻要實作代理-(void)tableview:(uitableview *)tableview commiteditingstyle:(uitableviewcelleditingstyle)editingstyle forrowatindexpath:(nsindexpath *)indexpath;方法,隻要實作了此方法向左滑動就會顯示删除按鈕。隻要點選删除按鈕這個方法就會調用,但是需要注意的是無論是删除還是添加都是執行這個方法,隻是第二個參數類型不同。下面看一下具體的删除實作:

從這段代碼我們再次看到了mvc的思想,要修改ui先修改資料。而且我們看到了另一個重新整理表格的方法- (void)deleterowsatindexpaths:(nsarray *)indexpaths withrowanimation:(uitableviewrowanimation)animation;,使用這個方法可以再删除之後重新整理對應的單元格。效果如下:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

添加和删除操作都是設定uitableview的編輯狀态,具體是添加還是删除需要根據代理方法-(uitableviewcelleditingstyle)tableview:(uitableview *)tableview editingstyleforrowatindexpath:(nsindexpath *)indexpath;的傳回值來确定。是以這裡我們定義一個變量來記錄點選了哪個按鈕,根據點選按鈕的不同在這個方法中傳回不同的值。

運作效果:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

隻要實作-(void)tableview:(uitableview *)tableview moverowatindexpath:(nsindexpath *)sourceindexpath toindexpath:(nsindexpath *)destinationindexpath;代理方法當uitableview處于編輯狀态時就可以排序。

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

最後給大家附上上面幾種操作的完整代碼:

通過前面的示範這裡簡單總結一些uitableview的重新整理方法:

- (void)reloaddata;重新整理整個表格。

- (void)reloadrowsatindexpaths:(nsarray *)indexpaths withrowanimation:(uitableviewrowanimation)animation ns_available_ios(3_0);重新整理指定的分組和行。

- (void)reloadsections:(nsindexset *)sections withrowanimation:(uitableviewrowanimation)animation ns_available_ios(3_0);重新整理指定的分組。

- (void)deleterowsatindexpaths:(nsarray *)indexpaths withrowanimation:(uitableviewrowanimation)animation;删除時重新整理指定的行資料。

- (void)insertrowsatindexpaths:(nsarray *)indexpaths withrowanimation:(uitableviewrowanimation)animation;添加時重新整理指定的行資料。

很多時候一個uiviewcontroller中隻有一個uitableview,是以蘋果官方為了友善大家開發直接提供了一個uitableviewcontroller,這個控制器 uitableviewcontroller實作了uitableview資料源和代理協定,内部定義了一個tableview屬性供外部通路,同時自動鋪滿整個螢幕、自動伸縮以友善我們的開發。當然uitableviewcontroller也并不是簡單的幫我們定義完uitableview并且設定了資料源、代理而已,它還有其他強大的功能,例如重新整理控件、滾動過程中固定分組标題等。

有時候一個表格中的資料特别多,檢索起來就顯得麻煩,這個時候可以實作一個搜尋功能幫助使用者查找資料,其實搜尋的原理很簡單:修改模型、重新整理表格。下面使用uitableviewcontroller簡單示範一下這個功能:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

在上面的搜尋中除了使用一個_contacts變量去儲存聯系人資料還專門定義了一個_searchcontact變量用于儲存搜尋的結果。在輸入搜尋關鍵字時我們重新整理了表格,此時會調用表格的資料源方法,在這個方法中我們根據定義的搜尋狀态去決定顯示原始資料還是搜尋結果。

我們發現每次搜尋完後都需要手動重新整理表格來顯示搜尋結果,而且當沒有搜尋關鍵字的時候還需要将目前的tableview重新設定為初始狀态。也就是這個過程中我們要用一個tableview顯示兩種狀态的不同資料,自然會提高程式邏輯複雜度。為了簡化這個過程,我們可以使用uisearchdisplaycontroller,uisearchdisplaycontroller内部也有一個uitableview類型的對象searchresultstableview,如果我們設定它的資料源代理為目前控制器,那麼它完全可以像uitableview一樣加載資料。同時它本身也有搜尋監聽的方法,我們不必在監聽uisearchbar輸入内容,直接使用它的方法即可自動重新整理其内部表格。為了和前面的方法對比在下面的代碼中沒有直接删除原來的方式而是注釋了對應代碼大家可以對照學習:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式
注意如果使用storyboard或xib方式建立上述代碼則無需定義uisearchdisplaycontroller成員變量,因為每個uiviewcontroller中已經有一個searchdisplaycontroller對象。

通過uitableview的學習相信大家對于ios的mvc已經有一個大緻的了解,這裡簡單的分析一下ios中mvc模式的設計方式。在ios中多數資料源視圖控件(view)都有一個datasource屬性用于和控制器(controller)互動,而資料來源我們一般會以資料模型(model)的形式進行定義,view不直接和模型互動,而是通過controller間接讀取資料。

就拿前面的聯系人應用舉例,uitableview作為視圖(view)并不能直接通路模型contact,它要顯示聯系人資訊隻能通過控制器(controller)來提供資料源方法。同樣的控制器本身就擁有視圖控件,可以操作視圖,也就是說視圖和控制器之間可以互相通路。而模型既不能通路視圖也不能通路控制器。具體依賴關系如下圖:

iOS開發系列--UITableView全面解析概述基本介紹資料源代理性能優化UITableViewCell常用操作UITableViewControllerMVC模式

<a href="http://pan.baidu.com/s/1jg5rpfs" target="_blank"></a>