UITableView繼承自UIScrollView,因此支持垂直滾動,而且性能極佳。UITableView的代理協議也繼承了UIScrollView的代理協議,可以通過實現UIScrollView的代理方法,監聽UITableView的變化。在UITableView中沒有列的概念,只有行的概念,數據都是按行顯示的。
在iOS中,通常使用UITableView實現表格數據展示
Plain:單組樣式,整個UITableView只有一組數據
Grouped:多組樣式,整個UITableView有多組數據
dataSource:數據源,用於設置UITableView展示的數據
delegate:代理,用於監聽UITableView的操作和對UITableView的一些屬性進行設置
row:行,在每一行上展示數據,每一行對應一個cell
section:組,UITableView中可以把相似的內容分維一組
Header:頭部,一組的頭部
Footer:尾部,一組的尾部
estimatedHeight:估計高度,有估計行高、估計頭部高度、有估計尾部高度;給系統一個估算高度,系統會先創建一個cell,然後再調用設置行高的方法
Selection:選中
reload:刷新,創新裝載數據
緩沖池獲取cell:在緩存池中獲取帶有對應標記的cell
(1).UITableView的每一行都是一個UITableViewCell對象,通過dataSource的tableView:cellForRowAtIndexPath:方法來初始化每一行
(2).常見屬性
imageView:用於顯示圖片
textLabel:用於顯示大標題
detailTextLabel:用於顯示小標題
contentView:contentView是UITableViewCell所顯示內容的父視圖,可顯示一些輔助指示視圖,自定義cell的子控件都添加到contentView中
backgroundView:添加一個背景視圖,可以用於設置cell普通狀態下的背景顏色
selectedBackgroundView:選中一個cell之後的背景視圖,可用於設置選中時候的背景顏色
multipleSelectionBackgroundView:選中多個時候的背景視圖,可用於設置選中多個時候的背景顏色
reuseIdentifier:用於給cell設置一個標識,設置標識的cell用於性能優化
selectionStyle:選中時候的顯示類型
selected:設置是否選中
highlighted:是否在選中的時候呈現高亮
(3).常見方法
// 初始化一個cell和設置cell的標識 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier NS_AVAILABLE_IOS(3_0);
// UITableView類型,類型有單組和多組 @property (nonatomic, readonly) UITableViewStyle style; // 數據源 @property (nonatomic, assign) iddataSource; // 代理 @property (nonatomic, assign) id delegate; // 行高 @property (nonatomic) CGFloat rowHeight; // 組的頭部高度 @property (nonatomic) CGFloat sectionHeaderHeight; // 組的尾部高度 @property (nonatomic) CGFloat sectionFooterHeight; // 行的估計高度 @property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // 組的頭部的估計高度 @property (nonatomic) CGFloat estimatedSectionHeaderHeight NS_AVAILABLE_IOS(7_0); // 組的尾部的估計高度 @property (nonatomic) CGFloat estimatedSectionFooterHeight // 設置背景視圖 @property(nonatomic, readwrite, retain) UIView *backgroundView NS_AVAILABLE_IOS(3_2); // 設置頭部顯示的視圖 @property (nonatomic, retain) UIView *tableHeaderView; // 設置尾部顯示的視圖 @property (nonatomic, retain) UIView *tableFooterView; // 是否可以編輯 @property (nonatomic, getter=isEditing) BOOL editing;
// 初始化一個tableView和設置tableView的樣式 - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
// 所有數據全部更新 - (void)reloadData; // 更新索引 - (void)reloadSectionIndexTitles NS_AVAILABLE_IOS(3_0);
// 獲取UITableView有多少組 - (NSInteger)numberOfSections; // 獲取第section組有多少行 - (NSInteger)numberOfRowsInSection:(NSInteger)section; // 獲取第section的位置和尺寸大小 - (CGRect)rectForSection:(NSInteger)section; // 獲取第section的頭部的位置和尺寸大小 - (CGRect)rectForHeaderInSection:(NSInteger)section; // 獲取第section的尾部的位置和尺寸大小 - (CGRect)rectForFooterInSection:(NSInteger)section; // 獲取第indexPath.section組第indexPath.row行的位置和尺寸大小 - (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath; // 獲取原點point所在的行的位置indexPath - (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point; // 獲取指定的cell的位置indexPath - (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell; // 獲取rect范圍內的所有行的位置indexPath所組成的數組 - (NSArray *)indexPathsForRowsInRect:(CGRect)rect; // 獲取indexPath位置的cell - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath; // 獲取看得見的所有的cell所組成的數組 - (NSArray *)visibleCells; // 獲取看得見的所有的cell的位置indexPath所組成的數組 - (NSArray *)indexPathsForVisibleRows; // 獲取第section組頭部的view - (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); // 獲取第section組尾部的view - (UITableViewHeaderFooterView *)footerViewForSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);
// 配合beginUpdates方法使用,在一個代碼塊中,提示開始更新,代碼塊中寫對行或組的插入、刪除 - (void)beginUpdates; // 配合beginUpdates方法使用,在一個代碼塊中,提示結束更新,代碼塊中寫對行或組的插入、刪除 - (void)endUpdates; // 插入組數據 - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; // 刪除組數據 - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; // 更新組數據 - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0); // 移動組 - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection NS_AVAILABLE_IOS(5_0); // 插入行 - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; // 刪除行 - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; // 更新行 - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0); // 移動行 - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath NS_AVAILABLE_IOS(5_0);
// 獲取選中行的位置indexPath - (NSIndexPath *)indexPathForSelectedRow; // 獲取選中多行的位置indexPath所組成的數組 - (NSArray *)indexPathsForSelectedRows NS_AVAILABLE_IOS(5_0);
// 重利用標識為identifier的cell,如果注冊過Cell,在沒有可用的cell時,會返回nil - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier; // 重利用標識為identifier的cell,如果你沒有注冊過cell,在沒有可用的cell時,程序直接崩潰 - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); // 重利用標識為identifier的自定義的頭部或尾部 - (id)dequeueReusableHeaderFooterViewWithIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
// 注冊xib創建的cell,設標識為identifier - (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0); // 注冊cellClass創建的cell,設標識為identifier - (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0); // 注冊xib創建的頭部或尾部,設標識為identifier - (void)registerNib:(UINib *)nib forHeaderFooterViewReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0); // 注冊aClass創建的頭部或尾部,設標識為identifier - (void)registerClass:(Class)aClass forHeaderFooterViewReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
要讓tableView顯示數據,要設置數據源,並實現必要的數據源方法
// 設置第section組的行數,必選 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; // 設置位置為indexPath的cell,必選 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; // 設置tableView的組數,不實現該方法就默認只有一組 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // 設置第section組的頭部 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection(NSInteger)section; // 設置第section組的尾部 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; // 設置位置為indexPath的是否可以編輯,與tableview:commitEditingStyle:方法配合使用 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath; // 當某一行可進行編輯時,實現該方法可以對那一行進行編輯 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath; // 設置位置為indexPath的是否可以移動, 與tableview:moveRowAtIndexPath方法配合使用 - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath; // 移動時使用 - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; // 給組添加索引 - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView; // 設置每一組對應的索引,index表示組號,title表示索引標題 - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index;
tableView的代理可以實現scrollView的代理方法,因為協議繼承了scrollView代理協議
// 即將顯示位置為indexPath的cell時調用 - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; // 即將顯示第section組的頭部時調用 - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); // 即將顯示第section組的尾部時調用 - (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); // 位置為indexPath的cell不再在屏幕上顯示時調用 - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath NS_AVAILABLE_IOS(6_0); // 第section組的頭部不再在屏幕上顯示時調用 - (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); // 第section組的尾部不再在屏幕上顯示時調用 - (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);
// 設置行高 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; // 設置頭部的高 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section; // 設置尾部的高 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
// 估計行高 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(7_0); // 估計頭部的高 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0); // 估計尾部的高 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0);
// 設置自定義View的頭部 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; // 設置自定義view的尾部 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;
iOS設備的內存有限,如果用UITableView顯示成千上萬條數據,就需要成千上萬個UITableViewCell對象,那將會耗盡iOS設備的內存。要解決該問題,需要重用UITableViewCell對象
優化:看到的cell才創建,看不見的cell就銷毀。
但其實蘋果沒有銷毀cell,只是把它放到了緩存池中
思想:當要顯示新的cell時,如果緩存池中有類似的cell就直接拿過來重用
(1).重用原理
當滾動列表時,部分UITableViewCell會移出窗口,UITableView會將窗口外的UITableViewCell放入一個對象池中,等待重用。當UITableView要求dataSource返回UITableViewCell時,dataSource會先查看這個對象池,如果池中有未使用的UITableViewCell,dataSource會用新的數據配置這個UITableViewCell,然後返回給UITableView,重新顯示到窗口中,從而避免創建新對象。有時候需要自定義UITableViewCell(用一個子類繼承UITableViewCell),而且每一行用的不一定是同一種UITableViewCell,所以一個UITableView可能擁有不同類型的UITableViewCell,對象池中也會有很多不同類型的UITableViewCell,那麼UITableView在重用UITableViewCell時可能會得到錯誤類型的UITableViewCell
(2).解決方案
UITableViewCell有個NSString *reuseIdentifier屬性,可以在初始化UITableViewCell的時候傳入一個特定的字符串標識來設置reuseIdentifier(一般用UITableViewCell的類名)。當UITableView要求dataSource返回UITableViewCell時,先通過一個字符串標識到對象池中查找對應類型的UITableViewCell對象,如果有,就重用,如果沒有,就用傳入的這個字符串標識來初始化一個新的UITableViewCell對象
1.同時設置cell的選中背景View和默認背景View,設置默認背景View優先級更高
2.在對cell進行編輯操作時,模型和cell的行數必須一致,不然會報錯
3.在storyboard中的tableView中添加cell,可以拖控件UITableViewCell,也可以在tableView的屬性欄設置Prototype Cells的個數
4.不設置tableView的數據源,即使實現了數據源協議的方法也不能顯示數據
5.不設置tableView的代理,即使實現了代理協議的方法也不能顯示數據
6.關於估計高度:只要返回了估計高度,那麼就會先調用tableView:cellForRowAtIndexPath:方法創建cell,再調用tableView:heightForRowAtIndexPath:方法獲取cell的真實高度
7.當控件的約束剛設置時,如果要緊接著使用控件的Frame,要先調用layoutIfNeeded方法進行強制布局,系統才對控件的Frame進行設置,否則Frame為0
8.設計自定義cell時,應該先把所有可能出現的控件布置好,哪些暫時不需要顯示的可以隱藏,在需要顯示時再取消隱藏
9.應該充分利用模型,因為模型始終貫穿tableView,可以用於記錄一些cell的不確定的屬性,如不等高時,用模型記錄每一個cell不同的高