超棒文集iOS程序猿iOS开发

iOS:UITableView 方法 属性 详解

2016-11-29  本文已影响477人  无尽思绪

参考
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/Reference/Reference.html <UITableView Class Reference> + UITableView.h

简介
--一个@的实例意味着展示和编辑分层列表的信息。一个tableview在一栏中展示了一系列item,它是UIScrollview的子类,允许用户在列表上滑动,但只能是垂直方向。
--tableview有俩种样式,见下图:

Datasource

@required  
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;  
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;  
@optional  
// Default is 1 if not implemented  
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;   
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; //每个section的title  
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; //每个section的footer  
  
// Editing/Moving or Reordering  
  
// 不同的row可以有同的-editing属性。当row的editing属性为NO,则它不可编辑(tableview setEditing改变的时候它没影响);为YES时,可编辑,可以有不同的editingStyle,详见"editingStyleForRowAtIndexPath方法"。Default is YES;  
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;  
// 不同的row是否可以移动。当tableview处于editing状态时:如果某个row返回值为NO,则它的reorder accessory view不会显示,不可移动;如果某个row的返回值为YES的时候,且datasource有实现-tableView:moveRowAtIndexPath:toIndexPath:方法,则显示该row的reorder accessory view 可以移动,否则还是不显示reorder accessory view,不可移动;  
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;  
  
// Index   
  
//返回list of section titles to display in section index view (e.g. "ABCD...Z#")  
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView;  
// which section corresponds to sectionIndexTitle,当点击某个sectionIndexTitle时候,返回某个section的值,tableview就移动到该section;  
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index;    
  
// Data manipulation - insert and delete / reorder or moving  support  
  
// 当tableview处于editing状态时,如果row可编辑,则会has the minus or plus button invoked(based on the UITableViewCellEditingStyle for the cell)。当点击minus or plus button的时候,就会调用该方法(如果是minus button,需要点击到随后的delete才会触发方法);  
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;  
// 当tableview处于editing状态,并且row可以canMove,如果拖动某row换行,则调用改方法;  
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;  
@end  

delegate方法

@optional  
// Display customization  
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;  
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);  
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);  
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath NS_AVAILABLE_IOS(6_0);  
- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);  
- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);  
  
// Variable height support  
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;  
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;  
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;  
  
// tableview在每次reload data时都会要所有cell的高度,这非常影响程序性能,使用estimatedHeight methods to quickly calcuate guessed values 去快速加载tableview  
// 原理:If these methods are implemented, the above -tableView:heightForXXX calls will be deferred until views are ready to be displayed, so more expensive logic can be placed there.  
- (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);  
  
// Section header & footer information.  
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; //custom view for header. will be adjusted to default or specified header height  
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section; //custom view for footer. will be adjusted to default or specified footer height  
  
// Accessories (disclosures).   
// 当cell的accessaryType为UITableViewCellAccessoryDetailDisclosureButton时,点击accessaryView将会调用delegate的tableView:accessoryButtonTappedForRowWithIndexPath方法。否则只是didSelectRowAtIndexPath;  
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath NS_DEPRECATED_IOS(2_0, 3_0);  
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath;  
  
// Selection  
  
// -tableView:shouldHighlightRowAtIndexPath: is called when a touch comes down on a row.   
// Returning NO or YES 当前选中的row是否高亮  
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);  
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);  
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);  
// Called before the user changes the selection. Return a new indexPath, or nil, to change the proposed selection.  
/* 先点击row1,再点击row2,俩着执行顺序:在下一行将要选中后才取消上一行的选中 
willSelectRowAtIndexPath 当前row为:0 
didSelectRowAtIndexPath 当前row为:0 
willSelectRowAtIndexPath 当前row为:1 
willDeselectRowAtIndexPath 当前row为:0 
didDeselectRowAtIndexPath 当前row为:0 
didSelectRowAtIndexPath 当前row为:1 
*/  
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;  
- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(3_0);  
// Called after the user changes the selection.  
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;  
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(3_0);  
  
// Editing  
  
// 允许cell自定义editingStyle.Default is UITableViewCellEditingStyleDelete(when the table has editing property set to YES)  
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;  
// 当tableview editing为YES,点击minus时出来deleteButton的title.Default is 'Delete';  
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(3_0);  
  
// 当tableview处于编辑时,控制row的background是否is indented就是row所在的那个框,例如缩不缩进可能会导致左边的edit按钮是在row框里面or外面)。Default is YES.(方法仅仅适用于grouped style 且跟之后的indentation level 方法没有关系)  
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath;  
  
// 方法被调whenever the 'editing' property is automatically changed by the table (allowing insert/delete/move). 注意,如果是tableview的'editing'改变,方法不会调用。只有This is done by a swipe activating a single row。  
- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath;  
- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath;  
  
// Moving/reordering  
  
// Allows customization of the target row for a particular row as it is being moved/reordered  
// 注意区别之前的tableView:moveRowAtIndexPath:toIndexPath方法。当手指按住reorder accessory view移动时,只要有row moved/reordered都会调用该方法,而前者方法只有当手指放开reorder accessory view时,结束move/order操作才会调用它。返回值代表进行移动操作后回到的行,如果设置为当前行,则不论怎么移动都会回到当前行。  
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath;                 
  
// Indentation  
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath; //不同row的缩进  
  
// 长按出来的Copy/Paste操作  
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(5_0);  
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender NS_AVAILABLE_IOS(5_0);  
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender NS_AVAILABLE_IOS(5_0);  
@end  

dataSource/delegate方法大概执行顺序(所有方法均实现):

执行顺序(没有实现estimatedHeight这些方法):

备注:

1.由上可看出,estimatedHeight在加载tableview的时候代替了heightFor方法,heightFor方法只有当cell需要显示的时候,才会调用。
2.关于estimatedHeightForXXX相关方法,里面的返回值并不能随意填写的,应该是真实高度的大概值;因为在加载tableview的时候,当所有的section header/footer row的高度都大概计算完,开始计算高度、并创建visible区域的cell时候,这些cell属不属于visible区域的判断依据就是之前的estimatedHeightForXXX方法返回的值算出来的;例如estimatedHeightForXXX相关方法返回值过大,算出来当前visible(屏幕)区域,包含3个section header 、footer以及里面的row,所以实际创建的时候也是创建这些cell(参考上文中方法执行顺序),当这些cell创建完,实际情况高度(heightForXXX方法所得)可能只占visible(屏幕)区域的一半,导致屏幕另一半空白。注意visible区域初始显示的cell是由estimatedHeightForXXX相关方法决定的,而不是heightForXXX这些方法真实高度决定的,所以有时tableview中visible区域尾部cell显示不出来或者创建的cell比visible区域cell多,都是estimatedHeightForXXX和heightForXXX方法相差导致的原因
3.以上方法和ViewController那些方法关系:先执行viewdidload、willAppear等相关方法,再执行numberOfSectionsInTableView系列方法

属性、方法

@属性 CGFloat rowHeight;sectionHeaderHeight;sectionFooterHeight; //类似相应方法,只不过是个统一值  
@属性 CGFloat estimatedRowHeight; estimatedSectionHeaderHeight;estimatedSectionFooterHeight NS_AVAILABLE_IOS(7_0); //类似相应方法,只不过是个统一值  
@属性 UIView *backgroundView ; //the background view will be automatically resized to track the size of the table view.    
@属性 UIEdgeInsets  separatorInset NS_AVAILABLE_IOS(7_0) ; //allows customization of the frame of cell separators  测试发现仅UIEdgeInsets中的right、left起作用   
// Data  
  
- (void)reloadData; // reloads everything from scratch. redisplays visible rows. because we only keep info about visible rows, this is cheap. will adjust offset if table shrinks  
- (void)reloadSectionIndexTitles NS_AVAILABLE_IOS(3_0); // reloads the index bar.  
  
// Info  
- (NSInteger)numberOfSections;  
- (NSInteger)numberOfRowsInSection:(NSInteger)section;  
  
- (CGRect)rectForSection:(NSInteger)section; // includes header, footer and all rows  
- (CGRect)rectForHeaderInSection:(NSInteger)section;  
- (CGRect)rectForFooterInSection:(NSInteger)section;  
- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath;  
  
- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point; // returns nil if point is outside of any row in the table  
- (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell; // returns nil if cell is not visible  
- (NSArray *)indexPathsForRowsInRect:(CGRect)rect; // returns nil if rect not valid   
  
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath; // returns nil if cell is not visible or index path is out of range  
- (NSArray *)visibleCells;  
- (NSArray *)indexPathsForVisibleRows;  
- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);  
- (UITableViewHeaderFooterView *)footerViewForSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);  
  
// 滚动row identified by index path到特殊位置  
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;  
// 滚动table使得selected row nearest to a specified position  
- (void)scrollToNearestSelectedRowAtScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;  
  
// Row insertion/deletion/reloading.  
// 这两个方法,是配合起来使用的,标记了一个tableView的动画块。分别代表动画的开始开始和结束。两者成对出现,可以嵌套使用。一般,在添加,删除,选择tableView中使用,并实现动画效果。在动画块内,不建议使用reloadData方法,如果使用,会影响动画。  
- (void)beginUpdates; // allow multiple insert/delete of rows and sections to be animated simultaneously.   
- (void)endUpdates; // only call insert/delete/reload calls or change the editing state inside an update block.  otherwise things like row count, etc. may be invalid.  
  
- (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);  
  
// Editing. When set, rows show insert/delete/reorder controls based on data source queries  
  
@property(nonatomic,getter=isEditing) BOOL editing;                             // default is NO. setting is not animated.  
- (void)setEditing:(BOOL)editing animated:(BOOL)animated;  
  
@属性 BOOL allowsSelection;allowsSelectionDuringEditing; allowsMultipleSelection;allowsMultipleSelectionDuringEditing;  
// Selection  
  
- (NSIndexPath *)indexPathForSelectedRow; // returns nil or index path representing section and row of selection.  
- (NSArray *)indexPathsForSelectedRows NS_AVAILABLE_IOS(5_0); // returns nil or a set of index paths representing the sections and rows of the selection.  
  
// Selects and deselects rows. These methods will not call the delegate methods (-tableView:willSelectRowAtIndexPath: or tableView:didSelectRowAtIndexPath:), nor will it send out a notification.  
- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition;  
- (void)deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated;  
  
// Appearance  
  
@属性 NSInteger sectionIndexMinimumDisplayRowCount;//当tableView中多少行的时候开始显示IndexList,默认的设置是NSIntegerMax,即默认是不显示indexList的  
@属性 UIColor *sectionIndexColor; sectionIndexBackgroundColor; sectionIndexTrackingBackgroundColor;   
@property(nonatomic) UITableViewCellSeparatorStyle separatorStyle; // default is UITableViewCellSeparatorStyleSingleLine  
@property(nonatomic,retain) UIColor               *separatorColor; // default is the standard separator gray  
@property(nonatomic,retain) UIView *tableHeaderView; tableFooterView;  
  
//Reusable 重用  
/* 
A:UITableView头文件,会找到NSMutableArray*  visiableCells,和NSMutableDictnery* reusableTableCells两个结构。visiableCells内保存当前显示的cells,reusableTableCells保存可重用的cells; 
B:TableView显示之初,reusableTableCells为空,那么tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。开始的cell都是通过[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]来创建,而且cellForRowAtIndexPath只是调用最大显示cell数的次数; 
例如:有100条数据,iPhone一屏最多显示10个cell。程序最开始显示TableView的情况是: 
1. 用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]创建10次cell,并给cell指定同样的重用标识(当然,可以为不同显示类型的cell指定不同的标识)。并且10个cell全部都加入到visiableCells数组,reusableTableCells为空。 
2. 向下拖动tableView,当cell1完全移出屏幕,并且cell11(它也是alloc出来的,原因同上)完全显示出来的时候。cell11加入到visiableCells,cell1移出visiableCells,cell1加入到reusableTableCells。 
3. 接着向下拖动tableView,因为reusableTableCells中已经有值,所以,当需要显示新的cell,cellForRowAtIndexPath再次被调用的时候,tableView dequeueReusableCellWithIdentifier:CellIdentifier,返回cell1。cell1加入到visiableCells,cell1移出reusableTableCells(测试发现cell1会被移出,而不是还留在reusableTableCells中复用);cell2移出visiableCells,cell2加入到reusableTableCells。之后再需要显示的Cell就可以正常重用了。 
*/  
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;  // Used by the delegate to acquire an already allocated cell, in lieu of allocating a new one.  
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); // newer dequeue method guarantees a cell is returned and resized properly, assuming identifier is registered  
- (id)dequeueReusableHeaderFooterViewWithIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);  // like dequeueReusableCellWithIdentifier:, but for headers/footers  
  
// Beginning in iOS 6, clients can register a nib or class for each cell.  
// If all reuse identifiers are registered, use the newer -dequeueReusableCellWithIdentifier:forIndexPath: to guarantee that a cell instance is returned.  
// Instances returned from the new dequeue method will also be properly sized when they are returned.  
- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);  
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);  
  
- (void)registerNib:(UINib *)nib forHeaderFooterViewReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);  
- (void)registerClass:(Class)aClass forHeaderFooterViewReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);  

关于:俩种dequeueReusableCellWithIdentifier 方法的区别:

1.一个6.0才能使用的方法;
2.使用6.0新方法的前提是,必须使用配套的register 相关方法,例如:

[_tableView registerClass:[MXCustomCell class] forCellReuseIdentifier:CellIdentifier];   
 _tableView.dataSource = self;    
  _tableView.delegate = self;    
  [self.view addSubview:self.notNib_tableView];   
  
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {    
     
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier    
                                                            forIndexPath:indexPath];   
    cell.textLabel.text = [NSString stringWithFormat:@"6.0方法 %d", indexPath.row];    
    return cell;    
}  

3.用处:to dequeue your cell, it will be the right size and you'll be able to do layout inside your cell's contentView

备注:

参考:http://blog.sina.com.cn/s/blog_6fd90b5b0101705h.html

1.tableview 中cell的reordering示意图


2.tableview中cell的delete & insert操作示意图:

注:

在tableview执行deleteRow/insertRow类似操作时,tableview界面更新时,并没有reloadData,而是:
a.estimatedHeightFor每个section的header、footer、row;
b.计算visible区域每个cell的heightForRowAtIndexPath;
c.新添加的/新进入visible区域(删除)cell的cellForRowAtIndexPath方法;以及cell的indentationLevel、canEdit、canMove、editStyle、willDisplay等方法;
d.heightFor剩下/其余的row、section的header、footer;(c和d的顺序不同情况可能会调换)

3.批量插入,删除,部分更新操作:UITableView提供了一个批量操作的特性,这个功能在一次进行多个row或者scetion的删除,插入,获取更新多个cell内容的时候特别好用。所有的批量操作需要包含在beginUpdates和endUpdates块中,否则会出现异常。操作顺序如下图:


注意:在批量操作时,不管代码中先写的添加操作还是删除操作,添加操作都会被推出执行,直到这个块中所有的删除操作都执行完以后,才会执行添加操作。如上图:首先删除section 0中的row 1,然后删除section 1,再向section 1中添加一行。执行完批量更新以后就得到右半边的结果。

4.reloadData的执行过程:跟tableview第一次加载执行过程一样(参考上文"方法执行顺序"),只不过它将所有visible区域的cell都放入reusableTableCells中,所以dequeueReusableCellWithIdentifier方法可重用cell。

上一篇下一篇

猜你喜欢

热点阅读