Mail Composer won't dismiss iOS - ios

I have ViewController with button, and action on button:
- (IBAction)clickMe:(id)sender {
MailHelper *helper = [[MailHelper alloc] init];
[helper setAllData:self];
}
Also, there is helper class for mail composing (MailHelper.h):
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#interface MailHelper : UIViewController<MFMailComposeViewControllerDelegate>
#property MFMailComposeViewController* mailView;
- (void)setAllData:(UIViewController *)ctrl;
#end
and implementation (MailHelper.m):
- (void)setAllData:(UIViewController *)ctrl {
mailView = [[MFMailComposeViewController alloc] init];
mailView.mailComposeDelegate = self;
mailView.toRecipients = #[#"mail#email.com"];
[mailView setSubject:#"Subject"];
[ctrl presentViewController:mailView animated:YES completion:nil];
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[self dismissViewControllerAnimated:YES completion:nil];
}
I can open mail composer, but while sending mail, saving draft or deleting draft app crashes. Any ideas?

When the MFMailComposeViewController is request to be dismissed you are calling the dismiss method on self but self is presenting te MFMailComposeViewController.
Change:
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[self dismissViewControllerAnimated:YES completion:nil];
}
to
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[controller dismissViewControllerAnimated:YES completion:nil];
}
To fix the deallocation issue add the following:
import the <objc/runtime.h> in you .m file and make self be associated with the controller passed:
static void * MailHelperKey = &MailHelperKey;
- (void)setAllData:(UIViewController *)ctrl {
mailView = [[MFMailComposeViewController alloc] init];
mailView.mailComposeDelegate = self;
objc_setAssociatedObject(mailView, MailHelperKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
....
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[controller dismissViewControllerAnimated:YES completion:nil];
objc_removeAssociatedObjects(controller);
}
This will make sure that as long a the presenting control is not deallocated your mail helper. Does not deserve on price fro beauty but should work.

Ok guys. Solution is to make instance of helper somewhere else, let's say:
#implementation ViewController
MailHelper *helper;
and:
- (IBAction)clickMe:(id)sender {
helper = [[MailHelper alloc] init];
[helper setAllData:self];
}

Related

Present View Controller not working with ios 9

Present view controller is not working with ios 9,
when I press the button it not redirected to present view controller.
Why this happens ?
I had tried the below code
RegistrationViewController * viewController =[[UIStoryboard storyboardWithName:#"Registration" bundle:nil] instantiateViewControllerWithIdentifier:#"RegistrationViewController"];
[self presentViewController:viewController animated:YES completion:nil];
I had also tried the below code
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Registration"
bundle:nil];
RegistrationViewController *add =
[storyboard instantiateViewControllerWithIdentifier:#"RegistrationViewController"];
[self presentViewController:add animated:YES completion:^{
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:add
animated:YES
completion:nil];
});
}];
Edit
My complete code here. I am using Xcode 7 and running it with ios 9
#import "LoginViewController.h"
#import "RegistrationViewController.h"
#interface LoginViewController ()
#end
#implementation LoginViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)actionRegistrationClicked:(id)sender
{
RegistrationViewController * viewController =[[UIStoryboard storyboardWithName:#"Registration" bundle:nil] instantiateViewControllerWithIdentifier:#"RegistrationViewController"];
[self presentViewController:viewController animated:YES completion:nil];
}
#end
Registration view controller
#import "RegistrationViewController.h"
#interface RegistrationViewController ()
#end
#implementation RegistrationViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Make sure the presentedViewController is nil. If presentedViewController is not nil change the code to following.
RegistrationViewController * viewController =[[UIStoryboard storyboardWithName:#"Registration" bundle:nil] instantiateViewControllerWithIdentifier:#"RegistrationViewController"];
if ([self presentedViewController]) {
[[self presentedViewController] dismissViewControllerAnimated:NO completion:^{
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:viewController animated:YES completion:nil];
});
}];
} else {
[self presentViewController:viewController animated:YES completion:nil];
}
try viewDidAppear in
-(void)viewDidAppear:(BOOL)animated{
[self presentViewController:viewController animated:YES completion:nil];
}
Try the below code:
RegistrationViewController * viewController = [[self.storyboard instantiateViewControllerWithIdentifier:#"RegistrationViewController"];
[self presentViewController:viewController animated:YES completion:nil];

Move from Viewcontroller to another after image take

OK, I am a NOOB with iPhone and have a simple question I assume. I have found a lot of help out there but am not sure what I am doing wrong. I simply want to take a photo with the camera then move to the next view controller after successful capture and place it in an image view. Got most of this code from here so thanks already but can not seem to get it to work, all different kinds of errors I do not understand. I think it must be that I am killing the view controller then trying to re-instantiate it but am a little lost Please Help.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (IBAction)TakePhoto:(id)sender{
picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
[picker setSourceType:UIImagePickerControllerSourceTypeCamera];
[self presentViewController:picker animated:YES completion:NULL];
}
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
image = [info objectForKey:UIImagePickerControllerOriginalImage];
[imageView setImage:image];
[self dismissViewControllerAnimated:YES completion:NULL];
NSString *submit = #"viewControllerSubmit";
UIStoryboard *mainstoryboard = [UIStoryboard storyboardWithName:submit bundle:nil];
UIViewController *vc = [mainstoryboard instantiateViewControllerWithIdentifier:submit];
[self presentViewController:vc animated:YES completion:nil];
}
- (void) imagePickerControllerDidCancel:(UIImagePickerController *)picker{
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
As you can see I have tried a few things. I have tried searching for the errors but tried other things to no avail. Any assistance would be great, thanks in advance.
EDITED VERSION and FULL CODE BELOW HERE
====================================================================
OK, I am posting my full code. I can get the form to go from one to the next as I was simply calling the storyboard name wrong. However I can not get the image I take from the camera to show up on the next form. All I get is an empty ImageView without any errors. What am I missing? Please help been working on this for days and have researched a ton.
First View Controller .M File
#import "ViewController.h"
#import "ViewControllerSubmit.h"
#interface ViewController ()
#end
#implementation ViewController{
UIImage *newImage;
}
- (IBAction)TakePhoto:(id)sender{
picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
[picker setSourceType:UIImagePickerControllerSourceTypeCamera];
[self presentViewController:picker animated:YES completion:nil]; //nil from Null
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"ViewControllerSubmit"]){
ViewControllerSubmit *destViewController = segue.destinationViewController;
destViewController.theNewImage = newImage;
}
}
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
newImage = [info objectForKey:UIImagePickerControllerOriginalImage];
[self dismissViewControllerAnimated:YES completion:NULL];
NSString *submit = #"ViewControllerSubmit";
NSString *main = #"Main";
UIStoryboard *mainstoryboard = [UIStoryboard storyboardWithName:main bundle:nil];
UIViewController *vc = [mainstoryboard instantiateViewControllerWithIdentifier:submit];
[self presentViewController:vc animated:YES completion:nil];
}
- (void) imagePickerControllerDidCancel:(UIImagePickerController *)picker{
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
First View Controller .H File
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>{
UIImagePickerController *picker;
}
#end
Second View Controller .M File
#import "ViewControllerSubmit.h"
#interface ViewControllerSubmit ()
#property (weak, nonatomic) IBOutlet UIImageView *_image;
#end
#implementation ViewControllerSubmit
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self._image.image = self.theNewImage;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Second View Controller .H File
#import "ViewController.h"
#interface ViewControllerSubmit : ViewController
#property (strong, nonatomic) UIImage *theNewImage;
#end
Again, no error, the camera comes up, it takes the picture and when I click 'Use Photo' the second View Controller pops up but the image view does not show the image captured.
What am I missing? Any help would be great and again please forgive the ignorance, I am new to iPhone.
You need to write navigation code in completion block...
Try below code,
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
image = [info objectForKey:UIImagePickerControllerOriginalImage];
[imageView setImage:image];
[picker dismissViewControllerAnimated:YES completion:^{
NSString *submit = #"viewControllerSubmit";
UIStoryboard *mainstoryboard = [UIStoryboard storyboardWithName:submit bundle:nil];
UIViewController *vc = [mainstoryboard instantiateViewControllerWithIdentifier:submit];
[self presentViewController:vc animated:YES completion:nil];
}];
}

How can I start a ViewController from within a Method?

I want to use the TGCameraViewController from within a iOS Cordova Plugin method.
Here is the implementation of the method:
#import "BCamera.h"
#import "TGCameraViewController.h"
#implementation BCamera
// Cordova command method
-(void) openCamera:(CDVInvokedUrlCommand *)command {
// take a photo
}
From the documentation of the TGCameraViewController I have to use the following to take a photo:
#import "TGCameraViewController.h"
#interface TGViewController : UIViewController <TGCameraDelegate>
#property (strong, nonatomic) IBOutlet UIImageView *photoView;
- (IBAction)takePhotoTapped;
#end
#implementation TGViewController
- (IBAction)takePhotoTapped
{
TGCameraNavigationController *navigationController =
[TGCameraNavigationController newWithCameraDelegate:self];
[self presentViewController:navigationController animated:YES completion:nil];
}
#pragma mark - TGCameraDelegate optional
- (void)cameraWillTakePhoto
{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
- (void)cameraDidSavePhotoAtPath:(NSURL *)assetURL
{
// When this method is implemented, an image will be saved on the user's device
NSLog(#"%s album path: %#", __PRETTY_FUNCTION__, assetURL);
}
- (void)cameraDidSavePhotoWithError:(NSError *)error
{
NSLog(#"%s error: %#", __PRETTY_FUNCTION__, error);
}
#pragma mark - TGCameraDelegate required
- (void)cameraDidTakePhoto:(UIImage *)image
{
_photoView.image = image;
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)cameraDidCancel
{
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
How do I use this example to open the camera view from within my openCamera method?
You should present TGCameraNavigationController on rootViewController of your application, e.g.
#interface BCamera() <TGCameraDelegate>
#end
#implementation BCamera
- (void)openCamera:(CDVInvokedUrlCommand *) command
{
TGCameraNavigationController *navigationController = [TGCameraNavigationController newWithCameraDelegate:self];
UIViewController *rootVC = [[UIApplication sharedApplication] delegate] window] rootViewController];
[rootVC presentViewController:navigationController animated:YES completion:nil];
}
Also you must implement TGCameraDelegate protocol methods.
- (void)cameraDidTakePhoto:(UIImage *)image
{
//do something with image
[self dismissCameraVC];
}
- (void)cameraDidCancel
{
[self dismissCameraVC];
}
- (void)dismissCameraVC
{
UIViewController *rootVC = [[UIApplication sharedApplication] delegate] window] rootViewController];
[rootVC dismissViewControllerAnimated:YES completion:nil];
}

IOS switch view after FB login

I use the storyboard, and the first view is FB login. I want to switch view to the Navigation controller after FB login successfully (FB login can work).
I don't know how to switch the view. I even use a button and push, but it is not working.
The FBloginViewController class:
#import "FBloginViewController.h"
#import <FacebookSDK/FacebookSDK.h>
#interface LoginStatusViewController ()
-(void)toggleHiddenState:(BOOL)shouldHide;
#end
#implementation FBLoginViewController
-(void)toggleHiddenState:(BOOL)shouldHide{
self.lblUsername.hidden = shouldHide;
self.lblEmail.hidden = shouldHide;
self.profilePicture.hidden = shouldHide;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)loginView:(FBLoginView *)loginView handleError:(NSError *)error{
NSLog(#"%#", [error localizedDescription]);
}
-(void)loginViewShowingLoggedOutUser:(FBLoginView *)loginView{
self.lblLoginStatus.text = #"You are logged out";
[self toggleHiddenState:YES];
}
-(void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user{
NSLog(#"%#", user);
self.profilePicture.profileID = user.objectID;
self.lblUsername.text = user.name;
self.lblEmail.text = [user objectForKey:#"email"];
}
-(void)loginViewShowingLoggedInUser:(FBLoginView *)loginView{
self.lblLoginStatus.text = #"You are logged in.";
[self toggleHiddenState:NO];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self toggleHiddenState:YES];
self.lblLoginStatus.text = #"";
self.loginButton.readPermissions = #[#"public_profile", #"email"];
self.loginButton.delegate = self;
}
Use this method in your code at the point wherever you want to perform a switch forward to a new Controller.
[self performSegueWithIdentifier:#"yourSegueIdentifierName" sender:nil];
You will have to connect a segue in the Interface Builder or provide a segue programatically in your application.This method will automatically trigger the prepareForSegue method as soon as the above statement is encountered and the next controller will appear.
Hope this helps.
It seems you have to look into how to set up properly the view controllers in the application.
From th question the structure is
App Launch -> FBLogin -> NAv Contoller -> Next Viewcontroller
Change this to
App Launch ->NAv Contoller-> FBLogin ->(PUSH)-> Next Viewcontroller
Means embed the navigation controller in the FBLogin VC and continue the workflow
use method -pushViewController:animated: to move to next page
Replace your methods with this:
-(void)loginViewShowingLoggedInUser:(FBLoginView *)loginView{
self.lblLoginStatus.text = #"You are logged in.";
[self toggleHiddenState:NO];
//if you have anavigation controller then
[self performSegueWithIdentifier:#"yourSegueIdentifierName" sender:nil];
//if you want to open a modal viewController
YOURVIEWCONTROLLER *vc1 = [[YOURVIEWCONTROLLER alloc] initWithNibName:#"YOURVIEWCONTROLLER" bundle:nil];
[vc1 setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentViewController:vc1 animated:YES completion:nil];
}

Unable to dismiss MFMailComposeViewController, delegate not called

I am calling MFMailComposeViewController from a UITableViewController.
Problem is the delegate method is never called when I select Cancel or Send button in Mail compose window:
mailComposeController:(MFMailComposeViewController*)controllerdidFinishWithResult
Here is the table view class:
#implementation DetailsTableViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section==0 && indexPath.row==4) {
//SEND MAIL
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
if ([MFMailComposeViewController canSendMail]) {
[controller setSubject:[NSString stringWithFormat:#"Ref %#",[item objectForKey:#"reference"]]];
[controller setMessageBody:#" " isHTML:NO];
[controller setToRecipients:[NSArray arrayWithObject:[item objectForKey:#"email"]]];
[self presentModalViewController:controller animated:YES];
}
[controller release];
}
}
- (void)mailComposeController:(MFMailComposeViewController*)controllerdidFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
// NEVER REACHES THIS PLACE
[self dismissModalViewControllerAnimated:YES];
NSLog (#"mail finished");
}
The application doesn't crash. After the Cancel or Send button is pressed, the Compose Window stays on the screen with buttons disabled. I can exit application pressing Home key.
I am able to open other Modal Views form TableView but not MailCompose.
Make sure you use
controller.mailComposeDelegate = self;
and not
controller.delegate = self;
Your method signature is incorrect:
- (void)mailComposeController:(MFMailComposeViewController*)controllerdidFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
Should be:
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
Refer this article for full implementation : http://www.ioscreator.com/tutorials/send-email-from-an-app
working code after making removing deprecated one :
#import <MessageUI/MFMailComposeViewController.h>
#interface SettingsTableViewController () <MFMailComposeViewControllerDelegate, UITextFieldDelegate, UITextViewDelegate>
#end
#implementation SettingsTableViewController
// add default methods
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger sectionNum = indexPath.section;
NSInteger rowNum = indexPath.row;
if (sectionNum == 2 && rowNum == 1) {
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
if ([MFMailComposeViewController canSendMail]) {
[controller setSubject:[NSString stringWithFormat:#"Invitation to Northstar app"]];
[controller setMessageBody:#" " isHTML:NO];
// [controller setToRecipients:[NSArray arrayWithObject:[item objectForKey:#"email"]]];
//presentViewController:animated:completion:
[self presentViewController:controller animated:YES completion:NULL];
}
}
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
NSLog (#"mail finished");
[self dismissViewControllerAnimated:YES completion:NULL];
}
#end
I faced the same problem and was searching for a fix from past 2 days then I found a fix myself and you won't believe how minor it was.
In my case the view controller (say 'DetailsTableViewController' as per this question) from where I was presenting the MFMailComposeViewController is already being presented from some other view controller (say 'BaseViewController').
The issue was lying in the 'modalPresentationStyle' of 'DetailsTableViewController' while presenting it from BaseViewController.
The moment I changed it from 'UIModalPresentationFormSheet' to 'UIModalPresentationPageSheet' (for that matter any thing other than 'UIModalPresentationFormSheet') issue got resolved and mail controller delegate methods started firing as usual.
Note: I was already calling the below method in 'DetailsTableViewController' (for this example) so it didn't really matter for me which 'modalPresentationStyle' I was using.
- (void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
self.view.superview.bounds = CGRectMake(0, 0, 1024, 768);
self.view.superview.backgroundColor = [UIColor clearColor];
}

Resources