views:

375

answers:

2

I created a class to launch the MailComposer so that my iPhone app would only have one place to go when generating various kinds of e-mail: some with attachments, some not. Some with pre-filled addresses, some not.

I didn't want my class implement UIViewController, but it has to so it can be the delegate for the MailComposer. Otherwise, the view controllers that call my class would themselves have to be delegates for the MailComposer, which defeats the purpose.

The downside of having my class be a view controller is that it has to load to the screen before it can modally bring up the MailComposer. Unfortunately, view controllers can't be transparent. The effect is, whatever is on screen gets covered by a solid white view controller for a moment before the MailComposer appears.

I could maybe live with that, but not this: after the MailComposer goes away, I'm left with my blank view controller occupying the screen. I ought to be able to get rid of it from within itself by calling this:

[self.parentViewController dismissModalViewControllerAnimated:NO];

But that dies a horrible death: "Loading 43365 stack frames..."

Has my class -- a UIViewController that pre-fills and then launches a MailComposer -- lost track of its parentViewController? It isn't nil, because I've tested for that.

As launched from within the current view controller...

// My class is called Email.
Email *oEmail = [[[Email alloc] init] retain];
// Red, to remind myself that I'd like to someday learn to make it transparent.
oEmail.view.backgroundColor = [UIColor redColor]; 
// Pre-fill whatever fields you want, and specify attachments.
oEmail.EmailSubject = @"I am truly stumped";
// This has to go on screen first.
[self presentModalViewController:oEmail animated:NO];
// Then this can happen, which brings up the MailComposer.
[oEmail f_SendEmail];
// Commenting out the next line didn't help, so I turned it back on.
[oEmail release];

Inside the class, you need the mailComposeController:didFinishWithResult:error: method to make the MailComposer go away, and for that to happen, the class has to be the MFMailComposeViewControllerDelegate. Here's what happens in there:

// This gets rid of the mail composer.
[self dismissModalViewControllerAnimated:YES];

// This never fails to get rid of other modal view controllers when called
// from within those controllers, but boy does it not work here.
[self.parentViewController dismissModalViewControllerAnimated:NO];

If you can help me, I will be truly thankful!

+1  A: 

Instead of calling

[self.parentViewController dismissModalViewControllerAnimated:NO];

I would set up a delegate for your 'Email' controller. An example of this sort of connection can be seen in the 'FlipSide' application template when creating a new project.

Basically, you would set up a delegate for the Email controller:

Email *oEmail = [[[Email alloc] init] retain];
oEmail.view.backgroundColor = [UIColor redColor]; 
oEmail.EmailSubject = @"I am truly stumped";
[self presentModalViewController:oEmail animated:NO];
[oEmail f_SendEmail];
[oEmail setDelegate:self];
[oEmail release];

Then in the Email .h file:

@protocol EmailDelegate
-(void)emailDidFinish;
@end

@implementation Email : UIViewController {
      // Other stuff
      id <EmailDelegate> delegate;
}

@property (nonatomic, assign) id <EmailDelegate> delegate;

@end

Make sure you @synthesize delegate, then when you're ready to dismiss it call:

// This gets rid of the mail composer.
[self dismissModalViewControllerAnimated:YES];

// This never fails to get rid of other modal view controllers when called
// from within those controllers, but boy does it not work here.
if (delegate && [delegate respondsToSelector:@selector(emailDidFinish)]){
   [delegate emailDidFinish];
}

And finally, in your original view controller, make sure you've got in the .h file and then have:

-(void)emailDidFinish {
   [self dismissModal...];
}

Hope that helps.

Tom Irving
I will test this. Intriguing. I really don't care for delegating, because how can an object really be self-contained if it has to hand off responsibility to other objects? However, the MailComposer is an Apple-created object with no associated code module where we can add custom code. So we make some other object the delegate and then can write our code there. The trouble is, you have to put that code in every ViewController that might display a MailComposer. I can't tell if your suggested solution addresses that until I test.
Scott Pendleton
A: 

I had the same problem and I solved it a different way.

I created a function which pops current ViewController. In the h:

-(void)ics;

In the cpp:

-(void)ics{
  //[self.navigationController popViewControllerAnimated:NO];   
  [self.navigationController popToRootViewControllerAnimated:YES];
}

and called it after dismissing the MailComposer:

[self dismissModalViewControllerAnimated:YES];
[self ics];

voila!

Alexander Mardari