总结:
其实navigationController很简单,其往往就是应用程序委托的根视图控制器
且其往往就是通过第一个显示的视图控制器来初始化自己。
而后的工作,就是pushViewController,popViewController。。。
[self.navigationController pushViewController: controller animated:YES];
[self.navigationController popViewController Animated: YES];
表视图也很简单,只要实现其特定的数据源方法,委托方法,即可实现。
numberOfRowsInSection
cellForRowAtIndexPath
通过cell.accessoryType,cell.accessoryView控制其显示类型。
通过editingStyleForRowAtIndexPath的返回值来控制编辑类型(移动或删除插入)
实现委托方法moveRowAtIndexPath:toIndexPath:实现移动
实现委托方法commitEditingStyle:forRowAtIndexPath:实现插入删除
对比第8章内容:
UITableViewController,内建有一个UITableView,无需xib文件
如果需要自定义cell样式,可在dequeue获取cell后,向其contentsView addSubView
而如果是自定义的Controller,关联一个xib,其中有一个UITableView
此时,如果要自定义Cell样式,有两种方法:
1.代码方式,创建UITableViewCell子类,在其init中添加子视图,
在controller中的viewDidLoad中为此cell registerClass,进而可以获取到该类型的cell
2.xib方式,创建一个xib,包含一个UITableViewCell, 利用IB修改界面
然后在controller中的viewDidLoad中为此cell registerNib,进而可以获取到该类型的cell
疑问:那为啥不像本章例子一样,直接在Controller的cellForRowAtIndexPath中,
获取常规的Cell后,再向其contentsView addSubView?难道是为了Cell的复用考虑?
==========================================================================
stack:
base:第一个压入栈的对象,栈底
top:最后一个压入栈的对象,栈顶
push:压栈
pop:弹出
导航控制器维护着一个控制器栈。
title是当前栈顶controller的title属性
左边button是上一个controller的title属性,
如果点击,则会返回该controller,相当于将当前的栈顶弹出。
accessory icons:通常有以下两种类型
disclosure indicator :右侧灰色小箭头
detail disclosure button:右侧细节按钮(选择该行会触发一个动作;点击该按钮,会触发另外一个动作。)
这两种为默认的右侧视图,还可以通过cell.detailView定制自己的控件
when to use disclosure indicators and detail disclosure buttons:
If you want to offer a single choice for a row tap, don‘t use an accessory icon if a row tap will only lead to a more detailed view of that row.
如果点击该行,只会看到该行的细节,而不会引导到另一个view,则不要使用accessory icon(无右侧视图)
Mark the row with a disclosure indicator (gray arrow) if a row tap will lead to a new view (nota detail view).
如果点击该行,会引导到另外一个view(不是细节视图),则使用disclosure indicator(灰色箭头)
If you want to offer two choices for a row, mark the row with a detail disclosure button.
This allows the user to tap on the row for a new view or the disclosurebutton for more details.
如果想实现两种,则使用detail disclosure button,点击该行,会引导到另一个视图。点击该按钮,会显示细节。
accessory view
usually holds the accessory icon 通常包含accessory icon
不过也能此view上实现更多功能。
第一种:accessory icons
第二种:check mark
第三种:accessory view
第四种:move
第五种:delete
第六种:editable detail
The Nav Application’s Skeleton
command+shift+N创建工程
command+N创建文件
When we subclass UITableViewController, we inherit some nice functionality from that class that will create a table view with no need for a nib file.
apple提供的UITableViewController中,已经包含了一个UITableView,无需nib文件
1.创建空工程
2.创建类BIDFirstLevelViewController继承自UITableViewController
3.用此控制器作为一级视图,创建导航控制器,并将导航控制器设置为应用程序委托的根视图控制器
BIDFirstLevelViewController *first = [[BIDFirstLevelViewController alloc] init];
UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController :first];
self.window.rootViewController = navigation;
使用一级视图BIDFirstLevelViewController 作为初始视图来创建构造导航控制器
导航控制器作为应用程序窗口的根视图
第二级viewcontroller,继承自UITableViewController,拥有一个UIImage
@interface BIDSecondLevelViewController : UITableViewController
@property (strong, nonatomic) UIImage *rowImage;
@end
第一级viewcontroller,继承自UITableViewController,拥有一个二级viewController数组
@interface BIDFirstLevelViewController : UITableViewController
@property (copy, nonatomic) NSArray *controllers; //一系列二级controllers
@end
使用数据源方法获取cell的时候,根据第二级视图的title和image,赋值cell属性
// Configure the cell...
BIDSecondLevelViewController *controller = self.controllers[indexPath.row];
cell.textLabel.text = controller.title;
cell.imageView.image = controller.rowImage;
使用委托方法,当点击一级视图的某行,navi到某一个二级视图
注意,怎么通过一级视图,获取到其导航控制器
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BIDSecondLevelViewController *controller = self.controllers[indexPath.row];
[self.navigationController pushViewController: controller animated:YES];
}
self.navigationController这个属性何时被赋值的?难道是使用一级视图构建导航控制器的时候??
创建第一个二级视图控制器
创建BIDDisclosureButtonViewController继承自BIDSecondLevelViewCont
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
@property (copy, nonatomic) NSArray *movies; //显示的不同行
@property (strong, nonatomic) BIDDisclosureDetailViewController *detailController; //保持的细节展示视图,为了复用
控制器的初始化
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.title = @"Disclosure Buttons";
self.rowImage = [UIImage imageNamed:@"disclosureButtonControllerIcon.png"];
self.movies = @[@"Toy Story", @"A Bug’s Life", @"Toy Story 2",
@"Monsters, Inc.", @"Finding Nemo", @"The Incredibles",
@"Cars", @"Ratatouille", @"WALL-E", @"Up",
@"Toy Story 3", @"Cars 2", @"Brave"];
self.detailController = [[BIDDisclosureDetailViewController alloc] init];
}
return self;
}
记得为该表视图控制器注册可复用表视图单元
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.tableView registerClass:[UITableViewCell class]
forCellReuseIdentifier: CellIdentifier]; //为什么都是同一个字符串?底层难道也会为不同的控制器维护不同的队列?
}
数据源方法
#pragma mark - Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [self.movies count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; //detail disclosure button 类型
cell.textLabel.text = self.movies[indexPath.row];
return cell;
}
委托方法
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Hey, do you see the disclosure button?"
message:@"Touch that to drill down instead."
delegate:nil
cancelButtonTitle:@"Won’t happen again"
otherButtonTitles:nil];
[alert show];
} //点击该行时,弹出警告视图
点击detail disclosure button
- (void)tableView:(UITableView *)tableView
accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
NSString *selectedMovie = self.movies[indexPath.row];
NSString *detailMessage = [[NSString alloc] initWithFormat:@"This is details for %@.", selectedMovie];
self.detailController.message = detailMessage;
self.detailController.title = selectedMovie;
[self.navigationController pushViewController: self.detailController animated:YES];
//在导航视图中,所有需要显示的视图,都采用pushViewController方法
}
细节视图控制器=========================================================
创建无xib文件,继承自UIViewController的用于展示细节的控制器BIDDisClosureDetailViewController
在修改loadView,让Controller的view为一个UILabel
@interface BIDDisclosureDetailViewController : UIViewController
@property (weak, readonly, nonatomic) UILabel *label;
@property (copy, nonatomic) NSString *message;
@end
readonly 只有getter,没有setter
- (UILabel *)label;
{
return (id)self.view;
}
- (void)loadView;
{
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = 0; //这样即可显示任意多行的文本
label.textAlignment = NSTextAlignmentCenter;
self.view = label;
}
- (void)viewWillAppear:(BOOL)animated;
{
[super viewWillAppear:animated];
self.label.text = self.message;
}
此控制器的view,就是一个全屏的uilabel
注意,loadView和viewDidLoad只会在第一次加载视图的时候调用,
如果我们打算只初始化一次此Controller,那么需要在viewWillAppear中更新显示的数据
因为当第一次显示此细节展示控制器时候的时候,已经进行了loadView和viewDidLoad
而且此控制器是被二级视图控制器保持着的,并不会被释放。
所以需要在viewWillAppear中更新uilabel显示的内容。
这样,以上显示细节视图控制器,就可以被复用,
当点击不同的cell的accessory button的时候,显示出来的其实是同一个detail viewcontroller
细节视图控制器=========================================================
第二个视图控制器 BIDCheckListViewController
cell.accessoryType = UITableViewCellAccessoryCheckmark;
@property (assign, noatomic) NSUInteger selectedSnack; //记录被选中的行
其实很简单,在获取cell的时候
if (self.selectedSnack == indexPath.row) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
然后在didSelectRowAtIndexPath委托中,记得去掉之前选中的状态
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row != self.selectedSnack) {
if (self.selectedSnack != NSNotFound) {
NSIndexPath *oldIndexPath = [NSIndexPath indexPathForRow:self.selectedSnack inSection:0];
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath: oldIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath :indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
self.selectedSnack = indexPath.row;
}
//反选,去掉高亮,因为已经check mark了
[tableView deselectRowAtIndexPath: indexPath animated:YES];
}
第三个视图控制器BIDRowControlsViewController
cell.accessoryView = button;
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = self.characters[indexPath.row];
if (cell.accessoryView == nil) {
UIImage *buttonUpImage = [UIImage imageNamed:@"button_up.png"];
UIImage *buttonDownImage = [UIImage imageNamed:@"button_down.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setBackgroundImage:buttonUpImage
forState:UIControlStateNormal];
[button setBackgroundImage:buttonDownImage
forState:UIControlStateHighlighted];
[button setTitle:@"Tap" forState:UIControlStateNormal];
[button sizeToFit];
[button addTarget: self
action: @selector(tappedButton:)
forControlEvents: UIControlEventTouchUpInside];
cell.accessoryView = button;
}
//设置button tag,以备后用
cell.accessoryView.tag = indexPath.row;
return cell;
}
注意accessoryType和accessoryView
前者通过几个预定义类型,显示预定义视图,
后者可以设置为任何视图。
第四个视图控制器Movable Rows
editing mode
which is done using the setEditing:animated: method on the table view.
Once editing mode is turned on, a number of new delegate methods come into play.
@property (strong, nonatomic) NSMutableArray *words;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.title = @"Move Me";
self.rowImage = [UIImage imageNamed:@"moveMeIcon.png"];
self.words = [@[@"Eeny", @"Meeny", @"Miney", @"Moe", @"Catch", @"A",
@"Tiger", @"By", @"The", @"Toe"] mutableCopy];
self. navigationItem. rightBarButtonItem = self.editButtonItem;
}
return self;
}
self.navigationController
self.navigationItem.rightBarButtonItem
self.editButtonItem
All UIViewControllersub classes have an editButtonItem property that provide a default
bar item button for toggling its editing state. For a UITableViewController subclass like
BIDMoveMeViewController, this button will toggle the editing state of the UITableView using the
setEditing:animated: method。
所有的UIViewController子类,都有个editButtonItem,其用来翻转视图的editing state
UITableViewController 的此editButtonItem采用setEditing:animated: 方法,来翻转其tableView的editing state
注意,edit是指删除和插入新行
新的委托方法
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}//注意,这里的editingStyle是指删除,或者插入新行,
默认为delete,所以只支持移动的话,需要实现此委托,返回None
- (BOOL)tableView:(UITableView *)tableView
shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
移动某行的时候,调用的是此委托方法
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
id object = [self.words objectAtIndex:fromIndexPath.row];
[self.words removeObjectAtIndex:fromIndexPath.row];
[self.words insertObject:object atIndex:toIndexPath.row];
}
Creating the Deletable Rows View
删除更简单
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath //删除时,为删除的行。插入时,为插入的行
{
[self.computers removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
不允许编辑(删除,插入),即只允许移动
UITableViewCellEditingStyleNone: We used this style in the
previous controller to indicate that a row can’t be edited. The option
UITableViewCellEditingStyleNone will never be passed into this method
because it is used to indicate that editing is not allowed for this row.
默认为删除
UITableViewCellEditingStyleDelete: This is the default option. We ignore this
parameter because the default editing style for rows is the delete style, so we
know that every time this method is called, it will be requesting a delete. You can
use this parameter to allow inserts and deletes within a single table.
UITableViewCellEditingStyleInsert: This is generally used when you need
to let the user insert rows at a specific spot in a list. In a list whose order is
maintained by the system, such as an alphabetical list of names, the user will
usually tap a toolbar or navigation bar button to ask the system to create a new
object in a detail view. Once the user is finished specifying the new object, the
system will place it in the appropriate row.
第6个控制器,可编辑detail
Sixth Subcontroller: An Editable Detail Pane
Creating the Data Model Object
@interface BIDPresident : NSObject <NSCoding, NSCopying>
@property (assign, nonatomic) NSInteger number;
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *fromYear;
@property (copy, nonatomic) NSString *toYear;
@property (copy, nonatomic) NSString *party;
@end
NSCoding协议
encodeWithCoder:
initWithCoder:
NSCopying协议
copyWithZone:
BIDPresidentsViewController subClass BIDSecondLevelViewController
BIDPresidentDetailViewController subClass UITableViewController 细节展示控制器
#import <UIKit/UIKit.h>
@class BIDPresident;
@protocol BIDPresidentDetailViewControllerDelegate;
@interface BIDPresidentDetailViewController : UITableViewController <UITextFieldDelegate>
@property (copy, nonatomic) BIDPresident *president; //data
@property (weak, nonatomic) id<BIDPresidentDetailViewControllerDelegate> delegate;//委托
@property (assign, nonatomic) NSInteger row; //选择的行
@property (strong, nonatomic) NSArray *fieldLabels;
- (IBAction)cancel:(id)sender;
- (IBAction)save:(id)sender;
- (IBAction)textFieldDone:(id)sender;
@end
//此协议,用来告诉其委托,数据已经修改,需要更新。
@protocol BIDPresidentDetailViewControllerDelegate <NSObject>
- (void)presidentDetailViewController:(BIDPresidentDetailViewController *)controller
didUpdatePresident:(BIDPresident *)president;
@end
NSString为何要用copy?而不是strong?
strong和retain同义, weak和assign同义, 为什么要采用这种说法, 似乎是ARC出现后为了消除引用计数的观念而采用的做法.
至于为什么要用copy, 由于纯NSString是只读的, 所以strong和copy的结果一样,据stackOverflow上的说法,是为了防止mutable string被无意中修改,
NSMutableString是NSString的子类, 因此NSString指针可以持有NSMutableString对象.
#define kNumberOfEditableRows 4
#define kNameRowIndex 0
#define kFromYearRowIndex 1
#define kToYearRowIndex 2
#define kPartyIndex 3
#define kLabelTag 2048
#define kTextFieldTag 4094
@implementation BIDPresidentDetailViewController {
NSString *initialText; //编辑前的初始值
BOOL hasChanges; //是否有修改
}
//表视图初始化
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle: UITableViewStyleGrouped]; //强制为分组模式
if (self) {
// Custom initialization
self.fieldLabels = @[@"Name:", @"From:", @"To:", @"Party:"];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem: UIBarButtonSystemItemCancel 系统预定义按钮
target:self
action:@selector(cancel:)];
self.navigationItem.rightBarButtonItem =[[UIBarButtonItem alloc]
initWithBarButtonSystemItem: UIBarButtonSystemItemSave 系统预定义按钮
target:self
action:@selector(save:)];
}
return self;
}
//
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.allowsSelection = NO; //不允许选中
}
//取消
- (void)cancel:(id)sender
{
[self.navigationController popViewController Animated: YES]; //取消,则弹出
}
//保存
- (void)save:(id)sender
{
[self.view endEditing:YES];
if (hasChanges) {
[self.delegate presidentDetailViewController:self
didUpdatePresident:self.president]; //调用委托方法,让委托自己去更新数据
}
[self.navigationController popViewControllerAnimated:YES];
}
//完成输入,收缩键盘
//此方法为textField控件关联controller的Action,响应Did End On Exit事件
//注意其不是一个委托方法,仅仅是文本输入的事件响应
- (void)textFieldDone:(id)sender
{
[sender resignFirstResponder];
}
//
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return kNumberOfEditableRows;
}
//获取表视图单元
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 75, 25)];
label.tag = kLabelTag; //假如后面还需要获取该控件,则可以设置其tag
label.textAlignment = NSTextAlignmentRight;
label.font = [UIFont boldSystemFontOfSize:14];
[cell.contentView addSubview:label]; //直接在cell中添加子视图
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(90, 12, 200, 25)];
textField.tag = kTextFieldTag;
textField.clearsOnBeginEditing = NO;
textField.delegate = self; //设置委托
textField.returnKeyType = UIReturnKeyDone;
[textField addTarget:self
action:@selector(textFieldDone:)
forControlEvents:UIControlEventEditingDidEndOnExit]; //输入完成,响应
[cell.contentView addSubview:textField];
}
UILabel *label = (id)[cell viewWithTag: kLabelTag];//通过Tag,再次获取label
label.text = self.fieldLabels[indexPath.row];
UITextField *textField = (id)[cell viewWithTag:kTextFieldTag]; //使用(id)做类型转换
textField.superview.tag = indexPath.row; //设置tag,后面会用到
switch (indexPath.row) {
case kNameRowIndex:
textField.text = self.president.name;
break;
case kFromYearRowIndex:
textField.text = self.president.fromYear;
break;
case kToYearRowIndex:
textField.text = self.president.toYear;
break;
case kPartyIndex:
textField.text = self.president.party;
break;
default:
break;
}
return cell;
}
//textField委托方法,开始编辑
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
initialText = textField.text;
}
//textField委托方法,完成编辑
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (![textField.text isEqualToString:initialText]) {
hasChanges = YES;
switch (textField.superview.tag) {
case kNameRowIndex:
self.president.name = textField.text;
break;
case kFromYearRowIndex:
self.president.fromYear = textField.text;
break;
case kToYearRowIndex:
self.president.toYear = textField.text;
break;
case kPartyIndex:
self.president.party = textField.text;
break;
default:
break;
}
}
}
@end
实现BIDPresidentsViewController
@interface BIDPresidentsViewController : BIDSecondLevelViewController <BIDPresidentDetailViewControllerDelegate>
@property (strong, nonatomic) NSMutableArray *presidents;
@end
@implementation BIDPresidentsViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.title = @"Detail Edit";
self.rowImage = [UIImage imageNamed:@"detailEditIcon.png"];
NSString *path = [[NSBundle mainBundle] pathForResource:@"Presidents"ofType:@"plist"];
NSData *data = [[NSData alloc] initWithContentsOfFile: path];
//archiver从文件获取对象
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData: data];
self.presidents = [unarchiver decodeObjectForKey:@"Presidents"];
[unarchiver finishDecoding];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.tableView registerClass:[UITableViewCell class]forCellReuseIdentifier:CellIdentifier];
}
#pragma mark - Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.presidents count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
BIDPresident *president = self.presidents[indexPath.row];
cell.textLabel.text = president.name;
return cell;
}
#pragma mark - Table View Delegate Methods
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BIDPresident *president = self.presidents[indexPath.row];
BIDPresidentDetailViewController *controller = [[BIDPresidentDetailViewController alloc] init];
controller.president = president;
//注意BIDPresidentDetailViewController 的president属性为copy,
//意味着此赋值将创建一个独立的president,跟当前数组中的president不是同一份内存
//为什么必须为copy,因为在detailController中,textFiled编辑后,就会改写其维护的president对象,
//最终点取消则不会保存。
controller.delegate = self;
controller.row = indexPath.row;
[self.navigationController pushViewController:controller animated:YES];
}
//委托方法,需要更新president数据
#pragma mark - President Detail View Delegate Methods
- (void)presidentDetailViewController:(BIDPresidentDetailViewController *)controller
didUpdatePresident:(BIDPresident *)president
{
[self.presidents replaceObjectAtIndex: controller.row withObject:president];
[self.tableView reloadData];
}
@end
The keyboard should feature a Returnbutton instead of a Donebutton. When tapped, that button should take the user to the next row’s text field。
实现点击键盘return按钮,跳转到下一行。
- (void)textFieldDone: (id)sender
{
//[sender resignFirstResponder];
UITextField *senderField = sender;
NSInteger nextRow = (senderField.superview.tag + 1) % kNumberOfEditableRows;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:nextRow inSection:0];
UITableViewCell *nextCell = [self.tableView cellForRowAtIndexPath:indexPath];
UITextField *nextField = (id)[nextCell viewWithTag:kTextFieldTag];
[nextField becomeFirstResponder];
}