在模态视图控制器被关闭时调用底层视图控制器中的函数

Posted

技术标签:

【中文标题】在模态视图控制器被关闭时调用底层视图控制器中的函数【英文标题】:Call Function in Underlying ViewController as Modal View Controller is Dismissed 【发布时间】:2010-11-11 01:24:24 【问题描述】:

我有一个 mainViewController。我调用 [self pushModalViewController:someViewController] 使 someViewController 成为活动视图。

现在我想在 mainViewController 中调用一个函数,因为 someViewController 会随着 [self dismissModalViewController] 消失。

viewDidAppear 没有被调用可能是因为视图已经存在,就在模态视图的下方。一旦 modalView 自行关闭,如何调用 mainViewController 中的函数?

非常感谢!

【问题讨论】:

【参考方案1】:

这个答案被重写/扩展以解释 3 种最重要的方法 (@galambalazs)

1。块

最简单的方法是使用回调block。如果您只有一个侦听器(父视图控制器)对解雇感兴趣,这很好。您甚至可以通过事件传递一些数据。

MainViewController.m

SecondViewController* svc = [[SecondViewController alloc] init];
svc.didDismiss = ^(NSString *data) 
    // this method gets called in MainVC when your SecondVC is dismissed
    NSLog(@"Dismissed SecondViewController");
;
[self presentViewController:svc animated:YES completion:nil];

SecondViewController.h

@interface MainViewController : UIViewController
    @property (nonatomic, copy) void (^didDismiss)(NSString *data);
    // ... other properties
@end

SecondViewController.m

- (IBAction)close:(id)sender 

    [self dismissViewControllerAnimated:YES completion:nil];

    if (self.didDismiss) 
        self.didDismiss(@"some extra data");


2。委派

Delegation 是 Apple 推荐的模式:

关闭呈现的视图控制器

如果呈现的视图控制器必须向呈现的视图控制器返回数据,请使用delegation 设计模式来促进传输。委托使在应用程序的不同部分重用视图控制器变得更加容易。通过委托,呈现的视图控制器存储对委托对象的引用,该对象实现了来自正式protocol 的方法。当它收集结果时,呈现的视图控制器在其委托上调用这些方法。在典型的实现中,呈现视图控制器使自己成为其呈现视图控制器的委托。

MainViewController

MainViewController.h

@interface MainViewController : UIViewController <SecondViewControllerDelegate>
    - (void)didDismissViewController:(UIViewController*)vc;
    // ... properties
@end

MainViewController.m 中的某处(呈现)

SecondViewController* svc = [[SecondViewController alloc] init];
svc.delegate = self;
[self presentViewController:svc animated:YES completion:nil];

MainViewController.m 中的其他地方(被告知解雇)

- (void)didDismissViewController:(UIViewController*)vc

    // this method gets called in MainVC when your SecondVC is dismissed
    NSLog(@"Dismissed SecondViewController");

SecondViewController

SecondViewController.h

@protocol SecondViewControllerDelegate <NSObject>
- (void)didDismissViewController:(UIViewController*)vc;
@end

@interface SecondViewController : UIViewController
@property (nonatomic, weak) id<SecondViewControllerDelegate> delegate;
// ... other properties
@end

SecondViewController.m

中的某处
[self.delegate myActionFromViewController:self];
[self dismissViewControllerAnimated:YES completion:nil];

(注意:带有 didDismissViewController: 方法的协议可以在整个应用程序中重复使用)


3。通知

另一种解决方案是发送NSNotification。这也是一种有效的方法,如果您只想通知解雇而不传递太多数据,它可能比委托更容易。但它的主要用例是当您想要 多个侦听器 用于解除事件(不仅仅是父视图控制器)时。

但请确保在完成后始终将自己从 NSNotificationCentre 中移除!否则,即使在您被解除分配后,您也可能会因被要求通知而崩溃。 [编者注]

MainViewController.m

- (IBAction)showSecondViewController:(id)sender 

    SecondViewController *secondVC = [[SecondViewController alloc] init];
    [self presentViewController:secondVC animated:YES completion:nil];

    // Set self to listen for the message "SecondViewControllerDismissed"
    // and run a method when this message is detected
    [[NSNotificationCenter defaultCenter] 
     addObserver:self
     selector:@selector(didDismissSecondViewController)
     name:@"SecondViewControllerDismissed"
     object:nil];


- (void)dealloc

    // simply unsubscribe from *all* notifications upon being deallocated
    [[NSNotificationCenter defaultCenter] removeObserver:self];
 

- (void)didDismissSecondViewController 

    // this method gets called in MainVC when your SecondVC is dismissed
    NSLog(@"Dismissed SecondViewController");

SecondViewController.m

- (IBAction)close:(id)sender 

    [self dismissViewControllerAnimated:YES completion:nil];

    // This sends a message through the NSNotificationCenter 
    // to any listeners for "SecondViewControllerDismissed"
    [[NSNotificationCenter defaultCenter] 
     postNotificationName:@"SecondViewControllerDismissed" 
     object:nil userInfo:nil];

希望这会有所帮助!

【讨论】:

太棒了!我现在就试试:) 当我改变一切以使用主委托时:[someViewController setMainDelegate:self]; [自我 pushModalViewController:someViewController]; [mainDelegate someActionFromViewController:self]; [自我dismissModalViewController]; ......它会构建但崩溃你确定这个语法有效吗?我需要声明委托吗? 抱歉,是的,您必须在 @interface 部分声明委托。我还建议创建一个协议,以确保您调用一个可以处理您发送的消息的对象。所以 id 委托; 我使用这个链接来创建协议和声明。 ***.com/questions/645449/… @protocol PropertiesControllerDelegate -- id 委托; -- 但我仍然在 setDelegate 方法处崩溃.....-[PropertiesController setDelegate:]: unrecognized selector sent to instance 0x177840 2010-11-10 19:27:29.463 iScoutShoes[4142:307] ***由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[PropertiesController setDelegate:]:无法识别的选择器发送到实例 0x177840”【参考方案2】:

使用退出(展开)转场

当您使用故事板和转场时,您可以使用一种非常方便且代码最少的方法来关闭模态视图控制器并通知底层视图控制器模态视图控制器已关闭。

使用退出(放松)可以获得 3 个优势:

    您无需编写任何代码即可关闭模态视图控制器并 您可以让 ios 调用 底层视图控制器内的回调方法,该方法已呈现模型视图控制器。 您使用的语义与您在实现prepareForSegue: 时已经知道的语义完全相同

只需 2 步即可实现

    Parent 视图控制器中创建一个操作方法,以呈现另一个(模态)视图控制器:

斯威夫特

@IBAction func unwindFromSegue(segue: UIStoryboardSegue) 
    print("Unwind from segue", segue.identifier)

Objective-C

- (IBAction)unwindFromSegue:(UIStoryboardSegue *)segue 
    NSLog(@"Unwind from segue %s", segue.identifier);

    在情节提要中,在 child view controller 上右键单击 exit segue(又名 unwind segue,它是视图控制器顶部的最后一个图标),将unwindFromSegue: 拖放到您的按钮并选择操作

你已经完成了!现在,当您单击 dismiss 按钮时模态视图控制器关闭,unwindFromSegue: 通知您的底层视图控制器(Parent)模态视图控制器(Child)已关闭.

【讨论】:

【参考方案3】:

这是一个回调解决方案,它对您的模态和父级进行较少的修改: 在模型的 .h 中添加:

@property (nonatomic, copy) void (^dismissed)();

在模型的 .m 中,当您关闭模式时,将其放在完成中:

 [self dismissViewControllerAnimated:YES completion:^
    if(self.dismissed)
        self.dismissed();
];

当你实例化你的模式时,在父视图控制器中设置解除的回调:

Modal = //Init your modal
[Modal setDismissed:^
   //do stuff you wanted when it's dimissed
];
 [self presentViewController:Modal animated:YES completion:nil];

【讨论】:

以上是关于在模态视图控制器被关闭时调用底层视图控制器中的函数的主要内容,如果未能解决你的问题,请参考以下文章

当关闭模态视图控制器时,底层 UIScrollView 调整为全屏

当模态视图控制器被解除时如何调用函数

从 uitabbarController 视图中关闭模态视图

使用委托关闭视图不起作用

推送通知进入时调用的方法序列

关闭模态视图控制器时更新前一个视图控制器中的pickerView