国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

UITableView學(xué)習(xí)筆記

2019-11-14 18:34:15
字體:
供稿:網(wǎng)友

//非原創(chuàng)

                              

  看TableView的資料其實(shí)已經(jīng)蠻久了,一直想寫點(diǎn)兒東西,卻總是因?yàn)楦鞣N原因拖延,今天晚上有時(shí)間靜下心來記錄一些最近學(xué)習(xí)的TableView的知識(shí)。下面進(jìn)入正題,UITableView堪稱UIKit里面最復(fù)雜的一個(gè)控件了,使用起來不算難,但是要用好并不容易。當(dāng)使用的時(shí)候我們必須要考慮到后臺(tái)數(shù)據(jù)的設(shè)計(jì),tableViewCell的設(shè)計(jì)和重用以及tableView的效率等問題。

下面分9個(gè)方面進(jìn)行介紹:

一、UITableView概述

  UITableView繼承自UIScrollView,可以表現(xiàn)為Plain和Grouped兩種風(fēng)格,分別如下圖所示:

              

  其中左邊的是Plain風(fēng)格的,右邊的是Grouped風(fēng)格,這個(gè)區(qū)別還是很明顯的。

  查看UITableView的幫助文檔我們會(huì)注意到UITableView有兩個(gè)Delegate分別為:dataSource和delegate。

  dataSource是UITableViewDataSource類型,主要為UITableView提供顯示用的數(shù)據(jù)(UITableViewCell),指定UITableViewCell支持的編輯操作類型(insert,delete和reordering),并根據(jù)用戶的操作進(jìn)行相應(yīng)的數(shù)據(jù)更新操作,如果數(shù)據(jù)沒有更具操作進(jìn)行正確的更新,可能會(huì)導(dǎo)致顯示異常,甚至crush。

  delegate是UITableViewDelegate類型,主要提供一些可選的方法,用來控制tableView的選擇、指定section的頭和尾的顯示以及協(xié)助完成cell的刪除和排序等功能。

  提到UITableView,就必須的說一說NSIndexPath。UITableView聲明了一個(gè)NSIndexPath的類別,主要用來標(biāo)識(shí)當(dāng)前cell的在tableView中的位置,該類別有section和row兩個(gè)屬性,前者標(biāo)識(shí)當(dāng)前cell處于第幾個(gè)section中,后者代表在該section中的第幾行。

  UITableView只能有一列數(shù)據(jù)(cell),且只支持縱向滑動(dòng),當(dāng)創(chuàng)建好的tablView第一次顯示的時(shí)候,我們需要調(diào)用其reloadData方法,強(qiáng)制刷新一次,從而使tableView的數(shù)據(jù)更新到最新狀態(tài)。

 

二、UITableViewController簡介

  UITableViewController是系統(tǒng)提供的一個(gè)便利類,主要是為了方便我們使用UITableView,該類生成的時(shí)候就將自身設(shè)置成了其包含的tableView的dataSource和delegate,并創(chuàng)建了很多代理函數(shù)的框架,為我們大大的節(jié)省了時(shí)間,我們可以通過其tableView屬性獲取該controller內(nèi)部維護(hù)的tableView對(duì)象。默認(rèn)情況下使用UITableViewController創(chuàng)建的tableView是充滿全屏的,如果需要用到tableView是不充滿全屏的話,我們應(yīng)該使用UIViewController自己創(chuàng)建和維護(hù)tableView

  UITableViewController提供一個(gè)初始化函數(shù)initWithStyle:,根據(jù)需要我們可以創(chuàng)建Plain或者Grouped類型的tableView,當(dāng)我們使用其從UIViewController繼承來的init初始化函數(shù)的時(shí)候,默認(rèn)將會(huì)我們創(chuàng)建一個(gè)Plain類型的tableView。 

  UITableViewController默認(rèn)的會(huì)在viewWillAppear的時(shí)候,清空所有選中cell,我們可以通過設(shè)置self.clearsselectionOnViewWillAppear = NO,來禁用該功能,并在viewDidAppear中調(diào)用UIScrollViewFlashScrollIndicators方法讓滾動(dòng)條閃動(dòng)一次,從而提示用戶該控件是可以滑動(dòng)的。 

 

三、UITableViewCell介紹

   UITableView中顯示的每一個(gè)單元都是一個(gè)UITableViewCell對(duì)象,看文檔的話我們會(huì)發(fā)現(xiàn)其初始化函數(shù)initWithStyle:reuseIdentifier:比較特別,跟我們平時(shí)看到的UIView的初始化函數(shù)不同。這個(gè)主要是為了效率考慮,因?yàn)樵?span id="iwvjtn8m0" class="s2">tableView快速滑動(dòng)的滑動(dòng)的過程中,頻繁的alloc對(duì)象是比較費(fèi)時(shí)的,于是引入了cell的重用機(jī)制,這個(gè)也是我們?cè)?span id="iwvjtn8m0" class="s2">dataSource中要重點(diǎn)注意的地方,用好重用機(jī)制會(huì)讓我們的tableView滑動(dòng)起來更加流暢。

  我們可以通過cell的selectionStyle屬性指定cell選中時(shí)的顯示風(fēng)格,以及通過accessoryType來指定cell右邊的顯示的內(nèi)容,或者直接指定accessoryView來定制右邊顯示的view。 

  系統(tǒng)提供的UITableView也包含了四種風(fēng)格的布局,分別是:

typedef enum {    UITableViewCellStyleDefault,    UITableViewCellStyleValue1,    UITableViewCellStyleValue2,    UITableViewCellStyleSubtitle} UITableViewCellStyle;

  這幾種文檔中都有詳細(xì)描述,這兒就不在累贅。然而可以想象系統(tǒng)提供的只是最常用的幾種類型,當(dāng)系統(tǒng)提供的風(fēng)格不符合我們需要的時(shí)候,我們就需要對(duì)cell進(jìn)行定制了,有以下兩種定制方式可選:

  1、直接向cell的contentView上面添加subView

  這是比較簡單的一種的,根據(jù)布局需要我們可以在不同的位置添加subView。但是此處需要注意:所有添加的subView都最好設(shè)置為不透明的,因?yàn)槿绻?span id="iwvjtn8m0" class="s2">subView是半透明的話,view圖層的疊加將會(huì)花費(fèi)一定的時(shí)間,這會(huì)嚴(yán)重影響到效率。同時(shí)如果每個(gè)cell上面添加的subView個(gè)數(shù)過多的話(通常超過3,4個(gè)),效率也會(huì)受到比較大的影響。

  下面我們看一個(gè)例子:

復(fù)制代碼
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    NSArray *sections = [SvTableViewDataModal sections];    SvSectionModal *sectionModal = [sections objectAtIndex:indexPath.section];        static NSString *reuseIdetify = @"SvTableViewCell";    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdetify];    if (!cell) {        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdetify];        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;        cell.showsReorderControl = YES;                for (int i = 0; i < 6; ++i) {            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100 + 15 * i, 0, 30, 20)];            label.backgroundColor = [UIColor redColor];            label.text = [NSString stringWithFormat:@"%d", i];            [cell.contentView addSubview:label];            [label release];        }    }        cell.textLabel.backgroundColor = [UIColor clearColor];    cell.textLabel.text = [sectionModal.cityNames objectAtIndex:indexPath.row];    return cell;} 
復(fù)制代碼

  在上面這個(gè)例子中,我往每個(gè)cell中添加了6個(gè)subView,而且每個(gè)subView都是半透明(UIView默認(rèn)是半透明的),這個(gè)時(shí)候滑動(dòng)起來明顯就可以感覺到有點(diǎn)顫抖,不是很流暢。當(dāng)把每一個(gè)subView的opaque屬性設(shè)置成YES的時(shí)候,滑動(dòng)會(huì)比之前流暢一些,不過還是有點(diǎn)兒卡。

  2、從UITableViewCell派生一個(gè)類

  通過從UITableViewCell中派生一個(gè)類,可以更深度的定制一個(gè)cell,可以指定cell在進(jìn)入edit模式的時(shí)候如何相應(yīng)等等。最簡單的實(shí)現(xiàn)方式就是將所有要繪制的內(nèi)容放到一個(gè)定制的subView中,并且重載該subView的drawRect方法直接把要顯示的內(nèi)容繪制出來(這樣可以避免subView過多導(dǎo)致的性能瓶頸),最后再將該subView添加到cell派生類中的contentView中即可。但是這樣定制的cell需要注意在數(shù)據(jù)改變的時(shí)候,通過手動(dòng)調(diào)用該subView的setNeedDisplay方法來刷新界面,這個(gè)例子可以在蘋果的幫助文檔中的TableViewSuite工程中找到,這兒就不舉例了。

  觀看這兩種定制cell的方法,我們會(huì)發(fā)現(xiàn)subView都是添加在cell的contentView上面的,而不是直接加到cell上面,這樣寫也是有原因的。下面我們看一下cell在正常狀態(tài)下和編輯狀態(tài)下的構(gòu)成圖:

  cell在正常狀態(tài)下的構(gòu)成圖如下:

  進(jìn)入編輯狀態(tài)下cell的構(gòu)成圖如下:

  通過觀察上面兩幅圖片我們可以看出來,當(dāng)cell在進(jìn)入編輯狀態(tài)的時(shí)候,contentView會(huì)自動(dòng)的縮放來給Editing control騰出位置。這也就是說如果我們把subView添加到contentView上,如果設(shè)置autoresizingMask為更具父view自動(dòng)縮放的話,cell默認(rèn)的機(jī)制會(huì)幫我們處理進(jìn)入編輯狀態(tài)的情況。而且在tableView是Grouped樣式的時(shí)候,會(huì)為cell設(shè)置一個(gè)背景色,如果我們直接添加在cell上面的話,就需要自己考慮到這個(gè)背景色的顯示問題,如果添加到contentView上,則可以通過view的疊加幫助我們完成該任務(wù)。綜上,subView最好還是添加到cell的contentView中。

 

四、Reordering

  為了使UITableVeiew進(jìn)入edit模式以后,如果該cell支持reordering的話,reordering控件就會(huì)臨時(shí)的把a(bǔ)ccessaryView覆蓋掉。為了顯示reordering控件,我們必須將cell的showsReorderControl屬性設(shè)置成YES,同時(shí)實(shí)現(xiàn)dataSource中的tableView:moveRowAtIndexPath:toIndexPath:方法。我們還可以同時(shí)通過實(shí)現(xiàn)dataSource中的 tableView:canMoveRowAtIndexPath:返回NO,來禁用某一些cell的reordering功能。

  下面看蘋果官方的一個(gè)reordering流程圖:

  上圖中當(dāng)tableView進(jìn)入到edit模式的時(shí)候,tableView會(huì)去對(duì)當(dāng)前可見的cell逐個(gè)調(diào)用dataSource的tableView:canMoveRowAtIndexPath:方法(此處官方給出的流程圖有點(diǎn)兒問題),決定當(dāng)前cell是否顯示reoedering控件,當(dāng)開始進(jìn)入拖動(dòng)cell進(jìn)行拖動(dòng)的時(shí)候,每滑動(dòng)過一個(gè)cell的時(shí)候,會(huì)去掉用delegate的tableView:targetIndexPathForMoveFromRowAtIndexPath:toPRoposedIndexPath:方法,去判斷當(dāng)前劃過的cell位置是否可以被替換,如果不行則給出建議的位置。當(dāng)用戶放手時(shí)本次reordering操作結(jié)束,調(diào)用dataSource中的tableView:moveRowAtIndexPath:toIndexPath:方法更新tableView對(duì)應(yīng)的數(shù)據(jù)。

  此處給個(gè)我寫demo中的更新數(shù)據(jù)的小例子:

復(fù)制代碼
// if you want show reordering control, you must implement moveRowAtndexPath, or the reordering control will not show // when use reordering end, this method is invoke - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{    // update DataModal        NSArray *sections = [SvTableViewDataModal sections];    SvSectionModal *sourceSectionModal = [sections objectAtIndex:sourceIndexPath.section];    NSString *city = [[sourceSectionModal.cityNames objectAtIndex:sourceIndexPath.row] retain];    [sourceSectionModal.cityNames removeObject:city];    [SvTableViewDataModal replaceSectionAtIndex:sourceIndexPath.section withSection:sourceSectionModal];        SvSectionModal *desinationsSectionModal= [[SvTableViewDataModal sections] objectAtIndex:destinationIndexPath.section];    [desinationsSectionModal.cityNames insertObject:city atIndex:destinationIndexPath.row];    [SvTableViewDataModal replaceSectionAtIndex:destinationIndexPath.section withSection:desinationsSectionModal];        [city release];}
復(fù)制代碼

  上面代碼中首先拿到源cell所處的section,然后從該section對(duì)應(yīng)的數(shù)據(jù)中移除,然后拿到目標(biāo)section的數(shù)據(jù),然后將源cell的數(shù)據(jù)添加到目標(biāo)section中,并更新回?cái)?shù)據(jù)模型,如果我們沒有正確更新數(shù)據(jù)模型的話,顯示的內(nèi)容將會(huì)出現(xiàn)異常。

 

五、Delete & Insert

  cell的delete和insert操作大部分流程都是一樣的,當(dāng)進(jìn)入編輯模式的時(shí)候具體的顯示是delete還是insert取決與該cell的editingStyle的值,editStyle的定義如下:

typedef enum {    UITableViewCellEditingStyleNone,    UITableViewCellEditingStyleDelete,    UITableViewCellEditingStyleInsert} UITableViewCellEditingStyle;

  當(dāng)tableView進(jìn)入編輯模式以后,cell上面顯示的delete還是insert除了跟cell的editStyle有關(guān),還與 tableView的delegate的tableView:editingStyleForRowAtIndexPath:方法的返回值有關(guān)(在這里嘮叨一句,其實(shí)delegate提供了很多改變cell屬性的機(jī)會(huì),如非必要,還是不要去實(shí)現(xiàn)這些方法,因?yàn)閳?zhí)行這些方法也造成一定的開銷)

  delete和insert的流程如下蘋果官方文檔中給出的圖所示:

  下面是我寫的demo中刪除和添加部分的代碼:

復(fù)制代碼
#pragma mark -- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{    NSLog(@"commit editStyle: %d", editingStyle);        if (editingStyle == UITableViewCellEditingStyleDelete) {        NSArray *sections = [SvTableViewDataModal sections];        SvSectionModal *sourceSectionModal = [sections objectAtIndex:indexPath.section];        [sourceSectionModal.cityNames removeObjectAtIndex:indexPath.row];                [SvTableViewDataModal replaceSectionAtIndex:indexPath.section withSection:sourceSectionModal];        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];    }    else {        // do something for add it        NSArray *sections = [SvTableViewDataModal sections];        SvSectionModal *sourceSectionModal = [sections objectAtIndex:indexPath.section];        [sourceSectionModal.cityNames insertObject:@"new City" atIndex:indexPath.row];        [SvTableViewDataModal replaceSectionAtIndex:indexPath.section withSection:sourceSectionModal];                [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];    }}
復(fù)制代碼

  代碼中首先判斷當(dāng)前操作是delete操作還是insert操作,相應(yīng)的更新數(shù)據(jù),最后根據(jù)情況調(diào)用tableView的insertRowsAtIndexPaths:withRowAnimation:或者deleteRowsAtIndexPaths:withRowAnimation:方法,對(duì)tableView的視圖進(jìn)行更新。cell的刪除和添加操作相對(duì)還是比較簡單的。

 

六、Cell的Select操作

  當(dāng)我們?cè)趖ableView中點(diǎn)擊一個(gè)cell的時(shí)候,將會(huì)調(diào)用tableView的delegate中的tableView:didSelectRowAtIndexPath:方法。

  關(guān)于tableView的cell的選中,蘋果官方有以下幾個(gè)建議:

   1、不要使用selection來表明cell的選擇狀態(tài),而應(yīng)該使用accessaryView中的checkMark或者自定義accessaryView來顯示選中狀態(tài)。 

   2、當(dāng)選中一個(gè)cell的時(shí)候,你應(yīng)該取消前一個(gè)cell的選中。 

   3、如果cell選中的時(shí)候,進(jìn)入下一級(jí)viewCOntroller,你應(yīng)該在該級(jí)菜單從navigationStack上彈出的時(shí)候,取消該cell的選中。

  這塊兒再提一點(diǎn),當(dāng)一個(gè)cell的accessaryType為UITableViewCellAccessoryDisclosureIndicator的時(shí)候,點(diǎn)擊該accessary區(qū)域通常會(huì)將消息繼續(xù)向下傳遞,即跟點(diǎn)擊cell的其他區(qū)域一樣,將會(huì)掉delegate的tableView:didSelectRowAtIndexPath:方法,當(dāng)時(shí)如果accessaryView為 UITableViewCellAccessoryDetailDisclosureButton的時(shí)候,點(diǎn)擊accessaryView將會(huì)調(diào)用delegate的 tableView:accessoryButtonTappedForRowWithIndexPath:方法。

  

七、批量插入,刪除,部分更新操作

  UITableView提供了一個(gè)批量操作的特性,這個(gè)功能在一次進(jìn)行多個(gè)row或者scetion的刪除,插入,獲取更新多個(gè)cell內(nèi)容的時(shí)候特別好用。所有的批量操作需要包含在beginUpdates和endUpdates塊中,否則會(huì)出現(xiàn)異常。

  下面請(qǐng)看我demo中的一個(gè)批量操作的例子:

復(fù)制代碼
- (void)groupEdit:(UIBarButtonItem*)sender{    [_tableView beginUpdates];    // first update the data modal    [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationTop];        [_tableView deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationTop];        [SvTableViewDataModal deleteSectionAtIndex:0];        SvSectionModal *section = [[SvTableViewDataModal sections] objectAtIndex:0];    [section.cityNames insertObject:@"帝都" atIndex:0];    [SvTableViewDataModal replaceSectionAtIndex:0 withSection:section];        [_tableView endUpdates];}
復(fù)制代碼

  上面的例子中我們可以看到先往tableView的第0個(gè)section的第0行添加一個(gè)cell,然后將第0個(gè)section刪掉。按照我們程序中寫的順序,那么新添加進(jìn)去的“帝都”,將不在會(huì)顯示,因?yàn)榘恼麄€(gè)section都已經(jīng)被刪除了。

  執(zhí)行程序前后結(jié)果如下圖:

              

  demo中第0個(gè)section是陜西省的城市,第1個(gè)section是北京。左邊是執(zhí)行前的截圖,右邊是執(zhí)行后的截圖,觀察發(fā)現(xiàn)結(jié)果并不像我們前面推測(cè)的那樣。那是因?yàn)樵谂坎僮鲿r(shí),不管代碼中先寫的添加操作還是刪除操作,添加操作都會(huì)被推出執(zhí)行,直到這個(gè)塊中所有的刪除操作都執(zhí)行完以后,才會(huì)執(zhí)行添加操作,這也就是上面蘋果官方圖片上要表達(dá)的意思。 

  蘋果官方文檔有一副圖可以幫助我們更好的理解這一點(diǎn):

  原圖中操作是:首先刪除section 0中的row 1,然后刪除section 1,再向section 1中添加一行。執(zhí)行完批量更新以后就得到右半邊的結(jié)果。

   

八、IndexList

  當(dāng)我們tableView中section有很多,數(shù)據(jù)量比較大的時(shí)候我們可以引入indexList,來方便完成section的定位,例如系統(tǒng)的通訊錄程序。我們可以通過設(shè)置tableView的sectionIndexMinimumDisplayRowCount屬性來指定當(dāng)tableView中多少行的時(shí)候開始顯示IndexList,默認(rèn)的設(shè)置是NSIntegerMax,即默認(rèn)是不顯示indexList的。

  為了能夠使用indexlist我們還需要實(shí)現(xiàn)dataSource中一下兩個(gè)方法:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView; - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index; 

  第一個(gè)方法返回用于顯示在indexList中的內(nèi)容的數(shù)組,通常為A,B,C...Z。第二個(gè)方法的主要作用是根據(jù)用戶在indexList中點(diǎn)擊的位置,返回相應(yīng)的section的index值。這個(gè)例子可以在蘋果官方給出的TableViewSuite中找到,實(shí)現(xiàn)起來還是很簡單的。

 

九、其他

  1、分割線

  我們可以通過設(shè)置tableView的separatorStyle屬性來設(shè)置有無分割線以及分割線的風(fēng)格,其中style定義如下:

typedef enum {    UITableViewCellSeparatorStyleNone,    UITableViewCellSeparatorStyleSingleLine,    UITableViewCellSeparatorStyleSingleLineEtched} UITableViewCellSeparatorStyle;

  同時(shí)還可以通過tableView的separatorColor屬性來設(shè)置分割線的顏色。

  2、如何提高tableView的性能

  a、重用cell

  我們都知道申請(qǐng)內(nèi)存是需要時(shí)間,特別是在一段時(shí)間內(nèi)頻繁的申請(qǐng)內(nèi)存將會(huì)造成很大的開銷,而且上tebleView中cell大部分情況下布局都是一樣的,這個(gè)時(shí)候我們可以通過回收重用機(jī)制來提高性能。

  b、避免content的重新布局

  盡量避免在重用cell時(shí)候,對(duì)cell的重新布局,一般情況在在創(chuàng)建cell的時(shí)候就將cell布局好。

  c、使用不透明的subView

  在定制cell的時(shí)候,將要添加的subView設(shè)置成不透明的會(huì)大大減少多個(gè)view層疊加時(shí)渲染所需要的時(shí)間。

  d、如果方便,直接重載subView的drawRect方法

  如果定制cell的過程中需要多個(gè)小的元素的話,最好直接對(duì)要顯示的多個(gè)項(xiàng)目進(jìn)行繪制,而不是采用添加多個(gè)subView。

  e、tableView的delegate的方法如非必要,盡量不要實(shí)現(xiàn)

  tableView的delegate中的很多函數(shù)提供了對(duì)cell屬性的進(jìn)一步控制,比如每個(gè)cell的高度,cell是否可以編輯,支持的edit風(fēng)格等,如非必要最好不要實(shí)現(xiàn)這些方法因?yàn)榭焖俚恼{(diào)用這些方法也會(huì)影響性能。

 

 

 


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 繁昌县| 阿拉善左旗| 兴山县| 中方县| 思茅市| 枣庄市| 乐山市| 金乡县| 昭觉县| 泰来县| 尼木县| 吉木萨尔县| 怀集县| 长岭县| 佳木斯市| 桂林市| 东明县| 宜川县| 汝城县| 肇源县| 舟曲县| 仲巴县| 潜江市| 邢台县| 屯昌县| 金湖县| 祥云县| 南皮县| 望城县| 固镇县| 女性| 南开区| 莱州市| 郧西县| 仙游县| 庆安县| 阳东县| 黎平县| 汉沽区| 蛟河市| 化州市|