警告:尝试在视图不在窗口层次结构中的ViewController上显示ViewController

dia*_*nna 9 iphone objective-c ios

我已经查看过相关问题,但没有解决我的问题.

我试图使用dismissViewControllerAnimated:animated:completionpresentViewControllerAnimated:animated:completion连续.使用故事板,我通过部分卷曲动画模拟呈现InfoController.

部分卷曲显示我想要启动的InfoController上的按钮MFMailComposeViewController.因为部分卷曲部分隐藏了MFMailComposeViewController,我首先要通过取消动画部分卷曲来解除InfoController.然后我想要MFMailComposeViewController动画片.

目前,当我尝试这个时,部分卷曲没有动画,但是MFMailComposeViewController没有得到呈现.我也有一个警告:

警告:尝试在InfoController上显示MFMailComposeViewController:其视图不在窗口层次结构中!

InfoController.h:

#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>

@interface InfoController : UIViewController <MFMailComposeViewControllerDelegate>

@property (weak, nonatomic) IBOutlet UIButton *emailMeButton;

-(IBAction)emailMe:(id)sender;

@end
Run Code Online (Sandbox Code Playgroud)

InfoController.m

#import "InfoController.h"

@interface InfoController ()

@end

@implementation InfoController 

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (IBAction)emailMe:(id)sender {
    [self dismissViewControllerAnimated:YES completion:^{
        [self sendMeMail];
    }];
}

- (void)sendMeMail {
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
if([MFMailComposeViewController canSendMail]){
    if(mailController)
    {
        NSLog(@"%@", self); // This returns InfoController 
        mailController.mailComposeDelegate = self;
        [mailController setSubject:@"I have an issue"];
        [mailController setMessageBody:@"My issue is ...." isHTML:YES];
        [self presentViewController:mailController animated:YES completion:nil];
    }
}
}

- (void)mailComposeController:(MFMailComposeViewController*)controller
          didFinishWithResult:(MFMailComposeResult)result
                        error:(NSError*)error;
{
    if (result == MFMailComposeResultSent) {
        NSLog(@"It's sent!");
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}
Run Code Online (Sandbox Code Playgroud)

此外,如果我注释掉[self dismissViewControllerAnimated:YES completion:^{}];in (IBAction)emailMe,MFMailComposeViewController动画,但它部分隐藏在部分卷曲后面.我怎样才能首先解除卷曲然后在MFMailComposeViewController

非常感谢!

编辑:如果我发表评论,下面是视图的图像 [self dismissViewControllerAnimated:YES completion:^{}];

在此输入图像描述

auc*_*uco 19

这是视图控制器之间的通信问题,由于父子视图控制器关系不明确而导致......如果不使用协议和委派,这将无法正常工作.

经验法则是:

  • 父母知道他们的孩子,但孩子不需要了解他们的父母.

(听起来很无情,但如果你考虑的话,这是有意义的).

转换为ViewController关系:呈现视图控制器需要了解其子视图控制器,但子视图控制器必须不知道其父(呈现)视图控制器:子视图控制器使用其委托将消息发送回其(未知)父节点.

如果必须在标头中添加@Class声明来修复链式#import编译器警告,那么就知道出了问题.交叉引用总是一件坏事(顺便说一下,这也是委托应该始终(分配)而永远(强)的原因,因为这会导致交叉引用循环和一组僵尸)


那么,让我们看看你的项目的这些关系:

正如你没说的那样,我假设调用控制器名为MainController.所以我们将:

  • 一个MainController,父,拥有和呈现InfoController
  • InfoController(部分显示在MainController下面),拥有并呈现:
  • MailComposer,因为它将显示在MainController下面而无法显示.

所以你想拥有这个:

  • 一个MainController,父,拥有和呈现InfoController和MFMailController
  • InfoController(部分显示在MainController下面)
  • InfoController视图中的"Email-Button".单击它将通知MainController(它的未知委托)它应该解除InfoController(本身)并呈现MailComposer
  • 一个MailComposer将由MainController而不是InfoController拥有(显示和解散)

1. InfoController:定义@protocol InfoControllerDelegate:

子控制器定义一个协议,并且具有符合其协议的未指定类型的委托(换句话说:委托可以是任何对象,但它必须具有这一个方法)

@protocol InfoControllerDelegate
- (void)returnAndSendMail;
@end

@interface InfoControllerDelegate : UIViewController // …

@property (assign) id<InfoControllerDelegate> delegate

// ...

@end
Run Code Online (Sandbox Code Playgroud)

2. MainController拥有并创建InfoController和MFMailController

...并且MainController同时采用InfoControllerDelegate和MFMailComposeDelegate协议,因此它可以再次关闭MFMailComposer(注意,这不会也可能不需要是强大的属性,只是在这里显示它以使其清楚)

@interface MainController <InfoControllerDelegate, MFMailComposeViewControllerDelegate>

@property (strong) InfoController *infoController;
@property (strong) MFMailComposeViewController *mailComposer;
Run Code Online (Sandbox Code Playgroud)

3. MainController展示其InfoViewController并将其自身设置为委托

// however you get the reference to InfoController, just assuming it's there
infoController.delegate = self;
[self presentViewController:infoController animated:YES completion:nil];
Run Code Online (Sandbox Code Playgroud)

'infoController.delegate = self'是至关重要的一步.这使infoController有可能在不知道ObjectType(Class)的情况下将消息发送回MainController.不需要#import.它只知道,它是一个具有方法-returnAndSendMail的对象; 这就是我们需要知道的全部内容.

通常你会用alloc/init创建你的viewController,并让它懒洋洋地加载它的xib.或者,如果您正在使用Storyboards和Segues,您可能希望拦截segue(在MainController中)以便以编程方式设置委托:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // hook in the segue to set the delegate of the target
    if([segue.identifier isEqualToString:@"infoControllerSegue"]) {
        InfoController *infoController = (InfoController*)segue.destinationViewController;
        infoController.delegate = self;
    }
}
Run Code Online (Sandbox Code Playgroud)

4.在InfoController中,按下eMail按钮:

按下eMail按钮时,将调用委托(MainController).请注意,self.delegate是MainController并不重要,它与此方法相关--returnAndSendMail

- (IBAction)sendEmailButtonPressed:(id)sender {
    // this method dismisses ourself and sends an eMail
    [self.delegate returnAndSendMail];
}
Run Code Online (Sandbox Code Playgroud)

...在这里(在MainController中!),你将解散InfoController(清理,因为它是MainController的责任)并呈现MFMailController:

- (void)returnAndSendMail {
    // dismiss the InfoController (close revealing page)
    [self dismissViewControllerAnimated:YES completion:^{
        // and present MFMailController from MainController
        self.mailComposer.delegate = self;
        [self presentViewController:self.mailComposer animated:YES completion:nil];
    }];
}
Run Code Online (Sandbox Code Playgroud)

所以,你使用MFMailController做的事情几乎和InfoController一样.两者都有他们未知的代表,所以他们可以回复消息,如果他们这样做,你可以解雇他们并继续你应该做的任何事情.

笔记

  • -dismissViewControllerAnimated:completion:不应该从子视图控制器调用.在文档中,它说:" 呈现视图控制器负责解除它所呈现的视图控制器. " 这就是为什么我们仍然需要授权.它很有用,因为父母的关系和责任很重要!确实.你不能创造一些东西然后留下它.嗯,你可以,但你不应该.
  • 如果您不使用显示视图控制器动画,您可以链接这些父(采用子协议) - 子(定义父级协议和为孙子采用协议) - 孙子(定义协议...
  • 再说一次:一个MainController拥有并呈现所有子viewController的设计实际上是一个糟糕的设计.因此,所提出的解决方案是关于协议和通信,而不是将所有内容放在一个MainController中
  • 我不认为作为编码技术的块使我们无需定义关系和声明协议
  • 希望有所帮助


Thi*_*ama 1

对这个,

- (IBAction)emailMe:(id)sender {
    [self dismissViewControllerAnimated:YES completion:^{
        [self sendMeMail];
    }];
}
Run Code Online (Sandbox Code Playgroud)

关闭self viewController 后,您无法从 self 呈现视图控制器。

那你能做什么呢?

1)改变按钮按下方式,

- (IBAction)emailMe:(id)sender {
    [self sendMeMail];
}
Run Code Online (Sandbox Code Playgroud)

2)当mailViewController被关闭时,您可以关闭self viewController。

- (void)mailComposeController:(MFMailComposeViewController*)controller
          didFinishWithResult:(MFMailComposeResult)result
                        error:(NSError*)error;
{
    if (result == MFMailComposeResultSent) {
        NSLog(@"It's sent!");
    }
    [controller dismissViewControllerAnimated:NO completion:^ {
        [self dismissViewControllerAnimated:YES completion:nil];
    }];

}
Run Code Online (Sandbox Code Playgroud)