cocos2d for iPhone

A fast, easy to use, free, and community supported 2D game engine

Register or log in - lost password?
  • Blog
  • Store
  • Games
  • Documentation
  • Download
  • About

cocos2d for iPhone » Programming » iPhone SDK / UIKit / Objective-C / Xcode

Sending an email in game with MessageUI

(26 posts) (18 voices)
  • Started 2 years ago by Jonathan
  • Latest reply from sascsedu

Tags:

  • challenge email
  • cocoa-touch
  • cocos2d
  • cocos2d-iPhone MFMailComposeViewController
  • dashyapps
  • dismissModalViewControllerAnimated
  • email
  • landscape
  • mail
  • Mail Landscape Problem
  • MessageUI
  • MFMailComposeViewController
  • rotation
  • Send Mail
  • UIDeviceOrientation
  • UIInterfaceOrientation
  • uiviewcontroller
  1. Jonathan
    Member

    Hello everybody,

    I have been looking into sending in app emails and thought I should write a summary of what I have found and ask for help with the last point I have not resolved.

    Well first of all MessageUI.framework needs to be added to your project.

    I have decided to display the email page in dedicated scene that is loaded from the menu with simply replacing the scene with the director.

    EmailScene.h :

    #import "cocos2d.h"
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #import <MessageUI/MessageUI.h>
    #import <MessageUI/MFMailComposeViewController.h>
    
    @interface EmailScene : Scene <MFMailComposeViewControllerDelegate>
    {
    	IBOutlet UILabel *message;
    	MFMailComposeViewController *picker;
    }
    
    @property (nonatomic, retain) IBOutlet UILabel *message;
    -(void)displayComposerSheet;
    @end

    EmailScene.m :

    #import "EmailScene.h"
    #import "MenuScene.h"
    
    @implementation EmailScene
    
    @synthesize message;
    
    - (id) init {
        self = [super init];
        if (self != nil) {
    	[self displayComposerSheet];
        }
        return self;
    }
    
    // Displays an email composition interface inside the application. Populates all the Mail fields.
    -(void)displayComposerSheet
    {
    	[[Director sharedDirector] pause];
    
    	picker = [[MFMailComposeViewController alloc] init];
    	picker.mailComposeDelegate = self;
    
    	//Fill in the email as you see fit
    
    	//display the view
    	[[[Director sharedDirector] openGLView] addSubview:picker.view];
    
    	[picker presentModalViewController:picker animated:YES];
    	[picker release];
    }
    
    // Dismisses the email composition interface when users tap Cancel or Send. Proceeds to update the message field with the result of the operation.
    - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    {
    
    	[[Director sharedDirector] resume];
    
    	//return to previous scene
    	MenuScene * menuScene = [MenuScene node];
    	[[Director sharedDirector] replaceScene:menuScene];
    
    	//dismiss view after otherwise the code is not executed
    	[picker.view removeFromSuperview];
    	[picker dismissModalViewControllerAnimated:YES];
    }
    @end

    With this code the email view is displayed and functional but when returning from it, either the view is not removed properly or the director has lost the current view in which to run.

    The email view stays visible but the director runs the code from the menu scene (visible in the logs only).

    I don't think I am missing much for this to work so any help would be appreciated.

    Posted 2 years ago #
  2. Jonathan
    Member

    I found a solution that seems to work

    In -(void)displayComposerSheet

    I added:

    UIViewController* emailController = [[UIViewController alloc] init];
    [emailController setView:[[Director sharedDirector] openGLView]];

    and changed

    [picker presentModalViewController:picker animated:YES];

    to

    [emailController presentModalViewController:picker animated:YES];

    But I now get this message while the email view is displayed:

    Failed to swap renderbuffer in -[EAGLView swapBuffers]

    But this does create any problems at the moment.

    Posted 2 years ago #
  3. Jonathan
    Member

    If anyone is interested I also get a crash if I close the app while in the email scene:

    cocos2d: deallocing <EmailScene = 04CB06A0 | Tag = -1>
    *** Assertion failure in -[DisplayLinkDirector detach], /cocos2d/Director.m:385
    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'FATAL: Director: Can't detach the OpenGL View, because it is not attached. Attach it first.'
     Stack: (
    ...
    Posted 2 years ago #
  4. JonKean
    Member

    I ran into this same problem, and fixed it by stopping/starting the Director animations.

    Before presenting the modal controller:
    [[Director sharedDirector] stopAnimation];

    then after dismissing the modal controller:
    [[Director sharedDirector] startAnimation];

    Posted 2 years ago #
  5. georgeburns
    Member

    [picker.view.superview removeFromSuperview];
    as opposed to...
    '[picker.view removeFromSuperview];'
    fixed it for my version... little different than yours though.

    Posted 2 years ago #
  6. Narinder
    Member

    Hi
    I have implemented all email in cocos2d using the discussion above. Everything is working fine but i am facing one problem. My game is in landscape mode but mail composer view presented using the above code is in portrait mode. Can any one tell me what i need to do to make it landscape. I have tried CGAffineTransformMakeRotation on picker but it is not working.

    Posted 1 year ago #
  7. Narinder
    Member

    I resolved the problem by extending the UIViewController and by setting
    return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight); in UIViewController

    Mail Composer is now in landscape but i am facing another problem when i press cancel or send button whole screen turns pink. and logs the following message
    cocos2d: Resizing surface from 320.000000x480.000000 to 480.000000x320.000000

    Posted 1 year ago #
  8. CJ
    Moderator

    There is a fundamental problem with the way cocos2d is handling rotation.

    Apple expects you to use a viewcontroller to contain your view hiearchy. Adding this in is a simple fix, but making cocos2d work correctly with it is not.

    Having a view controller allows you to respond to which orientations you will support and when rotated it will do the following:
    1. Set your view controller's interfaceOrientation property
    2. Rotate your view controller's view by modifying the transform property inside of a UIView animation.

    Cocos2d operates under the assumption that you do not want to rotate your view (because on older devices this causes a performance penalty, though on newer hardware like 3gs and ipad there is no such penalty) so it offers you a way to rotate your openGL transform matrix instead by telling the director what orientation to use.

    The problem with this approach is that if you actually want to start cooperating with other things that read the interfaceOrientation property from your viewcontroller (which is readonly btw, and is only updated by private Apple apis during rotation) then cocos2d (and every other iphone gles game framework I could find) doesn't handle it properly.

    Here's a high level of what needs to happen to get everything in order:
    1. Use a custom UIViewController which uses EAGLView for its view
    2. Intercept attempts to rotate the view and reset the transform to the identity matrix.
    3. Apply your rotation in openGL based on the viewcontroller's interfaceOrientation property
    4. Converting coordinates should always use the viewcontroller's interfaceOrientation property and not the device's orientation.

    I have this all working (I think, though not 100% tested) in my current project, but I won't have time to make a patch for cocos2d for a while because I'm in the final stages of game development and I'm not using CCDirector anymore as I have written my own game loop/UIView+openGL coordinating code.

    If someone else would like to work on this, feel free to ping me for input.

    [Edit:] Note that I have updated the already open and accepted issue #13 with this information:
    http://code.google.com/p/cocos2d-iphone/issues/detail?id=13

    Posted 1 year ago #
  9. Narinder
    Member

    Hi All
    I am able to display the mail in landscape using the code at the following link
    http://codaset.com/gaminghorror/free-cocos2d-iphone-source-code-collection/source
    and adding the following code in my game scene

    SendMail *sendMail = [[SendMail alloc] init];
    sendMail.isHTMLBody = YES;
    [sendMail displayMailComposer];
    [[[CCDirector sharedDirector] openGLView] addSubview:sendMail.view];
    [sendMail presentModalViewController:sendMail animated:YES];
    [sendMail release];

    everything is working so far.

    Posted 1 year ago #
  10. srikanth
    Member

    In my application I used as in the above way. But, I could not dismiss the mail controller by touching on the send or cancel buttons ? How can I do it ?

    -(void)goToFirstScreen:(id)sender
    {
        NSLog(@"goToFirstScreen: ");
        CCScene *Scene = [CCScene node];
        CCLayer *Layer = [EmailScene node];
        [Scene addChild:Layer];
    
        [[CCDirector sharedDirector] setAnimationInterval:1.0/60];
        [[CCDirector sharedDirector] pushScene: Scene];
    }
    Th EmailScene class is
    
    #import "EmailScene.h"
    #import "testOfEnd.h"
    
    @implementation EmailScene
    
    - (id) init {
        self = [super init];
        if (self != nil) {
            [self displayComposerSheet];
        }
        return self;
    }
    
    -(void)displayComposerSheet
    {
        [[CCDirector sharedDirector] pause];
    
         picker = [[MFMailComposeViewController alloc] init];
         picker.mailComposeDelegate = self;
    
        [[[CCDirector sharedDirector] openGLView] addSubview:picker.view];
        [[CCDirector sharedDirector] stopAnimation];
        [picker presentModalViewController:picker animated:YES];
        [picker release];
     }   
    
    - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    {
    
        [[CCDirector sharedDirector] resume];
        //dismiss view after otherwise the code is not executed
        [picker.view removeFromSuperview];
         [[CCDirector sharedDirector] startAnimation];
        [picker dismissModalViewControllerAnimated:YES];
    
        //return to previous scene
        CCScene *Scene = [CCScene node];
        CCLayer *Layer = [testOfEnd node];
        [Scene addChild:Layer];
        [[CCDirector sharedDirector] replaceScene:Scene];
    }

    @end
    Sorry I could not format code. How can I do it ?
    Thank you.

    Posted 1 year ago #
  11. Codemattic
    Moderator

    put a backtick before and after:
    `

    your
     code
      goes
       here

    `
    (I also edited your post)

    Posted 1 year ago #
  12. srikanth
    Member

    I used the same code for iPad also. But, now I am getting problem at [picker presentModalViewController:picker animated:YES];
    The control is not getting out from it.

    Posted 1 year ago #
  13. Kukosk
    Member

    heyyyy finally. i've got it working ... it seems like the trouble was some animations ... ... i've got it like this now:

    on init:

    emailController = [[UIViewController alloc] init];
    [[[CCDirector sharedDirector] openGLView] addSubview:emailController.view];

    on button click:

    [[CCDirector sharedDirector] pause];
    [[CCDirector sharedDirector] stopAnimation];
    
    MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
    picker.mailComposeDelegate = self;
    
    [picker setSubject:@"TEST"];
    [picker setMessageBody:@"JAJAJA" isHTML:YES];
    
    [emailController presentModalViewController:picker animated:YES];
    [picker release];

    delegate method for MFMailComposeViewController

    - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    {
    	[[CCDirector sharedDirector] resume];
    	[[CCDirector sharedDirector] startAnimation];
    
    	[controller dismissModalViewControllerAnimated:NO];
    }
    Posted 1 year ago #
  14. Kukosk
    Member

    aaaaaaaaaaaarGH!!! and now i have realized that when i replace scene, it doesn't respond to touches !!! AH

    Posted 1 year ago #
  15. cybergreg
    Member

    A tip for cocos2d 0.99.5 users, just add the in-app email code to your RootViewController and put a call in your app delegate. This way you can take the code from almost any tutorial (google search) that uses the standard UIKit code, drop it in and go. In addition if you are using your RootViewController for rotation then pretty much everything you put in it will behave and rotate properly.

    Posted 1 year ago #
  16. Kukosk
    Member

    thank you ... it's working now !!! can i release my game with the current version of cocos2d ? (i mean 0.99.5beta), or is it a bad idea ? thank ya

    Posted 1 year ago #
  17. Ravenmann
    Member

    Hi All

    Struggeling with the same problem i got finally the solution.
    I use cocos2d version 0.99 and iphone sdk 3.1.3. So thats my environment.

    So here is the simple and fine solution:

    -(void)displayComposerSheet
    {
    
    	emailController = [[UIViewController alloc] init];
    
    	picker = [[MFMailComposeViewController alloc] init];
    
    	picker.mailComposeDelegate = self;
    
    	[emailController setView:[[CCDirector sharedDirector] openGLView]];
    
    	[[CCDirector sharedDirector] pause];
    
        [emailController presentModalViewController:picker animated:YES];
    
        [picker release];
    }

    And to get back to the previous scene i use this:

    - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    {
    
    	[picker dismissModalViewControllerAnimated:YES];
    
    	[[CCDirector sharedDirector] resume];
        //return to previous scene
    	[[CCDirector sharedDirector] replaceScene:[MenuScene scene]];
    }

    For completness here is the dealloc:

    - (void) dealloc
    {
    
    	[emailController release];
    	// don't forget to call "super dealloc"
    	[super dealloc];
    }

    I hope that helps some.
    The final tip comes from RippRapp.

    Good Luck and thanks for such a good engine.

    Posted 1 year ago #
  18. akki2010
    Member

    Hi,

    I am also trying to have an option of emailing something from inside the game. I was able to get the email interface to come but I am not able to get the screen back to the game screen once the user has pressed send or cancel. The code is as follows @Ravenmann I did the same way as you were doing but can't get back to the previous screen. My code is as follows:-

    -(void) sendMail
    {
    	newView = [[UIViewController alloc] init];
    
    	mailController = [[MFMailComposeViewController alloc] init];
    	mailController.delegate = self;
    
    	[newView setView:[[CCDirector sharedDirector] openGLView]];
    
    	[[CCDirector sharedDirector] pause];
    
    	[newView presentModalViewController:mailController animated:YES];
    
    }
    
    -(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    {
    	/*if (result == MFMailComposeResultSent)
    	{
    		NSLog(@"It's away!");
    	}*/
    
    	[mailController dismissModalViewControllerAnimated:YES];
    
    	[[CCDirector sharedDirector] resume];
    
    	[[CCDirector sharedDirector] replaceScene:[Screensaver scene]];
    }

    It will be really if someone could tell me what I am doing wrong.

    Posted 1 year ago #
  19. miiifan
    Member

    What if you just pop the scene instead of replacing it?

    - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    {
    
    	[mailController dismissModalViewControllerAnimated:YES];
    	[[CCDirector sharedDirector] popScene];
    	[[CCDirector sharedDirector] resume];
    
    }
    Posted 1 year ago #
  20. neilkal
    Member

    cybergreg, I actually used your root controller code for the iAd and tossed the email stuff in there. If I just display a composer from the app delegate everything is fine. But forgive my lack of knowledge, but how would I call up the composer sheet from one of my app's scenes?

    Posted 1 year ago #
  21. cybergreg
    Member

    @neilkal - I wrote a new blog post with a sample project attached:
    http://meachware.blogspot.com/2010/10/taking-advantage-of-rootviewcontroller.html

    Posted 1 year ago #
  22. gangcil
    Member

    @cybergreg: thanks for you nice blog and sample code.........it really works for me...

    Posted 1 year ago #
  23. Mochibits
    Member

    @cybergreg: thanks for the blog post. works great.

    Posted 11 months ago #
  24. eviljack
    Member

    cybergreg you sir, rock.

    Posted 11 months ago #
  25. SUPERMANDT
    Member

    This works perfect for me:

    -(void)displayComposerSheet
    {

    [[CCDirector sharedDirector] pause];
    [[CCDirector sharedDirector] stopAnimation];

    MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
    picker.mailComposeDelegate = self;
    [picker setSubject:@"T-Rex Math Stomper Report"];
    [picker setMessageBody:@"results will go here" isHTML:YES];
    CGAffineTransform landscapeTransform = CGAffineTransformMakeRotation( 1.570796327 ); // 90 degrees in radian
    [picker.view setTransform:landscapeTransform];

    [emailController presentModalViewController:picker animated:YES];
    [picker release];

    }
    - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
    {
    [[CCDirector sharedDirector] resume];
    [[CCDirector sharedDirector] startAnimation];
    [controller dismissModalViewControllerAnimated:NO];

    }

    http://www.DashyApps.com

    Posted 6 months ago #
  26. sascsedu
    Member

    I have done everything that are described here. But still my view opens with portrait view in Landscape mode. I am not getting my problem. Can anyone help me in this regard?

    Here is my Code. DO i need to change anything more. I have linked MessageUI framework in my project.

    -(void) mailCallback: (id) sender {
        //self = [super init];
        NSLog(@"I am in mail callback");
        [self displayComposerSheet];
    
    }
    
    -(void) displayComposerSheet
    {
        NSLog(@"Now I am in the ComposeSheet funtion");
        NSArray *toRecipients = [NSArray arrayWithObject:@"sajeebcsedu@gmail.com"];
        [[CCDirector sharedDirector] pause];
        [[CCDirector sharedDirector] stopAnimation];
        emailController = [[UIViewController alloc] init];
        [emailController setView:[[CCDirector sharedDirector] openGLView]];
        picker =[[MFMailComposeViewController alloc] init];
        picker.mailComposeDelegate=self;
        [picker setToRecipients:toRecipients];
        [picker setSubject:@"TEST"];
        [picker setMessageBody:@"JAJAJA" isHTML:YES];
    
        [emailController presentModalViewController:picker animated:YES];
        [picker release];
    }
    
    -(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
    {
        [[CCDirector sharedDirector] resume];
        [[CCDirector sharedDirector] startAnimation];
    
        [[CCDirector sharedDirector] replaceScene: [CCTransitionFadeDown transitionWithDuration:1.0f scene:[HelloWorldLayer node]]];
        [controller dismissModalViewControllerAnimated:NO];
    }
    Posted 4 months ago #

RSS feed for this topic

Reply

You must log in to post.

cocos2d for iPhone is proudly powered by bbPress.