您的位置:首页 > 产品设计 > UI/UE

UITableView使用

2015-07-06 22:56 711 查看
一: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个分区,多分区从最后一个开始自动计算,依次向前).section为1时,计算分区中cell的高度,section为0时计算分区中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:nibNameOrNilbundle: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{
   return50;
}

//选中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{
    staticNSString *cellIde =@"Cell";//声明cell的可重用标识符

   
//根据可重用标识符,到tableView的重用队列中,取cell对象
   UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier: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 = [arrayobjectAtIndex: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)UITableViewCell横线左端对其实现:

方法一:在初始化的表格的时候添加如下代码
   if ([_tableViewrespondsToSelector:@selector(setSeparatorInset:)])
{

        [_tableViewsetSeparatorInset:UIEdgeInsetsZero];
    }
   if ([_tableViewrespondsToSelector:@selector(setLayoutMargins:)])
{

        [_tableViewsetLayoutMargins:UIEdgeInsetsZero];
    }
实现代理方法

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

    
   if ([cellrespondsToSelector:@selector(setSeparatorInset:)])
{
        [cellsetSeparatorInset:UIEdgeInsetsZero];
    }
   if ([cellrespondsToSelector:@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

 

          


 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.tableViewbeginUpdates];

   
//要记得对分区进行删除操作。self.dataArray是一个二维数组。及实现多分区情况下的删除操纵。
   if([[self.dataArrayobjectAtIndex:
indexPath.section]count]  >
1)
    {

        [_tableViewdeleteRowsAtIndexPaths:[NSArrayarrayWithObject:[NSIndexPathindexPathForRow:indexPath.rowinSection:indexPath.section]]withRowAnimation:UITableViewRowAnimationLeft];

     }
   else
    {

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

        [_tableViewdeleteSections:[NSIndexSetindexSetWithIndex:indexPath.section]

                
withRowAnimation:UITableViewRowAnimationLeft];
    }

    

   
//注意:数据源在beginUpdates前面操作和在后面操作,并没有什么关系,关键一点是数据源要随着删除,插入的数据与之对应改变,不然就会出现错误。但是不能够把对数据源的操作放到endUpdates。
   NSMutableArray *arr=self.dataArray[indexPath.section];
    [arrremoveObjectAtIndex:indexPath.row];
    [self.dataArrayremoveObjectAtIndex:indexPath.section];
   if(arr.count!=0)
    [self.dataArrayinsertObject:arr
atIndex:indexPath.section];

    [self.tableViewendUpdates];

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 = [[[UITableViewCellalloc] initWithStyle: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 *)identifierNS_AVAILABLE_IOS(5_0);
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifierNS_AVAILABLE_IOS(6_0);

注:
去对象池中取可重用的cell,系统提供了2个方法,分别是
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier;
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath
*)indexPath NS_AVAILABLE_IOS(6_0);
这2个方法如果在已经注册的情况下作用是一样的。
区别在于后一个方法会强制需要注册,否则会报错,即使代码中有判断cell为空得情况。

2 自定义UITableViewCell
一般有两种方式:
①用一个xib文件来描述UITableViewCell的内容。
②通过代码往UITableViewCell的contentView中添加子视图,在初始化方法(比如init、initWithStyle: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 superview] superview];
    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=[tableViewcellForRowAtIndexPath:cellIndexPath];

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

  [tableView setSeparatorColor:[UIColorredColor]];

  改变UITableViewCell选中时背景色:
  cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.frame];

  cell.selectedBackgroundView.backgroundColor = [UIColorredColor];

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: