-->

模态视图控制器 - 如何显示和消除(modal View controllers - how to

2019-07-20 00:32发布

我打破我的头的最后一个星期如何与展示,解聘多个视图控制器解决的问题。 我创建了一个示例项目,并直接从项目粘贴代码。 我有3个与它们对应的.xib文件视图控制器。 MainViewController,VC1和VC2。 我有主视图控制器上的两个按钮。

- (IBAction)VC1Pressed:(UIButton *)sender
{
    VC1 *vc1 = [[VC1 alloc] initWithNibName:@"VC1" bundle:nil];
    [vc1 setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
    [self presentViewController:vc1 animated:YES completion:nil];
}

这将打开VC1没有问题。 在VC1,我还有一个按钮应该打开VC2,而在同一时间解雇VC1。

- (IBAction)buttonPressedFromVC1:(UIButton *)sender
{
    VC2 *vc2 = [[VC2 alloc] initWithNibName:@"VC2" bundle:nil];
    [vc2 setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
    [self presentViewController:vc2 animated:YES completion:nil];
    [self dismissViewControllerAnimated:YES completion:nil];
} // This shows a warning: Attempt to dismiss from view controller <VC1: 0x715e460> while a presentation or dismiss is in progress!


- (IBAction)buttonPressedFromVC2:(UIButton *)sender
{
    [self dismissViewControllerAnimated:YES completion:nil];
} // This is going back to VC1. 

我希望它回到主视图控制器,而在同一时间VC1应该已经从内存为好去除。 VC1应仅显示了,当我在VC1按钮,点击主控制器上。

主视图控制器上的其它按钮也应该能够显示VC2直接绕过VC1和应回到主控制器时被点击VC2的按钮。 没有长时间运行的代码,循环或计时器。 只是裸骨调用视图控制器。

Answer 1:

这条线:

[self dismissViewControllerAnimated:YES completion:nil];

未发送消息给它本身,它实际上将消息发送给其呈现的VC,要求它做解聘。 当你提出一个VC,创建呈现VC和呈现的一个之间的关系。 因此,尽管它是展示你不应该破坏呈现VC(所提出的VC不能发送解除消息回来...)。 如果你还没有真正考虑的是你离开的迷茫状态的应用程序。 见我的回答驳回呈现的视图控制器中,我推荐这种方法可以更清楚地写着:

[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为方法。 请注意,从完成块称为确保VC2没有呈现直到VC1被完全驳回。

现在,您是从VC1-> VCMain-> VC2移动,所以你可能只想进行动画过渡的一个。

更新

在你的意见,你在实现一个看似简单的事情所需的复杂性表示惊讶。 我向你保证,这个委托模式是如此重要的多的Objective-C和Cocoa的,而这个例子是关于最简单的,你可以得到,那你真的应该作出的努力得到舒服。

在苹果公司的视图控制器编程指南他们有这样一段话 :

驳回所呈现的视图控制器

当谈到时间以关闭一个呈现视图控制器,优选的做法是让所述呈现视图控制器关闭它。 换句话说,只要有可能,所呈现的视图控制器也应承担责任,驳回其相同的视图控制器。 虽然有用于通知其呈现视图控制器应当驳回呈现视图控制器的几种技术中,优选的技术是委托。 有关详细信息,请参阅“使用代表团与其他控制器进行通信。”

如果你真的想通过你想要什么来实现的,你是怎么回事的话,你会发现,你的消息传递给MainViewController做所有的工作都出来给你不希望使用NavigationController的唯一合乎逻辑的方式。 如果使用一个NavController,实际上你是“委托”,即使没有明确,到navController做所有的工作。 需要有一些物体,保持发生了什么事情与你的VC导航中心的轨道,你需要与它通信的一些方法,不管你做什么。

实际上苹果的建议是有点极端......在正常情况下,你不需要做专门的委托和方法,你可以依靠[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意味着什么了吗? 你不知道(我也不)......一个代表,你没有这种不确定性。
  • 当我试图运行这个方法,它只是没有警告或错误挂起。

所以,请...花时间来学习代表团!

UPDATE2

在你的评论已设法使其在VC2的解雇按钮处理程序使用此工作:

 [self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil]; 

这当然是要简单得多,但它给你留下了一些问题。

紧耦合
你是硬接线的结构的viewController一起。 例如,如果你要插入一个新的viewController之前mainVC,你所需的行为会打破(你会浏览到之前一个)。 在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)

这是说的相当冗长的方式 - 请使用委托。 如果有帮助,我在这里做图案的另一简要说明:
正在通过一个控制器在construtor总是不好的做法?

更新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到达,但它是最好的,你会得到没有一点明确的代表团。



Answer 2:

我想你是误会关于iOS的模式视图控制器的一些核心概念。 当您关闭VC1,通过VC1提出的任何视图控制器被解雇也是如此。 苹果用于模态视图控制器以堆叠的方式流入 - 你的情况VC2由VC1呈现。 您只要从VC1你现在VC2驳回VC1所以它是一团糟。 为了达到你想要什么,buttonPressedFromVC1应该有VC1本身驳回后,立即mainVC目前VC2。 我认为这是可以不委托来实现。 沿着线的东西:

UIViewController presentingVC = [self presentingViewController];
[self dismissViewControllerAnimated:YES completion:
 ^{
    [presentingVC presentViewController:vc2 animated:YES completion:nil];
 }];

需要注意的是self.presentingViewController存储在其他一些变量,因为VC1驳回本身后,你应该没有任何对它的引用。



Answer 3:

例如,在斯威夫特 ,描绘上述代工厂的解释和苹果的文档:

  1. 在此基础上苹果的文档及以上使用委托设计模式(修正一些错误),presentViewController版本代工的解释:

ViewController.swift

import UIKit

protocol ViewControllerProtocol {
    func dismissViewController1AndPresentViewController2()
}

class ViewController: UIViewController, ViewControllerProtocol {

    @IBAction func goToViewController1BtnPressed(sender: UIButton) {
        let vc1: ViewController1 = self.storyboard?.instantiateViewControllerWithIdentifier("VC1") as ViewController1
        vc1.delegate = self
        vc1.modalTransitionStyle = UIModalTransitionStyle.FlipHorizontal
        self.presentViewController(vc1, animated: true, completion: nil)
    }

    func dismissViewController1AndPresentViewController2() {
        self.dismissViewControllerAnimated(false, completion: { () -> Void in
            let vc2: ViewController2 = self.storyboard?.instantiateViewControllerWithIdentifier("VC2") as ViewController2
            self.presentViewController(vc2, animated: true, completion: nil)
        })
    }

}

ViewController1.swift

import UIKit

class ViewController1: UIViewController {

    var delegate: protocol<ViewControllerProtocol>!

    @IBAction func goToViewController2(sender: UIButton) {
        self.delegate.dismissViewController1AndPresentViewController2()
    }

}

ViewController2.swift

import UIKit

class ViewController2: UIViewController {

}
  1. 基础上铸造的解释上面使用委托设计模式(修正一些错误),pushViewController版本:

ViewController.swift

import UIKit

protocol ViewControllerProtocol {
    func popViewController1AndPushViewController2()
}

class ViewController: UIViewController, ViewControllerProtocol {

    @IBAction func goToViewController1BtnPressed(sender: UIButton) {
        let vc1: ViewController1 = self.storyboard?.instantiateViewControllerWithIdentifier("VC1") as ViewController1
        vc1.delegate = self
        self.navigationController?.pushViewController(vc1, animated: true)
    }

    func popViewController1AndPushViewController2() {
        self.navigationController?.popViewControllerAnimated(false)
        let vc2: ViewController2 = self.storyboard?.instantiateViewControllerWithIdentifier("VC2") as ViewController2
        self.navigationController?.pushViewController(vc2, animated: true)
    }

}

ViewController1.swift

import UIKit

class ViewController1: UIViewController {

    var delegate: protocol<ViewControllerProtocol>!

    @IBAction func goToViewController2(sender: UIButton) {
        self.delegate.popViewController1AndPushViewController2()
    }

}

ViewController2.swift

import UIKit

class ViewController2: UIViewController {

}


Answer 4:

拉杜Simionescu - 真棒工作! 以下您的斯威夫特爱好者的解决方案:

@IBAction func showSecondControlerAndCloseCurrentOne(sender: UIButton) {
    let secondViewController = storyboard?.instantiateViewControllerWithIdentifier("ConrollerStoryboardID") as UIViewControllerClass // change it as You need it
    var presentingVC = self.presentingViewController
    self.dismissViewControllerAnimated(false, completion: { () -> Void   in
        presentingVC!.presentViewController(secondViewController, animated: true, completion: nil)
    })
}


Answer 5:

我想这一点:

MapVC处于全屏地图。

当我按下一个按钮,它会打开PopupVC(而不是在全屏幕)地​​图上方。

当我在PopupVC按下一个按钮,返回到MapVC,然后我想执行viewDidAppear。

我这样做:

MapVC.m:在按钮动作,赛格瑞编程,并设置委托

- (void) buttonMapAction{
   PopupVC *popvc = [self.storyboard instantiateViewControllerWithIdentifier:@"popup"];
   popvc.delegate = self;
   [self presentViewController:popvc animated:YES completion:nil];
}

- (void)dismissAndPresentMap {
  [self dismissViewControllerAnimated:NO completion:^{
    NSLog(@"dismissAndPresentMap");
    //When returns of the other view I call viewDidAppear but you can call to other functions
    [self viewDidAppear:YES];
  }];
}

PopupVC.h:@interface之前,添加的协议

@protocol PopupVCProtocol <NSObject>
- (void)dismissAndPresentMap;
@end

@interface之后,一个新的属性

@property (nonatomic,weak) id <PopupVCProtocol> delegate;

PopupVC.m:

- (void) buttonPopupAction{
  //jump to dismissAndPresentMap on Map view
  [self.delegate dismissAndPresentMap];
}


Answer 6:

我已经提出在使用UINavigationController的解决了这个问题。 在MainVC,呈现VC1时

let vc1 = VC1()
let navigationVC = UINavigationController(rootViewController: vc1)
self.present(navigationVC, animated: true, completion: nil)

在VC1,当我想显示VC2并关闭在同一时间(只有一个动画)VC1,我可以有一个推动动漫

let vc2 = VC2()
self.navigationController?.setViewControllers([vc2], animated: true)

而在VC2,当接近视图控制器,像往常一样,我们可以使用:

self.dismiss(animated: true, completion: nil)


文章来源: modal View controllers - how to display and dismiss