第一篇Objective的文章,第一次使用Markdown来写。
页面传值的方法有很多,也很常用到,Delegate/NSNotification/Block/NSUserDefault/单例,等等。
一项一项增加,先写最常用的委托传值,也就是Delegate,老师讲的方法。(TryAttack,这个无聊的时候玩这个。)
委托传值 Delegate
协议方法是一种非常常用的传值方法,只要在协议中声明一个协议方法,然后两个类一个作为委托方一个作为遵守方来调用和实现方法就可以实现传值。十分高效而且针对性很强。
我们建立完Text Field元件和程式码的关联后,完成页面1UIViewController
的 Button
拖拽到 SecndViewController
,並且在Custom Class
的Class中设定第二个ViewController 为 SecndViewController
(即客制化所对应的Class)。这样就产生了 Storyboard Segue
的方法,选者Model
作为连接,Storyboard Segue
就会自动连接到UIViewController
。
如果想要使用不同的换页效果,可以在 Storyboard Segue 中的 Transition 属性做修改。
接下来我们就来实作委托传值的方法,首先来考虑如何往 SecndViewController
进行传值。
- 透过 Storyboard Segue 传值,在页面 2
SecndViewController
的 UIViewController class 里设置一个NSString
的变数,它用来接收由页面 1 透过 Storyboard Segue 所传过来的值,程式码如下。
@property (strong) NSString* strAttackPower;
之后在SecndViewController.h
的viewDidLoad
函式里我们将 string 的值指定给 pAttackResult
,此时页面 2 加载时就会把 pAttackResult
的内容设成 Storyboard Segue 所传送过来的值。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
int iDefensValue = rand()%10;
iScore = [strAttackPower intValue] - iDefensValue;
if ( iScore > 0 ){
pAttackResult.text = [NSString stringWithFormat: @"攻击造成%d伤害,得分%d", iScore, iScore];
}else {
pAttackResult.text = @"攻击被防御";
iScore = 0;
}
}
接着回到页面 1 的 UIViewController class 里,新增一个内建的函式 prepareForSegue
,至此我们完成透过 Storyboard Segue 传值的方法。
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
id pNextPage = segue.destinationViewController;
[pNextPage setValue:[NSString stringWithFormat: @"%d", 1+rand()%10 ] forKey: @"strAttackPower"];
[pNextPage setValue:self forKey:@"delegate"];
}
在ViewController
切换(Segue)之前会呼叫上面的方法,至此我们完成了向下一层的传值。
- 关键点在
setValue: forKey:
下面看一下定义:
setValue: forKey:
中value能够为nil,但是当value为nil的时候,会自动调用removeObject: forKey:
方法,key的参数只能够是NSString
类型,而对应的setObject: forKey:
的可以是任何类型
2.返回先前页面的方法
使用 dismissModalViewControllerAnimated:
方法,或是 dismissViewControllerAnimated:completion:
方法来解散SecndViewController
,并返回先前的页面。
- (IBAction)goBack:(id)sender {
if ([self.delegate respondsToSelector:@selector(incrementScore:from:)]) {
[self.delegate incrementScore:iScore from:self];
}
[self dismissViewControllerAnimated:YES completion: ^{}];
}
3.由上面的两步我们已经建立两个可以互相切换的 UIViewController
,一个是透过 Storyboard Segue 来切换并且向下传值,另一个则是使用 dismissViewControllerAnimated:
的方法来返回先前的 UIViewController。
接下来就是要解决的就是 SecndViewController
向上传值的部分,也就是通过代理委托delegate,建立一个协定 @protocol 的方式。
将此分为两个角色五个步骤。
委托者:
1.声明delegate属性。2.调用协议方法。
被委托者:
1.遵守协议。2.设定为被委托者。3.覆写协议方法。
委托者 SecndViewController
1.声明delegate属性:首先在页面 2 的 UIViewController class,建立一个协定 incrementScoreDelegate
,并且定义其内部的方法 goBack:
,接着宣告一个采用此协定的物件 delegate,其程式码如下。
//建立一个协定
@protocol incrementScoreDelegate;
@interface SecndViewController : UIViewController
@property (strong, nonatomic) IBOutlet UITextField *pAttackResult;
@property (strong) NSString* strAttackPower;
//协定中的方法
@property (weak) id delegate;
- (IBAction)goBack:(id)sender;
@end
//宣告一个採用incrementScoreDelegate 协定的物件
@protocol incrementScoreDelegate
- (void) incrementScore: (int) score from:(SecndViewController* ) page;
@end
2.调用协议方法: 也就是上面第二點2.返回先前页面的方法,要呼叫协定必须写在按钮事件中。当我们按下页面2的按钮的时候,就会呼叫采用incrementScoreDelegate
的Class,因此Class必须实作协定内的方法。
被委托者 UIViewController
1.遵守协议:採用协定的方式是在 @interface 区段的地方加上 <协定名称> 的程式码,由于此协定是写在其他的 class 中,所以在採用协定之前要先引用它,以下是页面1的 UIViewController class.h 档。
//引用持有incrementScoreDelegate协定的class
#import "SecndViewController.h"
//採用协定
@interface ViewController : UIViewController
2.设定为被委托者:在 .m 档中实作协定内的 incrementScore: 方法函式。
- (void) incrementScore:(int)score from:(SecndViewController *)page {
iTotalScore = iTotalScore + score;
pScoreBoardLable.text = [NSString stringWithFormat:@"Score: %d", iTotalScore];
}
3.覆写协议方法: 如果忽略此步骤,页面2里的 delegate 参数在呼叫 goBack
方法时,并不会知道是哪个 class实作了它的方法,因此参数也无法由页面2SecndViewController
传递至页面1UIViewController
。
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//将 pNextPage 设定成Storyboard Segue的目标UIViewController
id pNextPage = segue.destinationViewController;
//将值透过Storyboard Segue带给页面2 `pNextPage` 的string变数
[pNextPage setValue:[NSString stringWithFormat: @"%d", 1+rand()%10 ] forKey: @"strAttackPower"];
//将delegate设成自己(指定自己为代理)
[pNextPage setValue:self forKey:@"delegate"];
}
协议传值的五步,最重要的是思考清楚,谁作为委托者,谁遵守协议,然后用这五个步骤来进行实作。