UITableView使用

一:UITableView基础知识点。

   首先理解各个代理方法的调用顺序,这对于开发非常重要,理解好这一点,在项目开发中将会节省很多时间。下面我们一起来看一下,错误的地方,希望大家指出,一起学习。(最好的理解方式就是自己写一个小demo)

   

   //情况一:单分区情况

    1)首先调用-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView方法计算UITableView中分区的个数.

    2)执行-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section方法,计算每个分区中row的个数.

    3)计算每个cell的高度,cell有多少个,该方法- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath就被执行多少次.上面过程被反复执行了3.

    4)然后调用-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath创建cell,和显示cell的内容.屏幕中可以显示多少个cell,该方法就会被调用多少次.并且同时还会调用计算高度方法计算该cell的高度.

    5)当滚动屏幕出现新的cell的时候,首先会先调用-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath显示cell的内容.然后立马计算 cell的高度.

    [self.tableView reloadData];使用该方法进行刷新数据的时候,会按照刚刚执行方法的顺序,再执行一次.


   //多分区情况

   1: 先确定分区个数调用该 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView方法,Section个数.

   2: 调用-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

    scetion等于1开始(我这里是使用了2个分区,多分区从最后一个开始自动计算,依次向前).section1,计算分区中cell的高度,section0时计算分区中cell的高度.

   3: 然后调用-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath创建cell,和显示cell的内容.屏幕中可以显示多少个cell,该方法就会被调用多少次.从分区0开始,只计算屏幕上显示的内容.

   4:滑动屏幕的时候,调用-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath显示内容,同时调用- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath方法计算高度.

   [self.tableView reloadData];使用该方法进行刷新数据的时候,会按照刚刚执行方法的顺序,再执行一次.


1)看代码理解基本的属性和方法,下面代码实现了最基本的列表展示,移动表格可看见分区头视图进行替换。

#import "RootViewController.h"


@interface RootViewController ()

{

    NSMutableArray *_dataArray;//数据源:UITableView提供数据

}

@end


@implementation RootViewController


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

   self = [superinitWithNibName:nibNameOrNil bundle:nibBundleOrNil];

   if (self) {

        // Custom initialization

    }

    return self;

}


- (void)viewDidLoad

{

    [superviewDidLoad];

    

    //准备数据

   _dataArray = [[NSMutableArrayalloc] init];

    

    //实现通讯录的展示

   for (int i='A'; i<='Z';i++) {

        NSMutableArray *array = [[NSMutableArrayalloc] init];

       for (int j=0; j<10; j++) {

           NSString *str = [NSStringstringWithFormat:@"%c%d",i,j];

            [arrayaddObject:str];

        }

        [_dataArrayaddObject:array];


    }

    self.automaticallyAdjustsScrollViewInsets =NO;

    //表视图,是一个特殊的UIScrollView,默认横向固定,纵向可以上下滚动,contentSize自动计算

    //创建tableView并设置样式(常规的样式)

    //表示图,是一个特殊到uiscrollview,默认横向固定,纵向可以上下滚动。contentsize自动计算,不需要你来设置。UITableViewStylePlain常规显示样式。

   

    UITableView *tableView = [[UITableViewalloc] initWithFrame:CGRectMake(0,0,self.view.bounds.size.width,self.view.bounds.size.height-64)style:UITableViewStylePlain];

    //设置代理 (代理方法都是与tableView UI样式相关的方法)//设置tabview都数据源,协议中有2个必须实现都方法,所以要记住,使用来数据源加要注意方法实现,而且很重要。

    tableView.delegate =self;

    //设置tableView的数据源(数据源方法一般都是与UITableView数据操作有关的方法)

    tableView.dataSource =self;

    [self.viewaddSubview:tableView];


}


#pragma mark - UITableViewDelegate

//设置行高,不写此方法,默认高度44

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

   return 50;

}

//选中tableView中的某一行时,触发此方法

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

   NSLog(@"selected section:%d row:%d",indexPath.section,indexPath.row);

    //通过tableView设置某一行,自动反选,反选的代理方法不再被调用

    [tableView deselectRowAtIndexPath:indexPathanimated:YES];

    //这里一般情况下是push到下一级进行相应的操作。

}


//某一行被反选时,触发此方法当某一行由选中变为非选中状态会调用此方法。

//即某一行被反选时,触发次方法

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{

   NSLog(@"deselect section:%d row:%d",indexPath.section,indexPath.row);

}


#pragma mark - UITableViewDataSource


//告诉tableView有多少个分区(数据有多少类)

//如果不写此方法,默认tableView有一个分区

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{

    return_dataArray.count;

}


//告诉tableView每个分区有多少条()数据

//有多少个分区 此方法被执行多少次,section在方法执行时从0自增

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

   return [[_dataArrayobjectAtIndex:section] count];

}


//tableView中每一行都是一个UITableViewCell对象:单元格,本质上是一个视图

//一开始,屏幕中可见多少行,此方法被调用多少次,当上、下滑动屏幕时,此方法也会被调用

//cell的重用机制保证了只创建有限个cell对象,来显示多条数据,最大限度的节省了内存的开销,提高了程序的运行效率,具有极大的借鉴意义

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *cellIde =@"Cell";//声明cell的可重用标识符

    //根据可重用标识符,到tableView的重用队列中,取cell对象

   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIde];

   if (cell == nil) {

        //如果取不到,则创建新的cell对象

        //创建cell设置样式,并赋值可重用标识符

        cell = [[UITableViewCellalloc] initWithStyle:UITableViewCellStyleSubtitlereuseIdentifier:cellIde];

    }

    //设置cell的选中样式,iOS7之前,cell的选中样式默认为蓝色,iOS7之后

    cell.selectionStyle =UITableViewCellSelectionStyleGray;

    //设置cell右侧的提示样式

    //UITableViewCellAccessoryDisclosureIndicator箭头提示

    cell.accessoryType =UITableViewCellAccessoryDisclosureIndicator;

    //将数据,赋值给cell

    //indexPath section带有该行所在的分区信息 row该行在对应分区的位置

    //取到该分区所使用的数组

   NSArray *array = [_dataArrayobjectAtIndex:indexPath.section];

   NSString *str = [array objectAtIndex:indexPath.row];

    //将数组赋值给cell

    //UITableViewCellStyleSubtitle 副标题才能显示

    //设置cell的主标题

    cell.textLabel.text = str;

    //设置副标题

    cell.detailTextLabel.text =@"test";

    //设置图片(头像)

    cell.imageView.image = [UIImageimageNamed:@"0.png"];

   return cell;

}


//设置分区的头标题

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{

   return [NSStringstringWithFormat:@"%c分区头标",'A'+section];

}

UITableView使用_第1张图片




二:项目中常用到UITableView的一些小知识点:


1)UITableViewCell横线左端对其实现:


方法一:在初始化的表格的时候添加如下代码

   if ([_tableViewrespondsToSelector:@selector(setSeparatorInset:)]) {

        [_tableViewsetSeparatorInset:UIEdgeInsetsZero];

    }

   if ([_tableViewrespondsToSelector:@selector(setLayoutMargins:)]) {

        [_tableViewsetLayoutMargins:UIEdgeInsetsZero];

    }

实现代理方法

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{

    

   if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {

        [cellsetSeparatorInset:UIEdgeInsetsZero];

    }

   if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {

        [cellsetLayoutMargins:UIEdgeInsetsZero];

    }

    

}


方法二:去除表格细线,然后自定义细线进行添加


    _tableView.separatorStyle =UITableViewCellSeparatorStyleNone;

    //添加细线

    CALayer *lineLayer=[CALayerlayer];

    lineLayer.frame=CGRectMake(0,43.5,self.view.bounds.size.width,0.5);//位置根据需求自己设置

    lineLayer.backgroundColor=[UIColorgrayColor].CGColor;

    [self.view.layeraddSublayer:lineLayer];


2)取消点击cell时候的背景效果。

  cell. selectionStyle = UITableViewCellSeparatorStyleNone ;

//选中tableView中的某一行时,触发此方法

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    //通过tableView设置某一行,自动反选,反选的代理方法不再被调用

    [tableView deselectRowAtIndexPath:indexPathanimated:YES];

    

}



3)去除尾部多余的cell


           UITableView使用_第2张图片

 self.tableView.tableFooterView=[[UIView alloc]init];//一句话解决问题




4)实现添加删除cell的动画效果,beginUpdates方法和endUpdates方法。

      这两个方法,是配合起来使用的,标记了一个tableView的动画块。分别代表动画的开始开始和结束。

      两者成对出现,可以嵌套使用。一般,在添加,删除,选择 tableView中使用,并实现动画效果。

      在动画块内,不建议使用reloadData方法,如果使用,会影响动画。

    

 插入指定的行,

 

 在执行该方法时,会对数据源进行访问(分组数据和行数据),并更新可见行。所以,在调用该方法前,应该先更新数据源

 - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

 插入分组到制定位置

 - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation

 插入一个特定的分组。如果,指定的位置上已经存在了分组,那么原来的分组向后移动一个位置。

 

 

 

 删除制定位置的分组

 - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation

  删除一个制定位置的分组,其后面的分组向前移动一个位置。

 删除选择的row

- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;



 移动分组

 - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection

 

 移动原来的分组从一个位置移动到一个新的位置。如果,新位置上若存在某个分组,那这某个分组将会向上(下)移动到临近一个位置。该方法,没有动画参数。会直接移动。并且一次只能移动一个分组。

 

 在如上方法中,建议使用该动画块进行操作!


具体实现部分代码:

   删除部分操作:(插入原理其实是一样的,保证数据源随之更新即可)

   [self.tableView beginUpdates];

    //要记得对分区进行删除操作。self.dataArray是一个二维数组。及实现多分区情况下的删除操纵。

    if([[self.dataArray objectAtIndex: indexPath.section] count]  > 1)

    {

        [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section]] withRowAnimation:UITableViewRowAnimationLeft];

     }

    else

    {

        //如果我们的UITableView是分组的时候,我们如果删除某个分组的最后一条记录时,相应的分组也将被删除。所以,必须保证UITableView的分组,和cell同时被删除。

        [_tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]

                 withRowAnimation:UITableViewRowAnimationLeft];

    }

    

    //注意:数据源在beginUpdates前面操作和在后面操作,并没有什么关系,关键一点是数据源要随着删除,插入的数据与之对应改变,不然就会出现错误。但是不能够把对数据源的操作放到endUpdates

    NSMutableArray *arr=self.dataArray[indexPath.section];

    [arr removeObjectAtIndex:indexPath.row];

    [self.dataArray removeObjectAtIndex:indexPath.section];

    if(arr.count!=0)

    [self.dataArray insertObject:arr atIndex:indexPath.section];


    [self.tableView endUpdates];



5)UITableViewCell重用原理
    重用原理:当滚动列表时,部分 UITableViewCell 会移出窗口, UITableView 会将窗口外的 UITableViewCell 放入一个对象池中,等待重用。当 UITableView 要求 dataSource 返回 UITableViewCell 时, dataSource 会先查看这个对象池,如果池中有未使用的 UITableViewCell dataSource 会用新的数据配置这个 UITableViewCell ,然后返回给 UITableView ,重新显示到窗口中,从而避免创建新对象。
重用代码,大家应该是非常熟悉的:如下:  

   static NSString *cellIde =@"Cell";//声明cell的可重用标识符

    //根据可重用标识符,到tableView的重用队列中,取cell对象

   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIde];

   if (cell == nil) {

        //如果取不到,则创建新的cell对象,创建cell设置样式,并赋值可重用标识符

        cell = [[[UITableViewCellallocinitWithStyle:UITableViewCellStyleSubtitlereuseIdentifier:cellIde]autorelease];

    }


    但有时候系统自带的cell无法满足需求的时候,需要自定义UITableViewCell(用一个子类继承UITableViewCell,自定义UITableViewCell很重要,要掌握),而且每一行用的不一定是同一种UITableViewCell,所以一个UITableView可能拥有不同类型的UITableViewCell,对象池中也会有很多不同类型的UITableViewCell,那么UITableView在重用UITableViewCell时可能会得到错误类型的UITableViewCell


        解决方案:UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier9。当UITableView要求dataSource返回UITableViewCell时,先通过一个字符串标识到对象池中查找对应类型的UITableViewCell对象,如果有,就重用,如果没有,就传入这个字符串标识来初始化一个UITableViewCell对象。


重用UITableViewCell对象:

ios5 io6中分别提供了方法可以先注册cell,注册后就不需要在判断cell是否为空

- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);

- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);


注:

去对象池中取可重用的cell,系统提供了2个方法,分别是

- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;

- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);

2个方法如果在已经注册的情况下作用是一样的。

区别在于后一个方法会强制需要注册,否则会报错,即使代码中有判断cell为空得情况。


自定义UITableViewCell

一般有两种方式:

①用一个xib文件来描述UITableViewCell的内容。

②通过代码往UITableViewCellcontentView中添加子视图,在初始化方法(比如initinitWithStyle:reuseIdentifier:)中添加子控件,在layoutSubviews方法中分配子控件的位置和大小。

方法一:

1:首先计算需要显示在界面上cell的大小,然后显示相应的内容。当新出现一个cell的时候,去重用列队拿一个cell。然后去除上面显示的所有子视图(但是这样创建对象的消耗也会相应增加)。在重新创建对应位置的控件,进行赋值操作。


2:在初始化的时候,只需要创建对应的控件即可。然后在进行数据操作的时候设置相应控件的参数。


3:出现新cell的时候机会先调用显示,然后在调用高度。所以只要保证先获取对应位置cell的高度,然后在对各个控件进行赋值操作。


注意:每一个控件都要是全局的变量,这样才可以在该界面一直使用。


6)在表格视图界面点击一个按钮回到最开始的cell

    - (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;//使用这个方法即可


    [tableView scrollToRowAtIndexPath:[NSIndexPathindexPathForRow:0inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];//就是这个效果点击 button 之后回到顶部


7):UITableviewCell上知道自己点击了哪一个 button

-(void)buttonPressed:(id)sender { //button点击事件

    UITableViewCell *clickedCell = (UITableViewCell *)[[sender superviewsuperview];

    NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];

    ...

}


方法二:

- (void)buttonTappedAction:(id)sender {

    

    CGPoint buttonPosition = [sender convertPoint:CGPointZero  toView:self.tableView];                         

    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];   

    ...

    

}

8)  删除重用的cell的所有子视图

      从而得到一个没有特殊格式的cell,供其他cell重用。(可以很好的把对cell有过操作的效果全部去掉,然后拿过来重新使用,要保持之前的状态,只需要之前先存储好,在调用代理方法的时候获取数据显示即可)


    if (cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];

    }

    else

    {

        //删除cell的所有子视图

        while ([cell.contentView.subviews lastObject] != nil)

        {

            [(UIView*)[cell.contentView.subviews lastObject] removeFromSuperview];

        }

    }


9) 获取对应位置cell的方法.
   NSIndexPath   *cellIndexPath= [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
   UITableViewCell *cell=[tableView cellForRowAtIndexPath:cellIndexPath];

10)  
     设置 tableViewCell 间的分割线的颜色

  [tableView setSeparatorColor:[UIColor redColor]];


  改变UITableViewCell选中时背景色:

  cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.frame];

  cell.selectedBackgroundView.backgroundColor = [UIColor redColor];

 


你可能感兴趣的:(UITableView使用)