一, UITableView原理
1.
在
appDelegate
的协议方法中
,
创建应用程序的窗口对象
2. 创建 MainViewController 对象
MainViewController *mainVC= [[MainViewController alloc] init];
3. 创建导航控制器并将第二步创建的视图控制器对象作为导航控制器的根视图控制器
UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:mainVC];
4. 将第三步创建的导航控制器作为 window 的根视图控制器
self .window.rootViewController = navi;
5. MainViewController 通过调用自己 view 的 getter 方法来获取自己的视图对象 . 此时会调用懒加载的 view 方法 , 在 view 的 getter 方法内部 loadView 和 viewDidLoad 方法会被先后调用
//window 中的方法
- ( void )setRootViewController:
(UIViewController *)rootViewController
{
if (_rootViewController != rootViewController){
[_rootViewController release];
[ _rootViewController = rootViewController retain];
// 第一次调用 view 的 getter 方法 ( 懒加载 )
[ self addSubview:_rootViewController.view]
}
}
// 控制器中的方法
- (UIView *)view{
if (!_view){
[ self loadView];
[ self viewDidLoad];
}
return _view;
}
6. 在视图控制器的 viewDidLoad 方法内部 : 创建 CustomTableView 对象 , 并指定 customDelegate 为对应的根视图控制器 , 在由视图控制器的 view 调用 addSubview 将创建的表视图添加显示
在 MainViewController.m 文件中
- ( void )viewDidLoad{
[ super viewDidLoad];
CustomTableView *aTableView = [[CustomTableView alloc] initWithFrame: self .view.bounds];
aTableView.rowHeight = 60 ;
aTableView.customDelegate = self ;
[ self .view addSubview:aTableView];
[aTableView release];
}
7. 当视图控制器的视图通过 addSubview 添加表视图时 , 表视图重写的 didMoveToSuperview 方法就会被调用
8. 在执行表视图的 didMoveToSuperview 方法时 , 会调用表视图的私有方法 layoutCells 来布局表视图所要管理显示的单元格视图们
CustomTableView.m 文件中
< 1 > 创建私有方法 layoutCells
@interface CustomTableView ()
- ( void )layoutCells; // 为表视图布局单元格
@end
< 2 > 被移到父视图上自动调用
- ( void )didMoveToSuperview{
[ super didMoveToSuperview];
[ self layoutCells];
}
9. 在 layoutCells 方法内部 , 做如下操作 :
< 1 >numberOfRowsInCustomTable 方法来由代理对象指定表视图的行数
- (NSUInteger)numberOfRowsInCustomTable:(CustomTableView *)tableView{
return 100 ;} //MainViewController.m 方法中实现代理方法 , 及传返回值
< 2 > 借助行数以循环的形式有代理对象通过调用 tableView:cellForRowAtIndex: 来获取对应下表的单元格视图
- (CGFloat)tableView:(CustomTableView *)tableView heightForRowAtIndex:(NSInteger )index{ return 60 ;} //MainViewController.m 方法中实现代理方法 , 及传返回值
< 3 > 重新根据代理对象调用 tableView:heightForRowAtIndex: 获取到的对应行的行高来设定单元格的 frame
- (TableViewCell *)tableView:(CustomTableView *)tableView cellForRowAtIndex:(NSInteger)index{} /*MainViewController.m 方法中实现代理方法 , 及创建一个 TableViewCell, 然后传回给委托方 */ ( @optional )
< 4 > 为当前得到的单元格添加轻拍手势识别器
< 5 > 最后将单元格通过 addSubview 添加在表视图上
< 6 > 根据总行高计算表视图的 contentSize
CustomTableView.m 文件中
- ( void )layoutCells{
// 通过代理对象获得行数
< 1 > NSUInteger rows = [ self .customDelegate numberOfRowsInCustomTable: self ];
// 定义一个变量记录总高度
CGFloat totalHeight = 0 ;
CGFloat currentHeight = 0 ;
for ( int i = 0 ; i < rows; i++){
< 2 >TableViewCell *aCell = [ self .customDelegate tableView: self cellForRowAtIndex:i];
< 3 > if ([ self .customDelegate respondsToSelector: @selector (tableView:heightForRowAtIndex:)]) {
currentHeight = [ self .customDelegate tableView: self heightForRowAtIndex:i];
}
aCell.index = i;
aCell.frame = CGRectMake( 0 , totalHeight, CGRectGetWidth( self .bounds), currentHeight);
< 4 > UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector (handleTap:)];
[aCell addGestureRecognizer:tap];
[tap release];
< 5 > [ self addSubview: aCell];
totalHeight += currentHeight;
}
< 6 > // 根据总高度在设定 contensize
self .contentSize = CGSizeMake(CGRectGetWidth( self .bounds), totalHeight);
}
10. 当我们轻拍某一行单元格时 , 该单元格上轻拍手势识别器就会响应 , 在其对应的 handleTap: 方法内部 , 并没有直接处理轻拍 , 而是通过代理对象执行
tableView:didSelectRowAtIndex: 方法来处理单元格的点击
CustomTableView.m 文件中
- ( void )handleTap:(UITapGestureRecognizer *)sender{
// 当单元格的轻拍手势被识别后 , 当前表视图并不直接处理轻拍 , 而是交由代理对象通过执行协议方法作出最终处理
TableViewCell *aCell = (TableViewCell *)[sender view]; // 获取被轻拍的单元格 ( 轻拍手势自带 view 属性 )
if ( self .customDelegate && [ self .customDelegate respondsToSelector: @selector (tableView:didSelectRowAtIndex:)]) {
[ self .customDelegate tableView: self didSelectRowAtIndex:aCell.index];
}
}
在 MainViewController.h 中实现方法 ( @optional )
- ( void )tableView:(CustomTableView *)tableView didSelectRowAtIndex:(NSInteger)index{
}
最先创建一个封装好的 TableViewCell 类
@interface TableViewCell : UIView
@property ( nonatomic , retain ) UIImageView *imageView;
@property ( nonatomic , retain ) UILabel *textLable;
@property ( nonatomic , assign )NSInteger index; // 单元格在表视图中的下标
@end
TableViewCell.m 中实现
@implementation TableViewCell
- ( void )dealloc{
[_imageView release];
[_textLable release];
[ super dealloc];
}
- ( instancetype )initWithFrame:(CGRect)frame{
self = [ super initWithFrame:frame];
if ( self ) {
}
return self ;
}
- (UIImageView *)imageView{
if (!_imageView) {
self .imageView = [[[UIImageView alloc] initWithFrame:CGRectZero] autorelease];
[ self addSubview:_imageView];
}
return _imageView;
}
- (UILabel *)textLable{
if (!_textLable){
self .textLable = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
_textLable.font = [UIFont systemFontOfSize: 17 ];
[ self addSubview:_textLable];
}
return _textLable;
}
// 在外调用 imageView,_textLable 的 getter 方法时 ,self.bounds/frame 还没创建
- ( void )didMoveToSuperview{
[ super didMoveToSuperview];
CGFloat x = 5 ;
if (_imageView) {
self .imageView.frame = CGRectMake( 5 , 5 , CGRectGetHeight( self .bounds) - 10 , CGRectGetHeight( self .bounds) - 10 );
x = _imageView.frame.origin.x + CGRectGetWidth(_imageView.frame) + 5 ;
}
if (_textLable) {
self .textLable.frame = CGRectMake(x, 5 , CGRectGetWidth( self .bounds) - x - 5 , CGRectGetH eight( self .bounds) - 10 );
}
UIView *seperator = [[UIView alloc] initWithFrame:CGRectMake( 0 , CGRectGetHeight( self .frame) - 1 , CGRectGetWidth( self .frame) - 1 , 1 )];
seperator.backgroundColor = [UIColor lightGrayColor];
[ self addSubview:seperator];
[seperator release];
}
2. 创建 MainViewController 对象
MainViewController *mainVC= [[MainViewController alloc] init];
3. 创建导航控制器并将第二步创建的视图控制器对象作为导航控制器的根视图控制器
UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:mainVC];
4. 将第三步创建的导航控制器作为 window 的根视图控制器
self .window.rootViewController = navi;
5. MainViewController 通过调用自己 view 的 getter 方法来获取自己的视图对象 . 此时会调用懒加载的 view 方法 , 在 view 的 getter 方法内部 loadView 和 viewDidLoad 方法会被先后调用
//window 中的方法
- ( void )setRootViewController:
(UIViewController *)rootViewController
{
if (_rootViewController != rootViewController){
[_rootViewController release];
[ _rootViewController = rootViewController retain];
// 第一次调用 view 的 getter 方法 ( 懒加载 )
[ self addSubview:_rootViewController.view]
}
}
// 控制器中的方法
- (UIView *)view{
if (!_view){
[ self loadView];
[ self viewDidLoad];
}
return _view;
}
6. 在视图控制器的 viewDidLoad 方法内部 : 创建 CustomTableView 对象 , 并指定 customDelegate 为对应的根视图控制器 , 在由视图控制器的 view 调用 addSubview 将创建的表视图添加显示
在 MainViewController.m 文件中
- ( void )viewDidLoad{
[ super viewDidLoad];
CustomTableView *aTableView = [[CustomTableView alloc] initWithFrame: self .view.bounds];
aTableView.rowHeight = 60 ;
aTableView.customDelegate = self ;
[ self .view addSubview:aTableView];
[aTableView release];
}
7. 当视图控制器的视图通过 addSubview 添加表视图时 , 表视图重写的 didMoveToSuperview 方法就会被调用
8. 在执行表视图的 didMoveToSuperview 方法时 , 会调用表视图的私有方法 layoutCells 来布局表视图所要管理显示的单元格视图们
CustomTableView.m 文件中
< 1 > 创建私有方法 layoutCells
@interface CustomTableView ()
- ( void )layoutCells; // 为表视图布局单元格
@end
< 2 > 被移到父视图上自动调用
- ( void )didMoveToSuperview{
[ super didMoveToSuperview];
[ self layoutCells];
}
9. 在 layoutCells 方法内部 , 做如下操作 :
< 1 >numberOfRowsInCustomTable 方法来由代理对象指定表视图的行数
- (NSUInteger)numberOfRowsInCustomTable:(CustomTableView *)tableView{
return 100 ;} //MainViewController.m 方法中实现代理方法 , 及传返回值
< 2 > 借助行数以循环的形式有代理对象通过调用 tableView:cellForRowAtIndex: 来获取对应下表的单元格视图
- (CGFloat)tableView:(CustomTableView *)tableView heightForRowAtIndex:(NSInteger )index{ return 60 ;} //MainViewController.m 方法中实现代理方法 , 及传返回值
< 3 > 重新根据代理对象调用 tableView:heightForRowAtIndex: 获取到的对应行的行高来设定单元格的 frame
- (TableViewCell *)tableView:(CustomTableView *)tableView cellForRowAtIndex:(NSInteger)index{} /*MainViewController.m 方法中实现代理方法 , 及创建一个 TableViewCell, 然后传回给委托方 */ ( @optional )
< 4 > 为当前得到的单元格添加轻拍手势识别器
< 5 > 最后将单元格通过 addSubview 添加在表视图上
< 6 > 根据总行高计算表视图的 contentSize
CustomTableView.m 文件中
- ( void )layoutCells{
// 通过代理对象获得行数
< 1 > NSUInteger rows = [ self .customDelegate numberOfRowsInCustomTable: self ];
// 定义一个变量记录总高度
CGFloat totalHeight = 0 ;
CGFloat currentHeight = 0 ;
for ( int i = 0 ; i < rows; i++){
< 2 >TableViewCell *aCell = [ self .customDelegate tableView: self cellForRowAtIndex:i];
< 3 > if ([ self .customDelegate respondsToSelector: @selector (tableView:heightForRowAtIndex:)]) {
currentHeight = [ self .customDelegate tableView: self heightForRowAtIndex:i];
}
aCell.index = i;
aCell.frame = CGRectMake( 0 , totalHeight, CGRectGetWidth( self .bounds), currentHeight);
< 4 > UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector (handleTap:)];
[aCell addGestureRecognizer:tap];
[tap release];
< 5 > [ self addSubview: aCell];
totalHeight += currentHeight;
}
< 6 > // 根据总高度在设定 contensize
self .contentSize = CGSizeMake(CGRectGetWidth( self .bounds), totalHeight);
}
10. 当我们轻拍某一行单元格时 , 该单元格上轻拍手势识别器就会响应 , 在其对应的 handleTap: 方法内部 , 并没有直接处理轻拍 , 而是通过代理对象执行
tableView:didSelectRowAtIndex: 方法来处理单元格的点击
CustomTableView.m 文件中
- ( void )handleTap:(UITapGestureRecognizer *)sender{
// 当单元格的轻拍手势被识别后 , 当前表视图并不直接处理轻拍 , 而是交由代理对象通过执行协议方法作出最终处理
TableViewCell *aCell = (TableViewCell *)[sender view]; // 获取被轻拍的单元格 ( 轻拍手势自带 view 属性 )
if ( self .customDelegate && [ self .customDelegate respondsToSelector: @selector (tableView:didSelectRowAtIndex:)]) {
[ self .customDelegate tableView: self didSelectRowAtIndex:aCell.index];
}
}
在 MainViewController.h 中实现方法 ( @optional )
- ( void )tableView:(CustomTableView *)tableView didSelectRowAtIndex:(NSInteger)index{
}
最先创建一个封装好的 TableViewCell 类
@interface TableViewCell : UIView
@property ( nonatomic , retain ) UIImageView *imageView;
@property ( nonatomic , retain ) UILabel *textLable;
@property ( nonatomic , assign )NSInteger index; // 单元格在表视图中的下标
@end
TableViewCell.m 中实现
@implementation TableViewCell
- ( void )dealloc{
[_imageView release];
[_textLable release];
[ super dealloc];
}
- ( instancetype )initWithFrame:(CGRect)frame{
self = [ super initWithFrame:frame];
if ( self ) {
}
return self ;
}
- (UIImageView *)imageView{
if (!_imageView) {
self .imageView = [[[UIImageView alloc] initWithFrame:CGRectZero] autorelease];
[ self addSubview:_imageView];
}
return _imageView;
}
- (UILabel *)textLable{
if (!_textLable){
self .textLable = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
_textLable.font = [UIFont systemFontOfSize: 17 ];
[ self addSubview:_textLable];
}
return _textLable;
}
// 在外调用 imageView,_textLable 的 getter 方法时 ,self.bounds/frame 还没创建
- ( void )didMoveToSuperview{
[ super didMoveToSuperview];
CGFloat x = 5 ;
if (_imageView) {
self .imageView.frame = CGRectMake( 5 , 5 , CGRectGetHeight( self .bounds) - 10 , CGRectGetHeight( self .bounds) - 10 );
x = _imageView.frame.origin.x + CGRectGetWidth(_imageView.frame) + 5 ;
}
if (_textLable) {
self .textLable.frame = CGRectMake(x, 5 , CGRectGetWidth( self .bounds) - x - 5 , CGRectGetH eight( self .bounds) - 10 );
}
UIView *seperator = [[UIView alloc] initWithFrame:CGRectMake( 0 , CGRectGetHeight( self .frame) - 1 , CGRectGetWidth( self .frame) - 1 , 1 )];
seperator.backgroundColor = [UIColor lightGrayColor];
[ self addSubview:seperator];
[seperator release];
}
二,UITableView编辑
1.
创建
UITableViewController
子类
MainViewController
在 MainViewController.m 文件中
//UITableViewController 自动创建 UITableView, 实现 UITableViewDataSource,UITableViewDelegate 协议
@interface MainViewController ()
@property ( nonatomic , retain )NSMutableArray *dadaSource;
@end
@implementation MainViewController
- ( void )dealloc{
[_dadaSource release];
[ super dealloc];
}
- (NSMutableArray *)dadaSource{
if (!_dadaSource) {
self .dadaSource = [NSMutableArray array];
}
return _dadaSource;
}
2. 不用创建 tableView, 父类在 viewDidLoad 中已创建好
- ( void )viewDidLoad {
[ super viewDidLoad];
[ self .tableView registerClass:[UITableViewCell class ] forCellReuseIdentifier: @"CELL" ];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// 如果表视图需要通过编辑按钮来切换其编辑状态 , 则只需要调用 editButtonItem 方法来获取一个编辑按钮 , 添加在导航条上
self .navigationItem.rightBarButtonItem = self .editButtonItem;
[ self .dadaSource addObject: @" 第一行 " ];
[ self .dadaSource addObject: @" 添加最新一行 " ];
}
3. 指定分区和行数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1 ;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return self .dadaSource.count;
}
4. UITableViewCell 懒加载
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: @"CELL" forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = self .dadaSource[indexPath.row];
//cell.textLabel.text = [NSString stringWithFormat:@" 第 %ld 行单元格 ",indexPath.row + 1];
return cell;
}
5. 表视图删除 , 添加步骤
UITableViewDelegate 中的协议
< 1 > 让 tableView 处于编辑状态
- ( void )setEditing:( BOOL )editing animated:( BOOL )animated{
[_tableView setEditing:!_tableView.isEditing animated: true ];
}
< 2 > 指定 tableView 哪些行可以编辑
// Override to support conditional editing of the table view.
- ( BOOL )tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
// 该协议方法是询问代理对象对应行是否允许被进行编辑操作 .( 通过重写协议方法可以让表视图支持条件编辑 , 默认为全部允许编辑 )
//if (indexPath.row == 0) {
// return NO;
//}
return YES ;
}
< 3 > 指定 tableView 编辑的样式(添加、删除)
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
// 为表视图的对应行指定编辑样式 , 样式有 : 插入 , 删除 , 选中 , 和无
if (indexPath.row == self .dadaSource.count - 1 ) {
return UITableViewCellEditingStyleInsert; // 添加样式
}
return UITableViewCellEditingStyleDelete; // 删除样式
//return UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert; // 选择样式
}
< 4 > 编辑完成(先操作数据源,再修改 UI )
// Override to support editing the table view.
- ( void )tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// 在完成编辑的协议方法中 , 无论是删除样式还是插入样式都需要先操作数据源中的数据 , 在通过表视图的相关方法删除对应行或者插入新的行
if (editingStyle == UITableViewCellEditingStyleDelete) {
//1. Delete the row from the data source 删除对应行在数组中数据
[ self .dadaSource removeObjectAtIndex:indexPath.row];
//2. 删除单元格 UITableViewRowAnimationFade 这里用哪个枚举值都是一个效果
[tableView deleteRowsAtIndexPaths: @[ indexPath ] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
//1. 数组里插入新的数据
[ self .dadaSource insertObject: @" 新插入的数据 " atIndex:indexPath.row];
//2. 插入对应行
[tableView insertRowsAtIndexPaths: @[ indexPath ] withRowAnimation:UITableViewRowAnimationFade];
}
}
6. 表视图移动
< 1 > 让 tableView 处于编辑状态
- ( void )setEditing:( BOOL )editing animated:( BOOL )animated;
< 2 > 指定 tableView 哪些行可以移动
// Override to support conditional rearranging of the table view.
- ( BOOL )tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
if (indexPath.row == 0 ) {
return NO ;}
return YES ;
}
< 3 > 移动完成
// Override to support rearranging the table view.
- ( void )tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
// 在界面移动完成时 , 通过次协议方法来更新数据源中的元素顺序
//1. 先获取被移动的数据
id object = [ self .dadaSource[fromIndexPath.row] retain];
//2. 将该对象从数组中移除
[ self .dadaSource removeObjectAtIndex:fromIndexPath.row];
//3. 将该对象插入到指定下标
[ self .dadaSource insertObject:object atIndex:toIndexPath.row];
//4. 释放对象的所有权
[object release];
}
< 4 > 监测移动过程,实现限制跨区移动
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
在 MainViewController.m 文件中
//UITableViewController 自动创建 UITableView, 实现 UITableViewDataSource,UITableViewDelegate 协议
@interface MainViewController ()
@property ( nonatomic , retain )NSMutableArray *dadaSource;
@end
@implementation MainViewController
- ( void )dealloc{
[_dadaSource release];
[ super dealloc];
}
- (NSMutableArray *)dadaSource{
if (!_dadaSource) {
self .dadaSource = [NSMutableArray array];
}
return _dadaSource;
}
2. 不用创建 tableView, 父类在 viewDidLoad 中已创建好
- ( void )viewDidLoad {
[ super viewDidLoad];
[ self .tableView registerClass:[UITableViewCell class ] forCellReuseIdentifier: @"CELL" ];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// 如果表视图需要通过编辑按钮来切换其编辑状态 , 则只需要调用 editButtonItem 方法来获取一个编辑按钮 , 添加在导航条上
self .navigationItem.rightBarButtonItem = self .editButtonItem;
[ self .dadaSource addObject: @" 第一行 " ];
[ self .dadaSource addObject: @" 添加最新一行 " ];
}
3. 指定分区和行数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1 ;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return self .dadaSource.count;
}
4. UITableViewCell 懒加载
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: @"CELL" forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = self .dadaSource[indexPath.row];
//cell.textLabel.text = [NSString stringWithFormat:@" 第 %ld 行单元格 ",indexPath.row + 1];
return cell;
}
5. 表视图删除 , 添加步骤
UITableViewDelegate 中的协议
< 1 > 让 tableView 处于编辑状态
- ( void )setEditing:( BOOL )editing animated:( BOOL )animated{
[_tableView setEditing:!_tableView.isEditing animated: true ];
}
< 2 > 指定 tableView 哪些行可以编辑
// Override to support conditional editing of the table view.
- ( BOOL )tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
// 该协议方法是询问代理对象对应行是否允许被进行编辑操作 .( 通过重写协议方法可以让表视图支持条件编辑 , 默认为全部允许编辑 )
//if (indexPath.row == 0) {
// return NO;
//}
return YES ;
}
< 3 > 指定 tableView 编辑的样式(添加、删除)
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
// 为表视图的对应行指定编辑样式 , 样式有 : 插入 , 删除 , 选中 , 和无
if (indexPath.row == self .dadaSource.count - 1 ) {
return UITableViewCellEditingStyleInsert; // 添加样式
}
return UITableViewCellEditingStyleDelete; // 删除样式
//return UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert; // 选择样式
}
< 4 > 编辑完成(先操作数据源,再修改 UI )
// Override to support editing the table view.
- ( void )tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// 在完成编辑的协议方法中 , 无论是删除样式还是插入样式都需要先操作数据源中的数据 , 在通过表视图的相关方法删除对应行或者插入新的行
if (editingStyle == UITableViewCellEditingStyleDelete) {
//1. Delete the row from the data source 删除对应行在数组中数据
[ self .dadaSource removeObjectAtIndex:indexPath.row];
//2. 删除单元格 UITableViewRowAnimationFade 这里用哪个枚举值都是一个效果
[tableView deleteRowsAtIndexPaths: @[ indexPath ] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
//1. 数组里插入新的数据
[ self .dadaSource insertObject: @" 新插入的数据 " atIndex:indexPath.row];
//2. 插入对应行
[tableView insertRowsAtIndexPaths: @[ indexPath ] withRowAnimation:UITableViewRowAnimationFade];
}
}
6. 表视图移动
< 1 > 让 tableView 处于编辑状态
- ( void )setEditing:( BOOL )editing animated:( BOOL )animated;
< 2 > 指定 tableView 哪些行可以移动
// Override to support conditional rearranging of the table view.
- ( BOOL )tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
if (indexPath.row == 0 ) {
return NO ;}
return YES ;
}
< 3 > 移动完成
// Override to support rearranging the table view.
- ( void )tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
// 在界面移动完成时 , 通过次协议方法来更新数据源中的元素顺序
//1. 先获取被移动的数据
id object = [ self .dadaSource[fromIndexPath.row] retain];
//2. 将该对象从数组中移除
[ self .dadaSource removeObjectAtIndex:fromIndexPath.row];
//3. 将该对象插入到指定下标
[ self .dadaSource insertObject:object atIndex:toIndexPath.row];
//4. 释放对象的所有权
[object release];
}
< 4 > 监测移动过程,实现限制跨区移动
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
三,UIViewController建立
#import <Foundation/Foundation.h>
@protocol _____ <NSObject>
UIViewController
一个应用程序分配 80 M 内存
回收内存 :dealloc
UIViewController 是 MVC 设计模式的核⼼。
MVC 是一个框架级的设计模式。
M 是 Model, 主要用于建⽴数据模型 ( 即数据的结构 )
V 是 View, 我们能看到的所有控件都是 view,view 主要的功能是展示数据。
C 是控制器 , 主要是控制 M 和 V 的通信。是视图管理者和代理对象 , 响应需要得到 view 的指示 . 向 Model 层要数据 , 向 View 分发数据
系统的可维护性、 可扩展性增强
视图控制器 -> 模块化管理当前视图界面 ( 是靠里面自带 view 的属性来管理 )
-> view 属性 -> ( 创建 -> 1. 通过 view 属性的懒加载 2.l oadView 方法 ) -> 创建子视图 用 viewDidLoad 方法
-> 被管理的子视图需要 target/action 视图控制器来提供
-> 被管理的子视图需要 delegate 视图控制器成为代理对象并实现相关方法
UIViewController 生命周期 -> init
-> loadView
-> viewDidLoad
-> viewWillAppear 视图将要出现
-> viewDidAppear
-> viewWillDisappear 视图将要从界面移除
-> viewDidDisappear
-> dealloc
exceptin_bad_access 出现异常访问 ( 野指针 )
2015 - 09 - 02 10 : 08 : 49.076 UILesson07[ 624 : 25724 ] Application windows are expected to have a root view controller at the end of application launch
应用程序的窗口对象 在应用程序加载完成后 应该加一个视图控制器
1. 定义 UIViewController 的子类
UIViewController 自带用于布局子视图的视图
2. 如果要自己建立一个视图控制器的 view 视图 :loadView
MainViewController.h
@interface MainViewController : UIViewController
@end
MainViewController.m
子类重写父类的方法 ( 相当于对父类实现的方法做以扩展 ), 先调用子类方法 ( 子类方法要先把父类方法继承过来 )
//loadView 负责加载一个与屏幕尺寸一样大的 UIView 对象 , 当视图控制器的 view 属性的 getter 方法第一次调用时会调用 loadView 方法加载一个视图
- ( void )loadView{
// 加载视图 ( 父类方法会加载一个 window 一样大的全屏视图 )
[ super loadView];
// 父类中的方法
//self.view = [[[UIView alloc ] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// 如果要自定义视图控制器自带的 view, 则不需要通过 super 调用父类实现的 loadView 方法 , 而是直接给 view 属性赋值一个视图对象
UIImageView *imageView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 打开 imageView 的交互
imageView.userInteractionEnabled = YES ;
imageView.image = [UIImage imageNamed: @"1.jpg" ];
self .view = imageView;
[imageView release];
NSLog( @"%s" , __FUNCTION__ );
}
3. // 加载完成 ,viewDidLoad 方法中为视图控制器的视图添加要被管理的子视图
-( void )viewDidLoad{
[ super viewDidLoad];
//Do any additional setup after loading the view;
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeSystem];
aButton.frame = CGRectMake( 0 , 0 , 100 , 60 );
aButton.center = self .view.center;
aButton.backgroundColor = [UIColor redColor];
[aButton setTitle: @" 按钮 " forState:UIControlStateNormal];
//self 是当前视图控制器对象
// 如果视图是由视图控制器来管理的 , 那么如果这个视图需要对事件做出响应时 , 对应的视图控制器就是其响应者 , 并提供响应方法
[aButton addTarget: self action: @selector (handleButtonAction:) forControlEvents:UIControlEventTouchUpInside];
// 视图控制器管理的视图如果需要通过 target/action 或者 delegate 来处理对应的操作时 , 有该视图控制器提供处理方法 , 或者称为代理对象实现对应的协议方法
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector (handleTap:)];
[ self .view addGestureRecognizer:tap];
[ self .view addSubview:aButton];
}
// 另一种按按钮中的 return 键完成收回键盘的方法
谁管理视图谁给视图提供响应方法或者代理
@interface MainViewController ()<UITextFieldDelegate>
@end
text.keyboardType = UIReturnKeyDone;
text.delegate = self ;
- ( BOOL )textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES ;
}
UIViewController.m 中有意私有属性 view
父类中 view 属性是私有的 , 不能继承 , 不能重写方法 , 但是父类中方法是可以重写的 , 并优先调用子类重写的方法 , 如果上面子类的 loadView 方法中不 [ super loadView], 就不会实现父类中 loadView 方法 , 不会创建 view, 一直 !_view 会使其子类一直调用父类中 view 的 getter 方法造成死循环 ;
MainViewController 的父类 UIViewController 中 view 的 getter 方法 :
- (UIView *)view{
if (!_view){
[ self loadView];
[ self viewDidLoad];
}
return _view;
}
4. 创建视图控制器对象 , 作为 window 的根视图控制器
AppDelegate.m 中
// 创建视图控制器对象
MainViewController *mainVC = [[MainViewController alloc] init];
// 指定给 window, 作为 window 的根视图控制器
self .window.rootViewController = mainVC;
[mainVC release];
以上 self .window 调用 RootViewCintroller 的 setter 方法
- [( void )setRootViewController:
(UIViewController *)rootViewController
{
if (_rootViewController != rootViewController){
[_rootViewController release];
[ _rootViewController = rootViewController retain];
// 第一次调用 view 的 getter 方法 ( 懒加载 )
[ self addSubview:_rootViewController.view]
}
}
不能写成 [ self .window addSubview:mainVC.view]
// 点击 text text.delegate 指向一个已经被释放内容的地址
生命周期
- ( void )viewWillAppear:( BOOL )animated{
[ super viewWillAppear:animated];
NSLog( @"%s" , __FUNCTION__ );
}
- ( void )viewDidAppear:( BOOL )animated{
[ super viewDidDisappear:animated];
NSLog( @"%s" , __FUNCTION__ );
}
- ( void )viewWillDisappear:( BOOL )animated{
[ super viewWillDisappear:animated];
NSLog( @"%s" , __FUNCTION__ );
}
- ( void )viewDidDisappear:( BOOL )animated{
[ super viewDidDisappear:animated];
NSLog( @"%s" , __FUNCTION__ );
}
-( void )dealloc{
NSLog( @"%s" , __FUNCTION__ );
[ super dealloc];
}
@end