messageComposeViewController didFinishWithResult delegate method not being called - ios

I have a view controller "Paircontroller" that presents an MFMessageComposeViewController, as so:
NSArray *recpts = [[NSArray alloc]initWithObjects:phone.text, nil];
MFMessageComposeViewController *mcontr = [[MFMessageComposeViewController alloc]init];
mcontr.body = #"Sign up for our app!";
mcontr.recipients = recpts;
mcontr.subject = #"hey!";
mcontr.delegate = self;
[self presentViewController:mcontr animated:YES completion:^{
}];
this view controller's interface looks as follows:
#interface PairViewController : UIViewController<UITextFieldDelegate,CustomIOS7AlertViewDelegate, UINavigationControllerDelegate, MFMessageComposeViewControllerDelegate>
#end
Within the view-controller's implementation, I have defined the delegate method - (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
as follows:
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
switch (result) {
case MessageComposeResultCancelled:
NSLog(#"Cancelled");
break;
case MessageComposeResultFailed:
NSLog(#"unknown error sending m");
break;
case MessageComposeResultSent:
NSLog(#"Message sent successfully");
break;
default:
break;
}
[self dismissViewControllerAnimated:YES completion:^{}];
}
However, the delegate method is not being called (breakpoint not hit, and NSLogs not being hit either).
Can someone help me out on why this is not working?
Thanks!
C

I think the following change should work. MFMessageComposeViewController is a UINavigationController subclass, so in your original code you are setting UINavigationControllerDelegate to self.
mcontr.messageComposeDelegate = self;

Related

How do I check present viewController or root viewController?

I have set up my class with UITabBarDelegate and implemented its method didSelectItem to detect when a certain tabBar item is pressed. Works great. In each tabBar item I have one containerView that can show a "you have to login"-page if the user is not logged in, and another containerView that present viewControllers that are embedded in a navigationController.
I would like to keep track of the viewController that is presented in the current tab item, and/or the root viewController of that tab.
I have tried a number of different approaches, but most of them return nil or I can't get it to work. I think the whole container situation makes it harder to handle.
It looks something like this:
#interface MyTabBarController () <UITabBarDelegate>
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
NSUInteger indexOfTab = [[tabBar items] indexOfObject:item];
switch (indexOfTab) {
case 0: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
break;
}
case 1: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
break;
}
case 2: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
//These return nil
NSLog(#"🐳AAAAAA %#", ((UINavigationController*)_appD.window.rootViewController).visibleViewController);
NSLog(#"🐳AAAAAA %#", ((UITabBarController*)_appD.window.rootViewController).selectedViewController);
NSLog(#"🐳AAAAAA %#", self.navigationController.topViewController);
NSLog(#"🐳AAAAAA %#", self.navigationController.visibleViewController);
//This returns with a value, but can't get it to work with conditionals, that is, when I'm in root, the else is triggered
NSLog(#"🐳AAAAAA %#", self.tabBar.window.rootViewController);
if(!self.tabBar.window.rootViewController) {
NSLog(#"🐳🐳🐳THIS IS NOT ROOT🐳🐳🐳");
}else {
NSLog(#"🐳🐳🐳this is ROOT🐳🐳🐳");
}
// This returns nil
((UINavigationController*)_appD.window.rootViewController).visibleViewController;
((UITabBarController*)_appD.window.rootViewController).selectedViewController;
//Doesn't work
if([self.navigationController.viewControllers[0] isKindOfClass:[ExperiencesListViewController class]]) {
NSLog(#"🐳IS KIND OF CLASS LIST");
}
if([self.navigationController.viewControllers[0].childViewControllers isKindOfClass:[ExperiencesContainerViewController class]]) {
NSLog(#"🐳IS KIND OF CLASS CONTAINER");
}
break;
}
case 3: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
break;
}
case 4: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
break;
}
default:
break;
}
}
So, what else can I try? Seems like I have to use `self.tabBar.window.rootViewController` in some way, no?
***EDIT***
Oh, and I have tried the `tabBarController` delegate but that doesn't trigger. Also, the `tabBar` is constructed programmatically if that helps.
Sorry to have not read your question correctly. Here's what I suggest you do.
All of these view controllers that you're interested in keeping track of: you should have them send a custom notification from within their -viewDidAppear: (or -viewWillAppear:) method. Then let your ApolloTabBarController object register for that notification. When it gets the notification, you could then store a reference to the view controller. That reference will always point to the active view controller.
In your individual view controllers, do something like the following:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"XYZViewControllerDidBecomeActiveNotification"
object:self];
}
Of course, you would want to use some kind of constant for the notification name.
In your ApolloTabBarController class, register for XYZViewControllerDidBecomeActiveNotification and implement something like:
- (void)viewControllerDidBecomeActive:(NSNotification *)notification
{
self.activeViewController = [notification object];
}
I hope that helps!
When you're setting up each view controller for each of the tabs, set the tag property of the UITabBarItem to correspond to the index of the view controller in the tab bar's viewControllers array.
UIViewController* myFirstVC = [[UIViewController alloc] init];
UIViewController* mySecondVC = [[UIViewController alloc] init];
// "self" is your ApolloTabBarController.
[self setViewControllers:#[myFirstVC, mySecondVC]];
myFirstVC.tabBarItem =
[[UITabBarItem alloc] initWithTitle:#"First" image:nil tag:0];
mySecondVC.tabBarItem =
[[UITabBarItem alloc] initWithTitle:#"Second" image:nil tag:1];
Then, you'll be able to grab a reference to view controller.
// In your example, your ApolloTabBarController acts as its own delegate.
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
UIViewController* activeVC =
[[self viewControllers] objectAtIndex:[item tag]];
}

how to pass a variable value to another view properly

i have two views MainView and MainInvoicing
from MainView i am sending a int type variable value to MainInvoicing
this is my code
in MainInvoicing.h file i declared int type var
#property (nonatomic, assign) int myChoice;
in MainView.m file on a button click i am calling the MainInvoicing as
MainInvoicing *invoicing = [[MainInvoicing alloc] initWithNibName:#"Invoicing" bundle:nil];
[self presentViewController:invoicing animated:YES completion:nil];
invoicing.myChoice = 1;
from this my MainInvoicing is called perfectly, but myChoice is equal to zero '0'. while it should be '1'
i am receiving this value in MainInvoicing.m as
- (void)viewDidLoad
{
[super viewDidLoad];
[self Start];
}
and the start method is
- (void) Start
{
switch (myChoice)
{
case 1:
NSLog(#"value is %d",myChoice);
break;
case 2:
NSLog(#"value is %d",myChoice);
break;
default:
NSLog(#"Oooopppss...%d",myChoice);
break;
}
}
i am always on default part ….
where am i wrong or any suggestion to get the right value … please help…
You should assign value before you present view controller:
MainInvoicing *invoicing = [[MainInvoicing alloc] initWithNibName:#"Invoicing" bundle:nil];
invoicing.myChoice = 1;
[self presentViewController:invoicing animated:YES completion:nil];
As Greg said in his comment, it's all about order of code. If you look at your code you should see the order in which it is called.
MainInvoicing *invoicing = [[MainInvoicing alloc] initWithNibName:#"Invoicing" bundle:nil];
[self presentViewController:invoicing animated:YES completion:nil];
[self Start];
switch (myChoice)
{
case 1:
NSLog(#"value is %d",myChoice);
break;
case 2:
NSLog(#"value is %d",myChoice);
break;
default:
NSLog(#"Oooopppss...%d",myChoice);
break;
}
invoicing.myChoice = 1;
So you are trying to access the myChoice variable before you set it, which is why you are getting 0 instead of 1.
With all of that said, there is a possibility of your code working as it is written since the viewDidLoad function is not always called serially. But you should not expect it to always work and you should initialize variables before you call code that could use them immediately.
Accept Greg's answer, I was just adding a bit of explanation to the problem.

skscene UIViewController send SMS from SKScene

I am trying to allow the player to share their score by SMS when the game is over.
I have imported the framework in to my project. Imported in the my viewController.h file.
here is my viewController.h file
#import <UIKit/UIKit.h>
#import <SpriteKit/SpriteKit.h>
#import <MessageUI/MessageUI.h>
#interface myViewController : UIViewController <MFMessageComposeViewControllerDelegate> {
}
#end
I also tried to import into MyScene.h like so:
#import <MessageUI/MessageUI.h>
#interface MyScene : SKScene <MFMessageComposeViewControllerDelegate> {
}
When I want to show the SMS share, I use this code in my MyScene.m file
MFMessageComposeViewController *textComposer = [[MFMessageComposeViewController alloc] init];
[textComposer setMessageComposeDelegate:self];
if ([MFMessageComposeViewController canSendText]) {
[textComposer setRecipients:[NSArray arrayWithObject:nil]];
[textComposer setBody:#"Happy Happy Joy Joy!"];
[self presentViewController:textComposer animated:YES completion:NULL];
} else {
NSLog(#"Cant send text!");
}
But on this line
[self presentViewController:textComposer animated:YES completion:NULL];
I get an "No visible #interface for 'MyScene' declares the selector 'presentViewController:animated:completion:'" error.
I have tried to search for the last couple hours. Try god knows how many variations and examples from other posts/tutorials(which was good to learn a few things unrelated to this). Nothing seems the work. I starting to run out of hair to pull out. So any help would be great. I am sure for some of you Gurus here this should be a walk in the park. Thanks.
EDIT: I am not using storyboard, or the view controller for buttons/menu/game play etc...hence why I am not able to call the function from within the viewController itself.
EDIT:
So I tried what Paulw11 suggested in his link. Now I have the following errors.
in myViewController
MyScene.MyViewController = self;
I get a "Property 'MyViewController' not found on object of type 'MyScene'" error
also in MyScene.m
- (void)sendToController
{
NSLog(#"ok");
// use the already-created spriteViewController
[_MyViewController sendSMS];
}
[_MyViewController sendSMS]; line I get an "No visible #interface for 'MyViewController' declares the selector 'SendSMS'"
EDIT 2: *EDIT 2:* EDIT 2: *EDIT 2:*
I got it to open up the SMS. Small problem, it does not allow me to dismiss it /cancel.
Here is my sendSMS code:
-(void) sendSMS {
MFMessageComposeViewController *textComposer = [[MFMessageComposeViewController alloc] init];
[textComposer setMessageComposeDelegate:self];
if ([MFMessageComposeViewController canSendText]) {
[textComposer setRecipients:[NSArray arrayWithObject:#" "]];
NSString *body = [NSString stringWithFormat:#"Happy Day!: %i. ", _score];
[textComposer setBody:body];
UIViewController *vc = self.view.window.rootViewController;
[vc presentViewController: textComposer animated: YES completion:nil];
} else {
NSLog(#"Cant send text!");
}
}
Here is my dismiss code:
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
UIViewController *vc = self.view.window.rootViewController;
[vc dismissViewControllerAnimated:YES completion:NULL];
}
EDIT 3
The following code gives me the NSLog at the correct times, but does not dismiss the window.
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller
didFinishWithResult:(MessageComposeResult)result
{
UIViewController *vc = self.view.window.rootViewController;
// Notifies users about errors associated with the interface
switch (result)
{
case MessageComposeResultCancelled:
NSLog(#"Result: SMS sending canceled");
break;
case MessageComposeResultSent:
NSLog(#"Result: SMS sent");
break;
case MessageComposeResultFailed:
NSLog(#"Result: SMS sending failed");
break;
default:
NSLog(#"Result: SMS not sent");
break;
}
[vc dismissViewControllerAnimated:YES completion:NULL];
}
If you refer to the MFMessageComposeViewController Class Reference you will see that you need to present it modally using presentModalViewController:animated:. You are also responsible for dismissing it via your delegate object once you are done.
I suggest you have a look at the Message Composer sample code for an example of using the MFMessageComposeViewController class.
UPDATE
You can just dismiss the view controller that was passed to your delegate method -
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller
didFinishWithResult:(MessageComposeResult)result
{
// Notifies users about errors associated with the interface
switch (result)
{
case MessageComposeResultCancelled:
NSLog(#"Result: SMS sending canceled");
break;
case MessageComposeResultSent:
NSLog(#"Result: SMS sent");
break;
case MessageComposeResultFailed:
NSLog(#"Result: SMS sending failed");
break;
default:
NSLog(#"Result: SMS not sent");
break;
}
[controller dismissViewControllerAnimated:YES completion:NULL];
}

How to properly subclassing a UIActionSheet

I want subclass a UIActionSheet to use a block approach instead of delegate.
My problem is when I call the super initialization on UIActionSheet the variadic ... at the end of the method aren't recognized as a va_list and the action sheet only show the first button.
Here class implementation .m
#interface FLActionSheet ()
#property (nonatomic,strong) actionClickedBlock clickedBlock;
#end
#implementation FLActionSheet
+ (id)actionSheetWithTitle:(NSString *)title
clickedBlock:(actionClickedBlock)clickedBlock
cancelButtonTitle:(NSString *)cancelButtonTitle
destructiveButtonTitle:(NSString *)destructiveButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...
{
return [[self alloc] initWithTitle:title
clickedBlock:clickedBlock
cancelButtonTitle:cancelButtonTitle
destructiveButtonTitle:destructiveButtonTitle
otherButtonTitles:otherButtonTitles];
}
- (id)initWithTitle:(NSString *)title
clickedBlock:(actionClickedBlock)clickedBlock
cancelButtonTitle:(NSString *)cancelButtonTitle
destructiveButtonTitle:(NSString *)destructiveButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...
{
self = [super initWithTitle:title
delegate:self
cancelButtonTitle:cancelButtonTitle
destructiveButtonTitle:destructiveButtonTitle
otherButtonTitles:otherButtonTitles,nil];
if (self)
{
self.clickedBlock = [clickedBlock copy];
}
return self;
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
self.clickedBlock(buttonIndex);
}
#end
And here how I initialize the action sheet:
[[[FLActionSheet alloc] initWithTitle:#"Ordina per..."
clickedBlock:^(NSInteger buttonIndex) {
switch (buttonIndex)
{
case 0:
break;
default:
break;
}
}
cancelButtonTitle:nil
destructiveButtonTitle:#"Annulla"
otherButtonTitles:#"Data crescente", #"Data decrescente", #"Mittente crescente", #"Mittente decrescente"]
showFromBarButtonItem:myBarButtonItem
animated:YES];
And here the result:
I'm definitely doing something wrong but I do not understand what.
Ideas ?
UIActionSheet is not designed to be subclassed.
There are other ways how to use it with blocks. Best is to create a category, that implements delegate protocol and store the block using associated ojects mechanism. Implementation here.
I think initWithTitle:delegate:cancelButtonTitle:... is not the designated initializer and it is implemented using [self init] with following setTitle: and addButtonWithTitle: calls. You should do it similarily.
In fact, with variadic method, you have no other option. To collect all arguments, you have to use va_list and related functions. Implementation here. Then on each of them you call addButtonWithTitle: as I mentioned.
Please pass nil at the last of your otherButtonTitles parameter.
[[FLActionSheet alloc] initWithTitle:#"Ordina per..."
clickedBlock:^(NSInteger buttonIndex) {
switch (buttonIndex)
{
case 0:
break;
default:
break;
}
}
cancelButtonTitle:nil
destructiveButtonTitle:#"Annulla"
otherButtonTitles:#"Data crescente", #"Data decrescente", #"Mittente crescente", #"Mittente decrescente", nil]
showFromBarButtonItem:myBarButtonItem
animated:YES];

Need help dismissing email composer screen in iOS

I have an app that allows the user to send a test email from their iPhone. My app calls a method to activate the compose mail function like this:
-(void)displayComposerSheet
{
//set up a way to cancel the email here
//picker is an instance of MSMailComposeViewController already declared in the .h file
[picker setSubject:#"Test Mail"];
// Set up recipients
// Attach an image to the email
NSString *path = [[NSBundle mainBundle] pathForResource:#"Icon" ofType:#"png"];
NSData *myData = [NSData dataWithContentsOfFile:path];
[picker addAttachmentData:myData mimeType:#"image/png" fileName:#"Icon"];
// Fill out the email body text
NSString *emailBody = #"This is a test mail.";
[picker setMessageBody:emailBody isHTML:NO];
[self presentModalViewController:picker animated:YES];
NSLog(#"mail is working");
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
emailLabel.hidden = NO;
// Notifies users about errors associated with the interface
switch (result)
{
case MFMailComposeResultCancelled:
emailLabel.text = #"Mail sending canceled.";
break;
case MFMailComposeResultSaved:
emailLabel.text = #"Mail saved.";
break;
case MFMailComposeResultSent:
{
emailLabel.text = #"Mail sent.";
NSLog(#"It's away!");
UIAlertView *emailAlertView = [[UIAlertView alloc] initWithTitle:#"Sent!" message:#"Mail sent successfully." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[emailAlertView show];
[self dismissModalViewControllerAnimated:YES];
[self.navigationController popViewControllerAnimated:YES];
}
break;
case MFMailComposeResultFailed:
{
emailLabel.text = #"Mail sending failed.";
}
break;
default:
{
emailLabel.text = #"Mail not sent.";
}
break;
}
}
My problem is that when the compose email function is active, I am unable to come out of this function and return to my app. The only way out of this is by actually going ahead and sending a message. There is a default "cancel" bar button that appears on the top left hand corner of the navigation bar, which when clicked, gives me three options: "delete draft", "save draft", and "cancel". If I select "delete draft", it does nothing except to return me to the compose message screen. Is there a way for me to allow the user to return to the app after starting the compose mail function, but prior to sending it? Is there a way to add extra functionality to the "cancel" bar button to accomplish this?
Thanks in advance to all who reply.
You have to implement MFMessageComposeViewControllerDelegate with the - (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result method.
You will dismiss your message view in this method.
EDIT : i confused with MFMailComposeViewControllerDelegate but the purpose is the same as with MFMessageComposeViewControllerDelegate
Take a look at your own code in your ...didFinishWithResult: method:
case MFMailComposeResultCancelled:
emailLabel.text = #"Mail sending canceled.";
break;
case MFMailComposeResultSaved:
emailLabel.text = #"Mail saved.";
break;
case MFMailComposeResultSent:
{
emailLabel.text = #"Mail sent.";
NSLog(#"It's away!");
UIAlertView *emailAlertView = [[UIAlertView alloc] initWithTitle:#"Sent!" message:#"Mail sent successfully." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[emailAlertView show];
[self dismissModalViewControllerAnimated:YES];
[self.navigationController popViewControllerAnimated:YES];
}
When the result is MFMailComposeResultSent you're dismissing the modal view controller and popping the nav stack, which causes the mail compose view controller to go away, and also pops the stack to remove the view controller that presented the compose view controller. When the result is MFMailComposeResultCancelled, however, you just set some label's text. Same for MFMailComposeResultSaved. You need to dismiss the message compose view controller for these cases too if you want the compose view controller to go away when the user cancels or saves.
You should add this method in your view controller
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
[self becomeFirstResponder];
[self dismissViewControllerAnimated:YES completion:nil];
}
hope this one helps some one.
Set Delegate of MFMailComposeViewController
MFMailComposeViewController *mailcomposer = [[MFMailComposeViewController alloc]init];
mailcomposer.mailComposeDelegate = self;
And Use this Delegate Method
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error

Resources