copy: NSString/NSMutableArray
weak:代理/UI控件
strong: 其他OC对象
#1. Segue
storyboard上每根界面跳转的线, 都是UIStoryboardSegue对象
每个Segue对象都有三个属性:
- 唯一标识(
@property (nonatomic, readonly) NSString *identifier;),
- 来源控制器(
@property (nonatomic, readonly) id sourceViewController;)
- 目标控制器(
@property (nonatomic, readonly) id destinationViewController;)
##1.1. Segue的类型
- 自动型: 点击
某控件后, 自动执行Segue,自动完成界面跳转(由控制拖出, 用于不需要做任何判断直接跳转界面的情景)
- 手动型: 需要手写代码才能完成界面的跳转(按住control, 由来源
控制器(不是按钮)拖线到目标控制器, 需要设置标识)
##1.2. performSegueWithIdentifier:sender:方法的完整执行过程
1 2 3 4
| self为源控制器 第一个参数为Segue的表示, 第二个参数为要传递的数据*/ [self performSegueWithIdentifier:@"login2contacts" sender:nil];
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| - (IBAction)login { if ([self.accountField.text isEqualToString:@"mj"] && [self.pwdField.text isEqualToString:@"123"]) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self performSegueWithIdentifier:@"login2contact" sender:nil]; }); } else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"警告" message:@"账号或者密码错误" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil]; [alert show]; } } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { UIViewController *contactVc = segue.destinationViewController; contactVc.title = [NSString stringWithFormat:@"%@的联系人列表", self.accountField.text]; }
|
- 根据identifier去storyboard中找到对应的线
- 新建UIStoryboardSegue对象
- 设置Segue对象的sourceViewController(来源控制器)
- 新建并且设置Segue对象的destinationViewController(目标控制器)
- 调用sourceViewController的方法,做一些跳转前的准备工作并且传入创建好的Segue对象
1 2
| - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { id vc = segue.destinationViewController; if([vc isKindOfClass:[AddViewController class]]) { AddViewController *addVc = segue.destinationViewController; addVc.delegate = self; } else if ([vc isKindOfClass:[EditViewController class]]) { EditViewController *editVc = vc; NSIndexPath *path = [self.tableView indexPathForSelectedRow]; editVc.contact = self.contacts[path.row]; editVc.delegate = self; } }
|
- 调用Segue对象的- (void)perform;方法开始执行界面跳转操作
- 取得sourceViewController所在的UINavigationController
- 调用UINavigationController的push方法将destinationViewController压入栈中,完成跳转
##1.3. 数据后传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| - (IBAction)login { if ([self.accountField.text isEqualToString:@"mj"] && [self.pwdField.text isEqualToString:@"123"]) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self performSegueWithIdentifier:@"login2contact" sender:nil]; }); } else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"警告" message:@"账号或者密码错误" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil]; [alert show]; } } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { UIViewController *contactVc = segue.destinationViewController; contactVc.title = [NSString stringWithFormat:@"%@的联系人列表", self.accountField.text]; }
|
##1.4. 数据回传(重要)
使用代理
代理的实现过程
- 在目标控制器的头文件定义代理和代理对应的方法, 并定义代理属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #import <UIKit/UIKit.h> @class ContactViewController, Contact, AddViewController; @protocol AddViewControllerDelegate <NSObject> @optional - (void)addViewController:(AddViewController *)addVc DidAddContact: (Contact *)contact; @end @interface AddViewController : UIViewController @property (nonatomic, strong) ContactViewController *contacts; @property (nonatomic, weak) id<AddViewControllerDelegate> delegate; @end
|
- 当某个事件(
比如点击事件后需要传递数据给代理)引发代理事件时, 通知代理, 如果代理有某个方法, 就调用这个方法通知道理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| - (IBAction)add { [self.navigationController popViewControllerAnimated:YES]; if ([self.delegate respondsToSelector:@selector(addViewController:DidAddContact:)]) { Contact *contact = [[Contact alloc] init]; contact.name = self.nameField.text; contact.phone = self.phoneField.text; [self.delegate addViewController:self DidAddContact:contact]; } }
|
- 代理遵守协议, 代理接到消息后, 执行代理方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @interface ContactViewController () <AddViewControllerDelegate> - (IBAction)logout:(id)sender; @property (nonatomic, strong) NSMutableArray *contacts; @end //add控制器的代理方法 - (void)addViewController:(AddViewController *)addVc DidAddContact:(Contact *)contact { //1. 添加模型数据 [self.contacts addObject:contact]; //2. 刷新tableView表格 [self.tableView reloadData]; }
|
- 联系人列表界面点击加号添加联系人(即将进行控制器的跳转), 调用
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender方法(拿到目标控制器,设置添加控制器的代理为self(也就是源控制器)
- 进行控制器跳转
- add方法(目标控制器触发代理事件)关闭当前控制器,判断代理联系人控制器有没有实现某个方法, 调用联系人列表的代理
- 代理执行这个代理方法
#2. 两种弹窗
##2.1. 底部弹窗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| //遵守协议 @interface ContactViewController () <UIActionSheetDelegate> //设置底部弹窗需要遵守UIActionSheetDelegate协议 - (IBAction)logout:(id)sender { UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"确定要注销吗?" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"确定" otherButtonTitles:nil, nil]; [sheet showInView:self.view];//显示弹窗 } //buttonIndex为被点击的按钮的索引, 由上到下从零编号 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex != 0) { return; } if (buttonIndex == 0) { //点击了确定 [self.navigationController popViewControllerAnimated:YES]; } }
|
##2.2. 直接弹窗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| - (IBAction)login { if ([self.accountField.text isEqualToString:@"mj"] && [self.pwdField.text isEqualToString:@"123"]) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self performSegueWithIdentifier:@"login2contact" sender:nil]; }); } else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"警告" message:@"账号或者密码错误" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil]; [alert show]; } }
|
#3. 代理总结
协议的定义并不实现任何方法(只做声明),当某个带向遵守协议, 这个对象必须实现协议中声明的方法
A是B的代理, B将发送消息(数据)
1 2 3 4
| @protocol 代理协议名称 <NSObject> @optional 声明代理方法 @end
|
1 2 3
| @interface B : UIViewController @property (nonatomic, weak) id<代理协议名称> delegate; @end
|
1 2 3
| if ([self.delegate respondsToSelector:@selector(声明的方法) { [self.delegate 调用的声明的方法];
|
1 2 3 4
| @interface A () <代理协议名称> ... B.delegate = self(A)
|
受IOS之协议和委托启发, 对代理理解更深入
#4. 消息中心监听
消息中心就像一个广播基站,发送一条消息,在所有的添加监听的地方都能够收到此信息,并作出不同或者相同的动作
在前4个页面中都创建一个消息中心用来监听 一个object @"change",我们在第五个页面,通过消息中心,发送一个@"change" 消息,这样前四个页面就可以收到这个消息,然后做出相应的动作
- 一个文本输入框的文字发生改变时,文本输入框会发出一个
UITextFieldTextDidChangeNotification通知
- 因此通过监听通知来监听文本输入框的文字改变
1 2
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextFieldTextDidChangeNotification object:textField];
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| - (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); }
|
iOS设计模式之NSNotificationCenter 消息中心
#5. UISwitch
- UISwitch继承自UIControl,因此也能像UIButton一样监听一些事件,比如状态改变事件
- UISwitch可以通过拖线监听状态改变
- UISwitch可以通过
addTarget:...方法监听状态改变
1 2
| - (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| - (IBAction)rmbPwdChange { if(self.rmbPwd.isOn == NO) { [self.autoLogin setOn:NO animated:YES]; } } - (IBAction)autoLoginChange { if (self.autoLogin.isOn) { [self.rmbPwd setOn:YES animated:YES]; } }
|