iOSDay27之界面通信
1. 属性传值(前面的界面给后面传值)
第一步: 在 SecondViewController.h 文件里定义一个存放传过来值的变量 contents
1 #import <UIKit/UIKit.h> 2 3 @interface SecondViewController : UIViewController 4 5 // 第一步:声明属性,用于存放传过来的值 6 @property (nonatomic, copy) NSString *contents; 7 8 @end
第二步: 在 FirstViewController 的按钮点击方法里给 SecondViewController 的 contents 属性赋值
1 - (void)loginButtonClick:(UIButton *)sender 2 { 3 // 创建对象 4 SecondViewController *secondVC = [[SecondViewController alloc] init]; 5 6 // 第二步:进行赋值 7 secondVC.contents = self.rootView.userTextField.text; 8 9 // 导航控制器push栈中 10 [self.navigationController pushViewController:secondVC animated:YES]; 11 }
第三步: 在 SecondViewController 将传过来的值显示, 使用 contents 属性给 nameLabel 赋值(在 viewDidLoad 中)
// 第三步:显示内容 self.nameLabel.text = self.contents;
2. 协议传值(后面的界面给前面传值)
第一步: 声明协议(在委托方(SecondViewController)的 .h 文件中声明)
1 #import <UIKit/UIKit.h> 2 3 #warning 第一步:在SecondViewController.h里声明协议 4 @protocol SecondViewControllerDelegate<NSObject> 5 6 // 声明协议方法 7 - (void)passValueWithString:(NSString *)textString; 8 9 @end 10 11 @interface SecondViewController : UIViewController 12 13 @end
第二步: 声明代理人(在委托方(SecondViewController)的 .h 文件中声明代理人, 代理人是一个属性)
1 @interface SecondViewController : UIViewController 2 3 #warning 第二步:声明代理人 4 5 // 代理的语义设置使用assign, 防止循环引用 6 @property (nonatomic, assign) id <SecondViewControllerDelegate> secondDelegate; 7 8 @end
声明属性时的语义必须是 assign , 使用 retain , copy 会导致循环引用问题
第三步: 执行协议方法(委托方(SecondViewController)在点击事件中使用代理属性调用代理的传值方法)
1 // 实现点击事件方法(实现代理从后往前传值) 2 - (void)submitButtonClick:(UIButton *)sender 3 { 4 // 1.把输入的内容传到第一页 5 6 #warning 第三步:使用代理调用代理的传值方法 7 // [self.secondDelegate passValueWithString:self.eatTextField.text]; 8 9 // 判断如果代理实现了方法进行调用(安全判断,当没有方法实现时不会导致程序崩溃) 10 if ([self.secondDelegate respondsToSelector:@selector(passValueWithString:)]) { 11 [self.secondDelegate passValueWithString:self.eatTextField.text]; 12 } 13 14 // 2.跳回到第一页 15 [self.navigationController popViewControllerAnimated:YES]; 16 }
第四步: 签订协议(代理人(FirstViewController)遵守协议)
1 #warning 第四步:遵守代理协议 2 @interface FirstViewController ()<SecondViewControllerDelegate> 3 4 @property (nonatomic, strong) UILabel *myLabel; 5 6 @end
第五步: 指定代理人(在代理人(FirstViewController) 跳转到委托方的点击事件方法中指定)
1 // 代理人的点击事件方法(跳转到委托方) 2 - (void)rightBarClick:(UIBarButtonItem *)sender 3 { 4 // 1.创建视图控制器对象 5 SecondViewController *secondVC = [[SecondViewController alloc] init]; 6 7 #warning 第五步:设置代理人 8 // 指定当前对象为代理人 9 secondVC.secondDelegate = self; 10 11 // 2.push跳转 12 [self.navigationController pushViewController:secondVC animated:YES]; 13 }
第六步: 实现协议方法(代理人(FirstViewController)实现代理方法, 并接收传过来的值显示)
1 #warning 第六步:实现代理方法,并接收传过来的值 2 - (void)passValueWithString:(NSString *)textString 3 { 4 // 将传过来的值显示 5 self.myLabel.text = textString; 6 }
3. Block传值
1> 回顾Block
2> Block注意事项
在 controller 中定义 Block , 实现 Block 的时候, 如果 Block 里面需要使用当前控制器对象,不可以直接使用 self , 否则会引起循环引用
解决办法:
① 使用 __block 修饰变量防止循环引用(MRC)
② 使用 __weak 修饰变量防止循环引用(ARC)
1 // 使用__weak修饰防止循环引用 2 // __weak SecondViewController *secodVC = self; // 一般写法 3 4 // typeof(...) 得到括号中的类型 5 __weak typeof(self) secodVC = self; // 装逼写法 6 7 self.block = ^void () { 8 NSLog(@"这事block的实现"); 9 10 secodVC.view.backgroundColor = [UIColor grayColor]; 11 };
typeof(...) 得到括号中的类型
3> Block传值的两种方式
方式一: 使用Block属性实现回调传值
方式二: 在方法中定义Block实现回调传值
4> Block属性传值
第一步: 在传值方(SecondViewController)定义 Block 属性
1 #import <UIKit/UIKit.h> 2 3 @interface SecondViewController : UIViewController 4 5 #warning 第一步:定义Block属性 6 @property (nonatomic, copy) void(^secondBlock)(NSString *); 7 8 @end
第二步: 在接值方(FirstViewController) 跳转到委托方的点击事件实现方法中 实现Block, 即Block回调
1 // 跳转点击事件实现方法 2 - (void)barClick:(UIBarButtonItem *)sender 3 { 4 SecondViewController *secondVC = [[SecondViewController alloc] init]; 5 6 #warning 第二步 实现Block 7 // Block回调 8 secondVC.secondBlock = ^void (NSString *string) { 9 // 得到Block回传的string,并赋值给myLabel 10 self.myLabel.text = string; 11 }; 12 13 [self.navigationController pushViewController:secondVC animated:YES]; 14 }
第三步: 在传值方(SecondViewController)的返回点击事件中, 调用Block实现传值
1 // 实现方法 进行Block传值 2 - (void)submitButtonClick:(UIButton *)sender 3 { 4 // 1.Block传值 5 6 #warning 第三步 Block传值, 调用Block 7 self.secondBlock(self.textField.text); 8 9 // 2.返回第一页 10 [self.navigationController popViewControllerAnimated:YES]; 11 }
5> 在方法中定义Block传值
6> Block存储域
① 没有使用局部变量的 Block 内存存储在全局区
② 使用局部变量的 Block 内存存储在 栈区
③ 当 Block 变量定义为属性的时候, 必须使用 copy 修饰, retain无效, 即 retain 和 assign 会造成野指针问题
当对 Block 进行 copy 操作的时候, 此时 Block 的内存区域为 堆区
当不使用 Block 时需要使用 Block_Release() 进行销毁