这一行:
[self dismissViewControllerAnimated:YES completion:nil];
不是向自己发送消息,它实际上是向其呈现的 VC 发送消息,要求它进行解散。当你呈现一个 VC 时,你在呈现的 VC 和被呈现的 VC 之间创建了一种关系。所以你不应该在呈现的时候破坏正在呈现的 VC(呈现的 VC 不能发回驳回消息……)。由于您没有真正考虑到它,因此您使应用程序处于混乱状态。看我的回答Dismissing a Presented View Controller
其中我推荐这个方法写得更清楚:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
在您的情况下,您需要确保所有控制都在mainVC 中完成。您应该使用委托将正确的消息从 ViewController1 发送回 MainViewController,以便 mainVC 可以关闭 VC1,然后呈现 VC2。
在 VC2 VC1 中,在@interface 上方的 .h 文件中添加协议:
@protocol ViewController1Protocol <NSObject>
- (void)dismissAndPresentVC2;
@end
并在同一文件的@interface 部分中向下声明一个属性来保存委托指针:
@property (nonatomic,weak) id <ViewController1Protocol> delegate;
在 VC1 .m 文件中,关闭按钮方法应该调用委托方法
- (IBAction)buttonPressedFromVC1:(UIButton *)sender {
[self.delegate dissmissAndPresentVC2]
}
现在在 mainVC 中,在创建 VC1 时将其设置为 VC1 的委托:
- (IBAction)present1:(id)sender {
ViewController1* vc = [[ViewController1 alloc] initWithNibName:@"ViewController1" bundle:nil];
vc.delegate = self;
[self present:vc];
}
并实现委托方法:
- (void)dismissAndPresent2 {
[self dismissViewControllerAnimated:NO completion:^{
[self present2:nil];
}];
}
present2: 可以是与您的VC2Pressed: 按钮 IBAction 方法相同的方法。请注意,它是从完成块调用的,以确保在 VC1 完全关闭之前不显示 VC2。
您现在正在从 VC1->VCMain->VC2 移动,因此您可能只希望对其中一个过渡进行动画处理。
更新
在您的 cmets 中,您对实现看似简单的事情所需的复杂性表示惊讶。我向你保证,这种委托模式对于 Objective-C 和 Cocoa 的大部分内容来说都是如此重要,而且这个例子是你能得到的最简单的例子,你真的应该努力适应它。
在 Apple 的 View Controller Programming Guide 中,他们有 this to say:
关闭呈现的视图控制器
当需要关闭呈现的视图控制器时,首选方法是让呈现的视图控制器关闭它。换句话说,只要有可能,呈现视图控制器的同一个视图控制器也应该负责解除它。尽管有几种技术可以通知呈现视图控制器其呈现的视图控制器应该被解除,但首选技术是委托。有关详细信息,请参阅“使用委托与其他控制器通信”。
如果你真的仔细考虑过你想要实现什么,以及你将如何去做,你会意识到给你的 MainViewController 发送消息来完成所有的工作是唯一合乎逻辑的出路,因为你不想使用导航控制器。如果您确实使用 NavController,实际上您是在“委派”(即使不是明确地)给 navController 来完成所有工作。需要有 some 对象来集中跟踪您的 VC 导航发生的情况,并且无论您做什么,都需要 some 方法与之通信。 p>
实际上,Apple 的建议有点极端……在正常情况下,您不需要指定专门的委托和方法,您可以依赖 [self presentingViewController] dismissViewControllerAnimated: - 在像您这样的情况下,您希望解雇对需要注意的远程对象产生其他影响。
这是您可以想象在没有所有代表麻烦的情况下工作的东西......
- (IBAction)dismiss:(id)sender {
[[self presentingViewController] dismissViewControllerAnimated:YES
completion:^{
[self.presentingViewController performSelector:@selector(presentVC2:)
withObject:nil];
}];
}
在要求呈现控制器解雇我们之后,我们有一个完成块,它调用presentingViewController中的一个方法来调用VC2。不需要委托。 (区块的一大卖点是它们在这些情况下减少了对代表的需求)。然而,在这种情况下,有一些事情会阻碍......
- 在 VC1 中您不知道 mainVC 实现了方法
present2 - 您最终可能会遇到难以调试的错误或崩溃。代表可以帮助您避免这种情况。
- 一旦 VC1 被解除,它就不会真正执行完成块......或者是吗? self.presentingViewController 还有什么意义吗?你不知道(我也不知道)...有了代表,你就没有这种不确定性。
- 当我尝试运行此方法时,它只是挂起,没有警告或错误。
所以请...花时间学习委托!
更新2
在您的评论中,您已经设法通过在 VC2 的关闭按钮处理程序中使用它来使其工作:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
这当然要简单得多,但它会给您带来许多问题。
紧密耦合
您将 viewController 结构硬连接在一起。例如,如果你要在 mainVC 之前插入一个新的 viewController,你需要的行为就会中断(你会导航到前一个)。在 VC1 中,您还必须 #import VC2。因此,您有很多相互依赖关系,这会破坏 OOP/MVC 目标。
使用委托,VC1 和 VC2 都不需要了解有关 mainVC 或其前件的任何信息,因此我们保持一切松散耦合和模块化。
内存
VC1 并没有消失,你还拿着两个指针指向它:
- mainVC 的
presentedViewController 属性
- VC2 的
presentingViewController 属性
您可以通过记录进行测试,也可以通过 VC2 进行测试
[self dismissViewControllerAnimated:YES completion:nil];
它仍然有效,仍然让您回到 VC1。
在我看来,这就像内存泄漏。
这方面的线索在您收到的警告中:
[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
// Attempt to dismiss from view controller <VC1: 0x715e460>
// while a presentation or dismiss is in progress!
逻辑崩溃了,因为您试图关闭呈现的 VC其中 VC2 是呈现的 VC。第二条消息并没有真正被执行——好吧,也许发生了一些事情,但你仍然有两个指向你认为已经摆脱的对象的指针。 (编辑 - 我已经检查过了,还不错,当你回到 mainVC 时,两个对象都会消失)
这是一种相当冗长的说法 - 请使用代表。如果有帮助,我在这里对模式进行了另一个简短的描述:
Is passing a controller in a construtor always a bad practice?
更新 3
如果你真的想避免委托,这可能是最好的出路:
在 VC1 中:
[self presentViewController:VC2
animated:YES
completion:nil];
但是不要忽略任何事情......正如我们确定的那样,无论如何它并没有真正发生。
在 VC2 中:
[self.presentingViewController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
我们(知道)我们还没有解雇 VC1,我们可以通过 VC1 回到 MainVC。 MainVC 关闭 VC1。因为 VC1 已经消失了,所以 VC2 也随之出现,所以你回到了 MainVC 的干净状态。
它仍然是高度耦合的,因为 VC1 需要知道 VC2,而 VC2 需要知道它是通过 MainVC->VC1 到达的,但它是最好的,没有一点显式委托。