警告:尝试在视图不在窗口层次结构中的ViewController上显示ViewController [英] Warning: Attempt to present ViewController on ViewController whose view is not in the window hierarchy

查看:101
本文介绍了警告:尝试在视图不在窗口层次结构中的ViewController上显示ViewController的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have already looked through related questions but nothing has solved my problem.

我正在尝试使用 dismissViewControllerAnimated:animated:completion presentViewControllerAnimated:animated:completion 相继出现。使用故事板,我通过部分卷曲动画模拟呈现InfoController。

I am attempting to use dismissViewControllerAnimated:animated:completion and presentViewControllerAnimated:animated:completion in succession. Using a storyboard, I am modally presenting InfoController via a partial curl animation.

部分卷曲显示InfoController上的一个按钮,我想要启动 MFMailComposeViewController 。因为部分卷曲部分隐藏了 MFMailComposeViewController ,所以我首先要通过取消动画部分卷曲来解除InfoController。然后我想要 MFMailComposeViewController 进行动画制作。

The partial curl reveals a button on InfoController that I want to initiate the MFMailComposeViewController. Because the partial curl partially hides the MFMailComposeViewController, I first want to dismiss InfoController by un-animating the partial curl. Then I want the MFMailComposeViewController to animate in.

目前,当我尝试这个时,部分卷曲没有 - 动画,但 MFMailComposeViewController 未显示。我还有一个警告:

At present, when I try this, the partial curl un-animates, but the MFMailComposeViewController doesn't get presented. I also have a warning of:


警告:尝试提供MFMailComposeViewController:on
InfoController:其视图不在窗口中层次结构!

Warning: Attempt to present MFMailComposeViewController: on InfoController: whose view is not in the window hierarchy!

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

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];
}

另外,如果我注释掉 [self dismissViewControllerAnimated :YES完成:^ {}]; in (IBAction)emailMe MFMailComposeViewController 动画,但它部分隐藏在部分卷曲后面。我怎样才能首先解除curl然后在 MFMailComposeViewController 中设置动画?

Also, if I comment out the [self dismissViewControllerAnimated:YES completion:^{}]; in (IBAction)emailMe, the MFMailComposeViewController animates in but it is partially hidden behind the partial curl. How can I first dismiss the curl and then animate in the MFMailComposeViewController?

非常感谢!

编辑:如果我注释掉 [self dismissViewControllerAnimated:YES completion:^ {}],那么视图的图像如下;

推荐答案

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

It's a communications issue between view-controllers resulting out of an unclear parent-child view-controller relationship... Without using a protocol and delegation, this won't work properly.

经验法则是:


  • 父母知道他们的孩子,但孩子们不需要知道他们的父母。

  • Parents know about their children, but children don't need to know about their parents.

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

(Sounds heartless, but it makes sense, if you think about it).

转换为ViewController关系:呈现视图控制器需要了解其子视图控制器,但子视图控制器必须不知道关于其父(呈现)视图控制器:子视图控制器使用其委托发送消息对于他们(未知)的父母。

Translated to ViewController relationships: Presenting view controllers need to know about their child view controllers, but child view controllers must not know about their parent (presenting) view controllers: child view controllers use their delegates to send messages back to their (unknown) parents.

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

You know that something is wrong if you have to add @Class declarations in your headers to fix chained #import compiler warnings. Cross-references are always a bad thing (btw, that's also the reason why delegates should always be (assign) and never (strong), as this would result in a cross-reference-loop and a group of Zombies)

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

So, let's look at these relationships for your project:

当你没说,我假设调用控制器名为 MainController 。所以我们将:

As you didn't say, I assume the calling controller is named MainController. So we'll have:


  • 一个MainController,父,拥有和呈现InfoController

  • 一个InfoController(部分显示在MainController下面),拥有并呈现:

  • MailComposer,由于它将显示在MainController下面,因此无法显示。

所以你想要这个:


  • 一个MainController,父母,拥有和展示InfoController& MFMailController

  • 一个InfoController(部分显示在MainController下面)

  • InfoController视图中的Email-Button。单击它将通知MainController(它的未知委托)它应该解除InfoController(本身)并呈现MailComposer

  • 将拥有的MailComposer(由MainController而不是InfoController

  • A MainController, the parent, owning and presenting the InfoController & MFMailController
  • An InfoController (revealed partially below MainController)
  • an "Email-Button" in the InfoController's view. On click it will inform the MainController (it's unknown delegate) that it should dismiss the InfoController (itself) and present the MailComposer
  • an MailComposer that will be owned (presented & dismissed) by the MainController and not by the InfoController

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

The child controller defines a protocol and has a delegate of unspecified type which complies to its protocol (in other words: the delegate can be any object, but it must have this one method)

@protocol InfoControllerDelegate
- (void)returnAndSendMail;
@end

@interface InfoControllerDelegate : UIViewController // …

@property (assign) id<InfoControllerDelegate> delegate

// ...

@end



2。 MainController拥有并创建InfoController和MFMailController



...并且MainController采用InfoControllerDelegate和MFMailComposeDelegate协议,因此它可以再次关闭MFMailComposer(注意,这不是也可能不应该是强大的属性,只是在这里显示这一点以明确说明)

2. MainController owns and creates both InfoController and MFMailController

...and the MainController adopts both the InfoControllerDelegate and the MFMailComposeDelegate protocol, so it can dismiss the MFMailComposer again (Note, that doesn't and probably shouldn't need to be strong properties, just showing this here to make it clear)

@interface MainController <InfoControllerDelegate, MFMailComposeViewControllerDelegate>

@property (strong) InfoController *infoController;
@property (strong) MFMailComposeViewController *mailComposer;



3。 MainController提供它的InfoViewController并将自己设置为委托



3. MainController presents its InfoViewController and sets itself as the delegate

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

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

The 'infoController.delegate = self' is the crucial step. This gives the infoController a possibility to send a message back to the MainController without knowing it ObjectType (Class). No #import required. All it knows, it that it's an object that has the method -returnAndSendMail; and that's all we need to know.

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

Typically you would create your viewController with alloc/init and let it load its xib lazily. Or, if you're working with Storyboards and Segues, you probably want to intercept the segue (in MainController) in order to set the delegate programmatically:

- (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;
    }
}



4。在InfoController中,按下电子邮件按钮:



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

4. In InfoController, the eMail button is pressed:

When the eMail button is pressed, the delegate (MainController) is called. Note that it's not relevant that self.delegate is the MainController, it's just relevant that it has this method -returnAndSendMail

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

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

...and here (in MainController!), you'll dismiss the InfoController (clean up because it's the responsibility of the MainController) and present the 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];
    }];
}

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

so, what you're doing with the MFMailController is practically the same as with the InfoController. Both have their unknown delegate, so they can message back and if they do, you can dismiss them and proceed with whatever you should to do.


  • -dismissViewControllerAnimated:不应从子视图控制器调用。在文档中,它说:呈现视图控制器负责解除它所呈现的视图控制器。。这就是为什么我们仍然需要授权。它很有用,因为父母的关系和责任很重要!确实。你不能创造一些东西,然后留下它。嗯,你可以,但你不应该。

  • 如果你不使用一个透露的视图控制器动画,你可以链接这些父(采用子协议) - 子(定义协议为父和为孙子采用协议) - 孙子(定义协议......)

  • 再次:一个MainController拥有并呈现 all 子viewController的设计是真的是一个糟糕的设计。所以提出的解决方案是关于协议和通信而不是将所有内容放在一个MainController中

  • 我不认为作为编码技术的块可以使我们免于需要定义关系并声明协议

  • 希望有所帮助

  • -dismissViewControllerAnimated:completion: should not be called from the child view controller. In the docs, it says: "The presenting view controller is responsible for dismissing the view controller it presented.". That's why we still need delegation. And it's useful, because the relationships and responsibilities of parents are important! Indeed. You can't create something and then just leave it be. Well, you can, but you shouldn't.
  • if you wouldn't use a revealing view controller animation, you could chain these Parent (adopting Child Protocol) - Child (defining protocol for parent and adopting protocol for grandchild) - Grandchild (defining protocol for ...
  • Again: a design where one MainController is owning and presenting all the child viewController is really a bad design. So the solution presented is about protocols and communication and not about putting everything in one MainController
  • I don't think that blocks as a coding technology free us from the need to define relationships and declare Protocols
  • Hope that helps

这篇关于警告:尝试在视图不在窗口层次结构中的ViewController上显示ViewController的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆