Cancel and send button missing on MFMailComposeViewController iOS - ios

I am working on an iOS app where user can email and send sms.I am using MFMailComposeViewController & MFMessageComposeViewController for sending email and SMS but sometimes Cancel and Send button appear over composer and sometime it doesn't appear at all. This issue is random and I have tried alot to figure out. Any help will be greatly appreciated.
Code
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
mail.mailComposeDelegate = self;
[mail setSubject:Email_Subject];
[mail setMessageBody:Share_Message isHTML:NO];
[mail setToRecipients:#[#""]];
[self presentViewController:mail animated:YES completion:NULL];
I have implemented MFMailComposeViewControllerDelegate in my viewController in header file.

OK, I've got some traction on this finally. My problem stemmed in part from me changing nav bar colors in my app delegate (Swift):
UINavigationBar.appearance().barTintColor = UIColor.black
UIApplication.shared.statusBarStyle = .lightContent
// To get my hamburger menu white colored
UINavigationBar.appearance().tintColor = UIColor.white
Due to this, and some interaction with the Zoomed mode (see my comment above), sometimes I'd get the Cancel/Send buttons set to white color in my mail controller. Like this:
I'll show you my class (Objective C) that displays the mail controller, which now works for me to solve the problem in this question. Note that the line:
[UINavigationBar appearance].tintColor = appleBlueTintColor;
is really the secret sauce in all of this:
#import <MessageUI/MessageUI.h>
#interface SendEmail : MFMailComposeViewController
// Returns nil if can't send email on this device. Displays an alert before returning in this case.
- (id) initWithParentViewController: (UIViewController *) parent;
// Show the email composer.
- (void) show;
// Message to append to the bottom of support emails. The string doesn't contain HTML.
+ (NSString *) getVersionDetailsFor: (NSString *) appName;
#end
#interface SendEmail ()<MFMailComposeViewControllerDelegate>
#property (nonatomic, weak) UIViewController *myParent;
#property (nonatomic, strong) UIColor *previousBarTintColor;
#property (nonatomic, strong) UIColor *previousTintColor;
#end
#import "SendEmail.h"
#import <sys/utsname.h>
#implementation SendEmail
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
// Using this init method depends on the `show` method being called immediately after-- to reset the nav bar colors back to what they were originally.
- (id) initWithParentViewController: (UIViewController *) theParent {
if (![MFMailComposeViewController canSendMail]) {
NSString *title = #"Sorry, the app isn't allowed to send email.";
NSString *message = #"Have you configured this device to send email?";
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil]];
[theParent presentViewController:alert animated:YES completion:nil];
return nil;
}
// The default app nav bar color is black-- and that looks bad on the mail VC. Need to change and then set back both the barTintColor and the tintColor (in the `show` method).
self.previousBarTintColor = [UINavigationBar appearance].barTintColor;
self.previousTintColor = [UINavigationBar appearance].tintColor;
[[UINavigationBar appearance] setBarTintColor:[UIColor whiteColor]];
// I got this color from the digital color meter-- this is what it is supposed to be on the Cancel/Send buttons.
UIColor *appleBlueTintColor = [UIColor colorWithRed:31.0/255.0 green:134.0/255.0 blue:254.0/255.0 alpha:1.0];
[UINavigationBar appearance].tintColor = appleBlueTintColor;
self = [super init];
if (self) {
self.mailComposeDelegate = self;
self.myParent = theParent;
}
return self;
}
- (void) show {
[self.myParent presentViewController:self animated:YES completion:^{
[[UINavigationBar appearance] setBarTintColor: self.previousBarTintColor];
[UINavigationBar appearance].tintColor = self.previousTintColor;
}];
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
[self.myParent dismissViewControllerAnimated:YES completion:nil];
}
+ (NSString *) getVersionDetailsFor: (NSString *) appName;
{
NSMutableString *message = [NSMutableString string];
[message appendString:#"\n\n\n--------------\n"];
[message appendFormat:#"iOS version: %#\n", [[UIDevice currentDevice] systemVersion]];
[message appendFormat:#"%# version: %# (%#)\n", appName,
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleShortVersionString"],
[[NSBundle mainBundle]objectForInfoDictionaryKey:#"CFBundleVersion"]];
[message appendFormat:#"Hardware: %#\n", [self machineName]];
return message;
}
// See http://stackoverflow.com/questions/11197509/ios-iphone-get-device-model-and-make
+ (NSString *) machineName;
{
struct utsname systemInfo;
uname(&systemInfo);
return [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
}
#end

My problem was the below line. Just comment this line as it makes the Barbutton title colour Clear.
UIBarButtonItem.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.clear], for: .normal)
//comment this line

Related

Why does NavgationItem reference disappears?

I created a NavigationBar and added it to the UIViewController. But after init, the reference turns to nil. I'm new to iOS and OC, I don't know why. Anyone can help? Thank you.
code summary:
#interface ContainerViewController()
#property (nonatomic, retain) UINavigationBar *nav;
#property (nonatomic, retain) UINavigationItem *navItem;
#end
#implementation ContainerViewController
- (instancetype) initWithParams:(NSDictionary *)params {
self = [super init];
if (self) {//...}
return self;
}
- setNavTitle:(NSDictionary *) params {
NSString *title = params[#"title"];
/////////////////////////////////
// here goes wrong
// self.navItem == nil here, why?
/////////////////////////////////
self.navItem.title = title;
}
- (void) viewWillAppear:(Bool)animated {
[super viewWillAppear:NO];
static float navHeight = 64.0;
UIViewController *wvController = [WebView init here];
UINavigationBar *nav = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), navHeight)];
UINavigationItem *navItem = [[UINavigationItem alloc] initWithTitle:title];
nav.items = [NSArray arrayWithObjects: navItem, nil];
///////////////////////////////
// I saved the reference here
//////////////////////////////
[self setNav:nav];
[self setNavItem:navItem];
[self.view addSubview:nav];
[self addChildViewController:wvController];
wvController.view.bounds = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - navHeight);
[self.view addSubview:wvController.view];
[wvController didMoveToParentViewController:self];
}
#end
This will be useful for you, kindly check and do
Tutorial point site is very easy to learn some important UI basics if you are working in Objective C

iOS Objective-C block callback issue

I am trying to use pure code to create UI practice block pass value between viewController. But the callback block didn't work. The NSLog method didn't print anything on debug area. Here's the code. Give me some tips, thank you.
VC.h
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
#property (copy, nonatomic) void (^callBack)(NSString *text);
#end
VC.m
- (UITextField *)textField {
if (!_textField) {
_textField = [[UITextField alloc] init];
_textField.backgroundColor = [UIColor whiteColor];
}
return _textField;
}
- (UIButton *)button {
if (!_button) {
_button = [[UIButton alloc] init];
_button.backgroundColor = [UIColor blueColor];
[_button addTarget:self action:#selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _button;
}
- (void)setupUI {
[self.view addSubview:self.textField];
[self.view addSubview:self.button];
[self.textField mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(200);
make.height.mas_equalTo(50);
make.centerX.mas_equalTo(self.view.mas_centerX);
make.centerY.mas_equalTo(self.view);
}];
[self.button mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(200);
make.height.mas_equalTo(50);
make.centerX.mas_equalTo(self.view);
make.centerY.mas_equalTo(self.view).offset(100);
}];
}
- (void)buttonAction {
NSString *str = self.textField.text;
if (self.callBack != nil) {
self.callBack(str);
NSLog(#"This statement didnt print in log");
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor redColor];
}
update code
VC2.m
- (void)viewWillAppear:(BOOL)animated{
self.callBack = ^(NSString *text){
};
}
- (void)buttonAction {
if (self.callBack) {
NSLog(#"It worked on debug area %#", self.textField.text);
self.callBack(self.textField.text);
}
self.textField.text = #"";
}
VC1.m
- (void)viewDidLoad {
[super viewDidLoad];
_secondVc = [[SecondViewController alloc] init];
_secondVc.callBack = ^(NSString *str){
};
[self setupUI];
self.view.backgroundColor = [UIColor greenColor];
}
- (void)viewWillAppear:(BOOL)animated {
if (_secondVc.callBack != nil) {
NSLog(#"It wrked on debug screen");
_secondVc.callBack = ^(NSString *str){
NSLog(#"It didn't worked on debug screen");
//I want set my label.text = str;
};
};
}
The only way is that you property
#property (copy, nonatomic) void (^callBack)(NSString *text);
is empty. Try to put breakpoint in buttonAction method and look at the property.
As Sander and KrishnaCA mentioned your callBack is nil. I would suggest you create a definition of the block like this:
typedef void(^TextBlock)(NSString *text);
Then change your property to:
#property (copy, nonatomic) TextBlock callBack;
Create a copy of the block in your first view controller:
#interface FirstViewController()
#property (copy, nonatomic) TextBlock firstViewControllerCallBack;
#end
Initialize the callback copy (i.e. in viewDidLoad)
- (void)viewDidLoad {
[super viewDidLoad];
self.firstViewControllerCallBack = ^(NSString *text){
NSLog(#"Second view controller's button tapped!");
};
}
Assign the callback to the second view controller right before presenting/pushing it:
SecondViewController *secondVC = [[SecondViewController alloc] init];
secondVC.callBack = self.firstViewControllerCallBack; // Assign the callback
// ... Presenting the view controller
Clean up the completion block after you done with it (i.e. in viewWillDisappear):
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.firstViewControllerCallBack = nil;
}

iOS7: selector on UIBarButtonItem not executing the method

I am iOS newbie. My app has multiple view controllers, so I abstracted the code as
NavigationBar.h
#interface NavigationBar : NSObject
- (void) title: (NSString *) title withViewController: (UIViewController *) viewController;
#end
NavigationBar.m
#import "NavigationBar.h"
#import "PennyNavigationController.h"
#implementation NavigationBar
- (void)title:(NSString *)title withViewController:(UIViewController *)viewController {
viewController.title = title;
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Menu"
style:UIBarButtonItemStyleBordered
target:(PennyNavigationController *) viewController.navigationController
action:#selector(showMenu)];
viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[self getImageNamed:#"Add" withDimension:30] style:UIBarButtonItemStylePlain target:self action:#selector(sayHello:)];
}
- (IBAction)sayHello:(id)sender
{
NSLog(#"add new transaction");
}
- (void)btnClicked:(id)sender {
// NSLog(#"add new transaction");
}
When the app starts, and I click on Add image, I don't see the log, additionally I see that the app closes.
What am I doing wrong?
P.S: Seems I am doing exactly mentioned in Add a custom selector to a UIBarButtonItem, but still it doesn't work

if my "button" is selected on view controller 1, then image should then display and stay on view controller 2

If the "button/checkbox" I have created below is selected by the user on view controller 1, then I would like an image to (appear) display on view controller 2.
if i leave view controller 2 screen and come back to view controller 2 screen, the image should still display as long as the button is still selected on view controller 1.
if the "button" is no longer selected on view controller 1, then the image should no longer be displayed on view controller 2.
Please note that the "button/checkbox" mentioned is being created by the code below. Please help, thank you.
(IBAction)checkButton2:(id)sender {
if (!checked2) {
[_checkBoxButton2 setImage:[UIImage imageNamed:#"checkBoxMarked.png"] forState:UIControlStateNormal];
checked2 = YES;
// alert placeholder
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Locked" message:#"this is locked." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:#"Cancel", nil];
[alert show];
[_checkBoxButton2 setImage:[UIImage imageNamed:#"checkBox.png"] forState:UIControlStateNormal];
checked2 = NO;
}
else if (checked2) {
[_checkBoxButton2 setImage:[UIImage imageNamed:#"checkBox.png"] forState:UIControlStateNormal];
checked2 = NO;
// alert placeholder
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Character selection:" message:#"The box is no longer selected." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:#"Cancel", nil];
[alert show];
}
}
You should Embed you View into a navigation controller.
When you press Next Screen, perform a segue:
- (IBAction)nextBtn:(id)sender {
[self performSegueWithIdentifier:#"segue" sender:self];
}
before you segue, set a BOOL flag to determine if it should display or not:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"step2Segue"]){
ImageViewController *controller = (ImageViewController *)segue.destinationViewController;
controller.displayImage = checked2;
}
}
Finaly in the ImageViewController add a condition based on the Bool to determine if you should display it or not:
in .h
#interface ImageViewController : ViewController
#property (strong, nonatomic) IBOutlet UIImageView *image;
#property (nonatomic, assign) BOOL displayImage;
#end
In .m
-(void)viewDidLoad{
if (self.displayImage) {
self.image.hidden = YES;
}else{
self.image.hidden = NO;
}
}
One thing at a time.
In the ViewController1 class, you'll have to have an instance of ViewController2 created and made strong in your .h (if you're not using Swift).
#property (nonatomic, strong) ViewController2 *viewController2;
Make sure it's instantiated at the time that you press the button. Within the .h for ViewController 2, you'll have to do something similar with a UIImage and UIImageView or whatever you use to display the picture
#property (nonatomic, strong) UIImage *myImage;
#property (nonatomic, strong) UIImageView *myImageView;
Then, in ViewController1, you'll have to control the events that happen in ViewController2 when you press the button. I would use a BOOL or something else simple to keep track of when the image is supposed to be visible.
BOOL imageShouldBeThere = false;
- (void) myButtonWasPressed: (id) sender
{
if(imageShouldBeThere == false)
{
[viewController2Instance showTheImage];
imageShouldBeThere = true;
}
else
{
[viewController2Instance hideTheImage];
imageShouldBeThere = false;
}
}
This is obviously just a rudimentary template, but it should do the trick.
Working code:
FirstViewController.m
- (IBAction)buttonClicked:(id)sender {
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"second"];
secondViewController.shouldShowImage = YES;
[self presentViewController:secondViewController animated:YES completion:^{
}];
}
SecondViewController.h
#property (nonatomic, assign) BOOL shouldShowImage;
SecondViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.shouldShowImage)
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
[imageView setImage:[UIImage imageNamed:#"9gag.png"]];
[self.view addSubview:imageView];
}
else
{
//dont show image....
}
}

UIPageViewController setViewController causing app to crash

I have a - (void)setViewControllers:(NSArray *)viewControllers direction:(UIPageViewControllerNavigationDirection)direction animated:(BOOL)animated completion:(void (^)(BOOL finished))completion set up in a method. Whenever the method is called it doesn't go to the next page. Instead, it goes to the UIPageViewController storyboard and then crashes. I'm not sure what I'm doing wrong. I am using MSPageViewController for the pageviewcontroller, could that be it?
Heres my code:
UIViewController *viewcont = [[UIViewController alloc]init];
NSArray *viewControllers = [NSArray arrayWithObject:viewcont];
[self setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
Thanks.
There are 3 storyboards all conforming to MSPageViewControllerChild with the pageIndex property synthesized. IntroPageViewController is the first storyboard (p1).
PagingViewController.h:
//
// PagingViewController.m
// MordechaiLevi
//
// Created by Mordechai Levi on 4/10/14.
// Copyright (c) 2014 Mordechai Levi. All rights reserved.
//
#import "PagingViewController.h"
#import "IntroPageViewController.h"
#import "MSPageViewController.h"
#interface PagingViewController ()
#end
#implementation PagingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.device = [UIDevice currentDevice];
self.device.proximityMonitoringEnabled = YES;
if (self.device.proximityMonitoringEnabled == YES) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(sensorCovered) name:#"UIDeviceProximityStateDidChangeNotification" object:nil];
}else{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Uh Oh!" message:#"To use this app you need a device with a proximity sensor." delegate:self cancelButtonTitle:#"Got it" otherButtonTitles:nil, nil];
[alert show];
}
self.view.backgroundColor = [UIColor colorWithRed:0.2 green:0.859 blue:0.643 alpha:1.0];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
- (void)sensorCovered {
if (self.device.proximityState == YES) {
IntroPageViewController *viewcont = [[IntroPageViewController alloc]init];
NSArray *viewControllers = [NSArray arrayWithObject:viewcont];
[self setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
NSLog(#"sensor covered");
}else{
NSLog(#"sensor not covered");
}
}
- (NSArray *)pageIdentifiers {
return #[#"p1", #"p2", #"p3"];
}
#end
Looks like you're using MSPageViewController, with a controller that doesn't conform to MSPageViewControllerChild.
From the documentation:
Each of them [controllers] must be a class that conforms to MSPageViewControllerChild (if you don't need to add any extra functionality to it you can use MSPageViewControllerPage).

Resources