我已经看过相关的问题,但是没有任何问题可以解决我的问题。
我正在尝试连续使用dismissViewControllerAnimated:animated:completion
和presentViewControllerAnimated:animated:completion
。使用 Storyboard ,我通过部分 curl 动画模态呈现InfoController。
部分 curl 在InfoController上显示了一个我想启动MFMailComposeViewController
的按钮。因为部分 curl 部分隐藏了MFMailComposeViewController
,所以我首先要通过取消部分 curl 的动画来关闭InfoController。然后我想要MFMailComposeViewController
进行动画处理。
目前,当我尝试此操作时,部分 curl 不会激活,但是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
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 completion:^{}];
中注释掉(IBAction)emailMe
,则MFMailComposeViewController
会动画化,但它会部分隐藏在部分 curl 的后面。如何首先消除 curl ,然后在MFMailComposeViewController
中设置动画?非常感谢!
编辑:如果我注释掉
[self dismissViewControllerAnimated:YES completion:^{}];
,则 View 外观如下图所示最佳答案
这是由于不清楚的父 subview Controller 关系导致的 View Controller 之间的通信问题。如果不使用协议(protocol)和委托(delegate),则无法正常工作。
经验法则是:
(听起来很无情,但是如果您考虑一下,这是有道理的)。
转换为ViewController关系:呈现 View Controller 需要了解其 subview Controller ,但是 subview Controller 必须不了解有关其父(呈现) View Controller 的信息: subview Controller 使用其委托(delegate)将消息发送回其(未知) parent 。
您知道如果必须在 header 中添加@Class声明来修复已链接的#import编译器警告,那是不对的。交叉引用始终是一件坏事(顺便说一句,这也是为什么委托(delegate)应该始终为(分配)且永不为(强)的原因,因为这将导致交叉引用循环和一组僵尸)
因此,让我们来看一下项目中的这些关系:
就像您没有说的那样,我假设调用 Controller 的名称为MainController。因此,我们将拥有:
所以你想拥有这个:
1. InfoController:定义一个@protocol InfoControllerDelegate:
子 Controller 定义一个协议(protocol),并具有一个未指定类型的委托(delegate),该委托(delegate)符合其协议(protocol)(换句话说:委托(delegate)可以是任何对象,但必须具有此一种方法)
@protocol InfoControllerDelegate
- (void)returnAndSendMail;
@end
@interface InfoControllerDelegate : UIViewController // …
@property (assign) id<InfoControllerDelegate> delegate
// ...
@end
2. MainController拥有并创建InfoController和MFMailController
...并且MainController同时采用了InfoControllerDelegate和MFMailComposeDelegate协议(protocol),因此它可以再次关闭MFMailComposer(请注意,这不是而且很可能不需要强属性,只需在此处显示即可清楚)
@interface MainController <InfoControllerDelegate, MFMailComposeViewControllerDelegate>
@property (strong) InfoController *infoController;
@property (strong) MFMailComposeViewController *mailComposer;
3. MainController呈现其InfoViewController并将其自身设置为委托(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(类)的情况下将消息发送回MainController。无需#import。它所知道的只是它是一个具有-returnAndSendMail方法的对象;这就是我们需要知道的。
通常,您将使用alloc/init创建viewController,并让它延迟加载xib。
或者,如果您正在使用 Storyboard 和Segues,则可能要拦截(在MainController中)segue,以便以编程方式设置委托(delegate):
- (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中,按下电子邮件按钮:
当按下电子邮件按钮时,将调用委托(delegate)(MainController)。请注意,self.delegate是MainController无关紧要,它具有此方法-returnAndSendMail无关紧要
- (IBAction)sendEmailButtonPressed:(id)sender {
// this method dismisses ourself and sends an eMail
[self.delegate returnAndSendMail];
}
...然后在这里(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];
}];
}
因此,使用MFMailController进行的操作实际上与使用InfoController进行的操作相同。两者都有其未知的委托(delegate),因此他们可以发回邮件,如果有,您可以将其解雇并继续执行您应做的事情。
注释
关于iphone - 警告: Attempt to present ViewController on ViewController whose view is not in the window hierarchy,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16384121/