iOS基础-UIKit框架-多控制器管理-实例:私人通讯录

一、搭建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

原文地址:https://www.cnblogs.com/marshall-yin/p/4736817.html