一、搭建UI界面(一共4个界面,5个控制器),新建4个控制器文件
二、各种功能的实现
1.功能实现1:当账号密码被输入时,登录按钮激活
1>连线监听2个文本框和登录按钮(Outlet)
注意:addTarget:方法一般是监听点击事件,文字的改变是无法监听的。
监听有三种方法,addTarget:方法,代理,通知。因为前2个在这里行不通,所以下面我们用通知来做。
细节:文本框的Clear Button属性设置为Appears while editing,方便用户删除。
2>通过通知来监听文本框文字的改变
-(void)viewDidLoad
{
[super viewDidLoad];
//监听通知
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(textChange) name:UITextFieldTextDidChangeNotification
object:self.accountField];
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(textChange) name:UITextFieldTextDidChangeNotification
object:self.pwdField];
}
//移除通知监听
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
//文本框的文字发生改变的时候调用
-(void)textChange
{
//控制按钮的状态
self.loginBtn.enabled = (self.accountField.text.length && self.pwdField.text.length);
}
2.功能实现2:当取消记住密码时,自动登录也自动取消;当开启自动登录时,记住密码自动开启。
1>连线监听2个开关(2个Outlet,2个Action)
2>实现方法
/**
* 监听记住密码开关的值改变
*/
- (IBAction)rmbPwdChange
{
if (self.rebPwdSwitch.isOn == NO){
[self.autoLoginSwitch setOn:NO animated:YES];
}
}
/**
* 监听自动登录开关的值改变
*/
- (IBAction)autoLoginChange
{
if (self.autoLoginSwitch.isOn){
[self.rebPwdSwitch setOn:NO animated:YES];
}
}
3.功能实现:注销的时候提醒用户:"确定要注销?"
1>连线监听注销按钮(Action)并实现方法
-(IBAction)logout:{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"确定要注销?" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self.navigationController popViewControllerAnimated:YES];
}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];
}
4.功能实现:点击登录按钮的时候先判断账号密码是否正确
1>连线监听登录按钮(Action)并实现方法
-(IBAction)login
{
if(![self.accountField.text isEqualToString:@"mj"]) {
// 账号不存在
[MBProgressHUD showError:@"账号不存在"];
return;
}
if(![self.pwdField.text isEqualToString:@"123"]) {
// 账号不存在
[MBProgressHUD showError:@"密码错误"];
return;
}
//显示一个蒙版(遮盖)
[MBProgressHUD showMessage:@"正在登录中……"];
//模拟(2秒后执行跳转)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 移除遮盖
//跳转 -- 执行login2contacts遮盖segue
[self performSegueWithIdentifier:@"login2contacts" sender:nil];
});
}
5.功能实现:登录成功后,联系人列表显示为"XXX的联系人列表"(数据顺传)
1>执行登录界面到联系人列表界面的segue时拿到联系人列表控制器。
//(无论自动还是手动)执行segue后,跳转之前会自动调用这个方法(此方法属于来源控制器) //一般在这里给下一个控制器传递数据
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
//1.取得目标控制器(联系人列表控制器)
UIViewController *contactVc = segue.destinationViewController;
//2.设置标题(contactVc.title == contactVc.navigationItem.title)
contactVc.title = [NSString stringWithFormat:@"%@的联系人列表",self.accountField.text];
}
6.功能实现:只有在姓名和电话都输入的情况下添加按钮才能点击
1>连线监听添加按钮(outlet)和2个文本框(outlet)
2>通过通知来监听文本框文字的改变
-(void)viewDidLoad { [super viewDidLoad]; //监听通知 [NSNotificationCenter defaultCenter] addObserver:self selector: @selector(textChange) name:UITextFieldTextDidChangeNotification object:self.nameField]; [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange)name:UITextFieldTextDidChangeNotification object:self.phoneField]; } //控制器view完全显示的时候调用 -(void)viewDidAppear:(BOOL)animated { //当点击+号时,默认弹出键盘供用户输入(让姓名文本框成为第一响应者) [self.nameField bocomeFirstResponder]; } -(void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } //文本框的文字发生改变的时候调用 -(void)textChange { //控制按钮的状态 self.addBtn.enabled = (self.nameField.text.length && self.phoneField.text.length); }
7.功能实现:点击添加按钮后回到上一个界面,并将姓名和电话传递到联系人列表界面(数据逆传)
思路:用代理(当一个对象发生一些事情可以告诉代理),在这里就是添加联系人控制器添加成功后要告诉联系人列表控制器,列表得知其添加成功后,将数据拿过
来显示。完毕。(列表是代理,代理永远是接收数据的)
1>连线监听添加按钮(Action)
2>在“添加控制器”中新建一个代理协议并遵守
@class MJAddViewController; @protocol MJAddViewControllerDelegate<NSObject> @optional -(void)addViewControllerDidAddContact:(MJAddViewController *)addVc didAddContact:(MJContect *)contact; @end @property (nonatomic,weak) id<MJAddViewControllerDelegate>delegate; //id类型,所以谁都可以当代理,只要你想接收我的数据,你就当我的代理,这样就没有耦合性了。
3>在添加控制器中实现添加按钮的方法
-(IBAction)add { //1.关闭当前控制器 [self.navigationController popViewControllerAnimated:YES]; //2.通知代理(如果实现了代理方法,就给代理发送消息,通知代理,已经添加) if([self.delegate respondsToSelector:@selector(addViewController:didAddContact:)] ){ MJContact *contact = [[MJContact alloc] init]; contact.name = self.nameField.text; contact.phone = self.phoneField.text; [self.delegate addViewController:self didAddContact:self]; }
4>在联系人列表控制器中遵守代理协议,并重写prepareForSegue:方法(将添加的代理设为列表)
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { MJAddViewController *addVc = segue.destinationViewController; addVc.delegate =self; }
5>实现数据源方法和代理方法
#pragma mark 数据源方法
第二个数据源方法:return self.contacts.count;
第三个数据源方法:
//1.创建cell static NSString *ID = @"contact"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID] ; } //2.设置cell的数据 MJContact *cantact = self.contacts[IndexPath.row]; cell.textLabel.text = contact.name; cell.detailTextLabel.text = contact.phone; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; return cell; }
#pragma mark - 代理方法 -(void)addViewController:(MJaddViewController *)addVc didAddContact:(MJContect *)contact { //1.添加模型数据 [self.contacts addObject:contact]; //2.刷新表格 [self.tableView reloadData]; }
过程:
1.点击+号-->系统自动调用prepareForSegue:方法(在这里设置目标控制器和代理)
2.点击添加按钮-->来到add方法,关闭添加联系人界面,调用代理方法通知代理已经添加了联系人,并将数据传给代理
3.来到代理方法,将传进来的数据添加到数组中并刷新表格。
4.刷新表格后会重新调用数据源方法,取出数组中的模型,并将模型的属性赋值给cell的子控件。
注意:要在C的viewDidLoad方法中取得数据,来赋值给界面上的UI控件
8.功能实现:点击某一行cell来到查看/编辑联系人界面
1>点击Cell,绑定重用标识contact
2>按住ctrl将cell连到查看联系人控制器,选择push
3>注意刚刚连接的线"目标控制器"是"添加联系人控制器",而这一条线"目标控制器"是"查看/编辑联系人控制器",所以需要在prepareForSegue:方法里进行判断
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { id vc = segue.destinationViewController; if([vc isKindOfClass:[MJAddViewController class] ]){ //添加联系人 MJAddViewController *addVc = vc; addVc.delegate =self; } else if ([vc isKindOfClass:[MJEditViewController class]]){ //查看/编辑 //拿到目标控制器 MJEditViewController *editVc = vc; //给编辑传递数据(需要在编辑里添加一个模型属性) //取得选中的那行 NSIndexPath *path [self.tableView indexPathForSelectedRow]; editVc.contact = self.contacts[path.row]; } }
4>查看联系人界面中保存按钮勾上Enabled和Hidden。姓名和电话去掉Enabled 。cell样式改为Right Detail
9.功能实现:将联系人列表中的数据显示在查看联系人界面
1>连线监听查看联系人界面的2个文本框(outlet)
2>在查看联系人控制器的viewDidLoad中设置数据
self.nameField.text = self.contact.name;
self.phoneField.text = self.contact.phone;
10.功能实现:点击查看联系人界面的编辑按钮进入编辑状态,并且弹出键盘
1>连线监听查看联系人界面的保存按钮(Outlet)和编辑按钮(Action)并实现方法
-(IBAction)edit:(UIBarButtonItem *)item { if(self.nameField.enabled){ //点击的是"取消" self.nameField.enabled = NO; self.phoneField.enabled = NO; [self.view endEditing:YES]; self.saveBtn.hidden = YES; item.title = @"编辑"; //还原回原来的数据 self.nameField.text = self.contact.name; self.phoneField.text = self.contact.phone; } else { //点击的是"编辑" self.nameField.enabled = YES; self.phoneField.enabled = YES; [self.phoneField bocomeFirstResponder]; self.saveBtn.hidden = NO; item.title = @"取消"; } }
11.功能实现:只有查看联系人界面中的电话和姓名同时有值时保存键才激活
1>在编辑联系人控制器的viewDidLoad中监听通知
-(void)viewDidLoad { [super viewDidLoad] [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange)name:UITextFieldTextDidChangeNotification object:self.accountField]; [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange)name:UITextFieldTextDidChangeNotification object:self.pwdField]; } -(void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } -(void)textChange { self.addBtn.enabled = (self.nameField.text.length && self.phoneField.text.length); }
12.功能实现:点击查看联系人界面的保存后返回到联系人列表并更新数据
1>在目标控制器创建一个代理协议并遵守
2>连线监听保存按钮(Action)并实现方法
-(IBAction)save { //1.关闭当前控制器View //2.通知代理已经保存 if([self.delegate respondsToSelector:@selector(editViewController:didSaveContact:)] ){ //更新模型数据 self.contact.name = self.nameField.text; self.contact.phone = self.phoneField.text; [self.delegate editViewController:self didSaveContact:self.contact]; }
3>在代理中遵守协议,并在prepareForSegue:中将列表设为代理
4>实现代理方法(在代理方法中调用reloadData即可)
13.功能实现:当有数据时,cell才有分隔线。没有数据就没有分隔线。
1>在联系人列表控制器的viewDidLoad中将所有线隐藏
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
2>手动添加分隔线(高度为1的UIView)
1# 新建一个类用来封装cell,将storyboard里的cell的Class设置为这个自定义cell。并将数据源方法中cell 的创建改为创建这个自定义cell的类型。(而非系统自带), 而且不需要再设置cell右边的箭头了。
2#在cell控制器中添加模型数据属性并提供一个静态方法快速创建cell(在这个方法中根据标识返回cell)
3#将模型数据封装起来
//1.创建cell UIContactCell *cell = [MJContactCell cellWithTableView:tableView]; //2.设置cell的数据 cell.contact = self.contacts[IndexPath.row]; return cell; 4#重写set方法,在其中设置数据 cell.textLabel.text = contact.name; cell.detailTextLabel.text = contact.phone;
PS:如果cell是通过storyboard或xib创建的,就不可能会调用initWithStyle来初始化cell
5#在awakeFromNib里添加分隔线
//如果cell是通过storyboard或xib创建的,就会调用下述方法 -(void)awakeFromNib { UIView *divider = [UIView alloc] init]; divider.backgroundColor = [UIColor blackColor]; divider.alpha = 0.2; [self.contactView addSubview:divider]; }
6#在ContactCell中添加一条分隔线属性,并在awakeFromNib中给属性赋值
7#在layoutSubviews方法中设置frame
//在这里拿到的cell高度是准确的 -(void)layoutSubviews { [super layoutSubviews]; CGFloat dividerX = 0; CGFloat dividerH = 1; CGFloat dividerY = self.frame.size.height - dividerH; CGFloat dividerW = self.frame.size.width; self.divider.frame = CGRectMake(dividerX ,dividerY ,dividerH ,dividerW ); }
14.功能实现:cell滑动删除
#pragma mark - tableView的代理方法 //如果实现了这个方法,就自动实现了滑动删除的功能 //提交了一个编辑操作就会调用(操作:删除/添加) -(void)tableView:(UITableView)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if(editingStyle == UITableViewCellEditingStyleDelete){ //提交删除操作 //1.删除模型数据 [self.contacts removeObjectAtIndex:indexPath.row]; //2.刷新表格 [self.tableView deleteRowsAtIndexPaths:@:[indexPath] withRowAnimation:UITableViewRowAnimationTop]; //3.归档 [NSKeyedArchiver archiveRootObject:self.contacts toFile:MJContactsFilepath]; } }
知识补充:tableView的刷新
1.数据刷新的总体步骤
1>修改模型数据
2>刷新表格(刷新界面)
2.刷新表格的三种方法
1>reloadData:整体刷新(每一行都会刷新)
2>reloadRowsAtIndexPaths: 局部刷新(前提:刷新前后模型数据的个数不变)
3>deleteRowsAtIndexPaths:局部删除(前提:模型数据减少个数=indexPaths的长度)
15.功能实现:批量删除
1>在viewDidLoad中拿到+号按钮,将其扔进数组,在数组中再添加一个垃圾桶
-(void)viewDidLoad { [super viewDidLoad]; UIBarButtonItem *addItem = self.navigationItem.rightBarButtonItem; UIBarButtonItem *deleteItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteClick)]; self.navigationItem.rightBarButtonItems = @[deleteItem,addItem]; } -(void)deleteClick { //让tableView进入编辑状态 [self.tableView setEditing:!self.tableView.isEditing animated:YES]; }
知识补充:UITableViewController