dismissModalViewController并将数据传回


84

我有两个视图控制器,firstViewControllersecondViewController。我正在使用此代码切换到我的secondViewController(我也向其传递了一个字符串):

secondViewController *second = [[secondViewController alloc] initWithNibName:nil bundle:nil];

second.myString = @"This text is passed from firstViewController!";

second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;

[self presentModalViewController:second animated:YES];

[second release];

然后,我在secondViewController中使用以下代码切换回firstViewController:

[self dismissModalViewControllerAnimated:YES];

所有这些都很好。我的问题是,如何将数据传递给firstViewController?我想将不同的字符串从secondViewController传递到firstViewController中。

Answers:


142

您需要使用委托协议...以下是操作方法:

在您的secondViewController的头文件中声明一个协议。它看起来应该像这样:

#import <UIKit/UIKit.h>

@protocol SecondDelegate <NSObject>
-(void)secondViewControllerDismissed:(NSString *)stringForFirst
@end


@interface SecondViewController : UIViewController
{
    id myDelegate;  
}

@property (nonatomic, assign) id<SecondDelegate>    myDelegate;

不要忘记在实现(SecondViewController.m)文件中合成myDelegate:

@synthesize myDelegate;

在您的FirstViewController的头文件中,通过执行以下操作订阅SecondDelegate协议:

#import "SecondViewController.h"

@interface FirstViewController:UIViewController <SecondDelegate>

现在,当您在FirstViewController中实例化SecondViewController时,应该执行以下操作:

// If you're using a view controller built with Interface Builder.
SecondViewController *second = [[SecondViewController alloc] initWithNibName:"SecondViewController" bundle:[NSBundle mainBundle]];
// If you're using a view controller built programmatically.
SecondViewController *second = [SecondViewController new]; // Convenience initializer that uses alloc] init]
second.myString = @"This text is passed from firstViewController!";
second.myDelegate = self;
second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second animated:YES];
[second release];

最后,在您的第一个视图控制器的实现文件(FirstViewController.m)中,为secondViewControllerDismissed实现SecondDelegate的方法:

- (void)secondViewControllerDismissed:(NSString *)stringForFirst
{
    NSString *thisIsTheDesiredString = stringForFirst; //And there you have it.....
}

现在,当您要关闭第二个视图控制器时,您要调用第一个视图控制器中实现的方法。这部分很简单。您要做的就是在第二个视图控制器中,在关闭代码之前添加一些代码:

if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:)])
{
    [self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!"];
}
[self dismissModalViewControllerAnimated:YES];

委托协议非常有用,非常有用。熟悉它们对您有好处:)

NSNotifications是执行此操作的另一种方法,但是作为最佳实践,当我想在多个viewController或对象之间进行通信时,我更喜欢使用它。如果您对使用NSNotifications感到好奇,这是我早些时候发布的答案:从appdelegate中的线程在多个viewcontroller中触发事件

编辑:

如果要传递多个参数,则关闭前的代码如下所示:

if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:argument2:argument3:)])
{
    [self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!" argument2:someObject argument3:anotherObject];
}
[self dismissModalViewControllerAnimated:YES];

这意味着firstViewController内部的SecondDelegate方法实现现在看起来像:

- (void) secondViewControllerDismissed:(NSString*)stringForFirst argument2:(NSObject*)inObject1 argument3:(NSObject*)inObject2
{
    NSString thisIsTheDesiredString = stringForFirst;
    NSObject desiredObject1 = inObject1;
    //....and so on
}

根据Apple的iOSView Controller编程指南,第二个ViewController应该在当前的视图控制器中关闭,而不在当前的视图控制器中关闭。
迈克尔,

听起来您尚未设置UITableView的委托。您可以将这个问题与所拥有的代码一起发布并回圈吗?我也许能为您提供帮助。
2013年

1
@Michael文档说,在self上调用dismiss会将调用转发给当前的视图控制器。另外,调用self会更干净,因为那样您就不必担心根据要定位的iOS版本(5或更低版本)在presentingViewController和parentViewController之间切换。
2013年

1
@Resty我同意;块非常有用。我当时正在考虑将此答案更改为支持障碍。但是,在这种情况下,我暂时看不到委托人的答案,因为它使我们有更多的自由来操纵可以传递给模式的对象。我只是懒惰,将尽快更新此答案以使用块:)
2014年

1
@sid谢谢兄弟,它对我有用,但是您需要稍作修改。许多事情都改变了。请对其进行编辑
ChenSmile '16

40

我在这里可能不合时宜,但是与非常冗长的委托/协议方法相比,我开始更喜欢块语法。如果您从vc1制作vc2,请在vc2上拥有一个属性,您可以从vc1设置它为一个块!

@property (nonatomic, copy) void (^somethingHappenedInVC2)(NSString *response);

然后,当要告诉vc1的vc2中发生某些事情时,只需执行在vc1中定义的块即可!

self.somethingHappenedInVC2(@"Hello!");

这使您可以将数据从vc2发送回vc1。就像魔术一样。IMO,这比协议要容易/干净得多。区块很棒,需要尽可能多地接受。

编辑-改进示例

假设我们有一个mainVC,我们希望暂时展示一个modalVC,以从用户那里获得一些输入。为了展示来自mainVC的modalVC,我们需要在mainVC内部分配/初始化它。很基本的东西。好了,当我们创建这个modalVC对象时,我们还可以在其上设置一个block属性,使我们能够轻松地在两个vc对象之间进行通信。因此,让我们以上面的示例为例,并将follwing属性放在modalVC的.h文件中:

 @property (nonatomic, copy) void (^somethingHappenedInModalVC)(NSString *response);  

然后,在我们的mainVC中,在分配/初始化一个新的modalVC对象之后,您可以像下面这样设置modalVC的block属性:

ModalVC *modalVC = [[ModalVC alloc] init];
modalVC.somethingHappenedInModalVC = ^(NSString *response) {
     NSLog(@"Something was selected in the modalVC, and this is what it was:%@", response);
}

因此,我们只是设置block属性,并定义执行该块时会发生什么。

最后,在我们的modalVC中,我们可以有一个tableViewController,它由字符串的dataSource数组支持。选择行后,我们可以执行以下操作:

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      NSString *selectedString = self.dataSource[indexPath.row];
      self.somethingHappenedInModalVC(selectedString);
 }

当然,每次在modalVC中选择一行时,我们都将从mainVC的NSLog行获得控制台输出。希望有帮助!


1
使用情节提要板时,这种方法是否仍然有效?现在它对我不起作用。只是由于lldb错误退出。主要差异。我可以看到,vc的分配现在是实例化的情节提要流。编辑我在创建块之前正在演示。固定。
malaki1974 2014年

2
我同意你的意见:)我很早以前就发布了我的答案。现在,我根据使用情况在使用块/协议之间进行切换。鉴于该线程到目前为止仍然很活跃,因此我应该在某个时候编辑我的答案以包含块。
2014年

1
这个答案需要接受,因为这带来了最直观的解决方案。
Sukitha Udugamasooriya

2
在两个适当的答案中,这是最佳答案!
kygcoleman

1
到目前为止,这是我的首选方法。然后,通过关闭快速完成此操作。委托和通知要好得多,因为您无需指定协议或这些“难看的”通知常量。如果您在显示的vc中使该变量的名称很好地表示闭包,则它可能是非常直观的代码,例如。Vc.didCancel,vc.didFinish ...您可以在提供它的vc中的prepareForSegue中进行设置(如果使用的是segues)。
HixField '16

4

嗯,寻找通知中心并在通知中传回信息。这是苹果的做法 -我个人采取这种方法,除非任何人有其他建议


该链接实际上使它复杂化了,您所需要的只是一个观察者(第一个View Controller),然后从第二个发送Notif。您可以将选择器分配给通知,还可以获取通过通知发送回的信息。
iOSDude 2011年

2

在第二个视图控制器中定义一个委托协议,并使第一个成为第二个的委托。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.