Push from a tableView to a MFMailComposerViewController - ios

I'm currently developing an app where I have a static tableView with 7 sections. These sections are different courses that the user can apply to and what I want to accomplish is when the user clicks the row / button of a section. It pushes a MailComposerView and inside the mail It says for example,
Hi I would like to apply to course %# which accrues at this date %#. The %# being the selected course and date.
Pleas tell me If I need to add more information / code.
Thanks.

You will need to make use of the didSelectRowAtIndexPath: method of your UITableView. Try something like below:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init];
mailViewController.mailComposeDelegate = self;
[mailViewController setSubject:#"Course Apply"]; //Set the subject here
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; // this gets the currently selected cell.. Hope in your case, it's a custom cell.
[mailViewController setMessageBody:[NSString stringWithFormat:#"Hi I would like to apply to course %# which accrues at this date %#",cell.courseSelected,cell.courseData] isHTML:NO]; //courseSelected and courseDate are properties that hold value in your custom cell
NSArray *toRecipients = [NSArray arrayWithObject:#"your.email#email.com"];
[mailViewController setToRecipients:toRecipients]; // set the recipient address here
[self presentViewController:mailViewController animated:YES completion:nil]; // and finally present it...
}
Hope this helps..

Import <MessageUI/MessageUI.h> and <MessageUI/MFMailComposeViewController.h> into your viewController and add the MFMailComposeViewControllerDelegate to your Interface.
Use this code to show MailComposeView:
MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init];
mailViewController.mailComposeDelegate = self;
[mailViewController setSubject:#"<subject here>"]
[mailViewController setMessageBody:#"" isHTML:NO];
NSArray *toRecipients = [NSArray arrayWithObject:#"your.email#email.com"];
[mailViewController setToRecipients:toRecipients];
[self presentViewController:mailViewController animated:YES completion:nil];
And this to dismiss the MailComposeView:
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
NSLog(#"ERROR: %#", error);
[self dismissViewControllerAnimated:YES completion:nil];
}
If you want to have different body text then put the code in a method which accepts NSString parameter and call that from the button or the selection method of the row and give it the text you want to have in the body.

Related

UITableView has extra space on top during "pull down to refresh"

I have a problem with my tableview.
It has a space on top like this:
When I open the TasksTableViewController, the problem doesn't show. But when I open another viewcontroller from TaskTableVC like this:
FilterTasksTableViewController * fttvc = [self.storyboard instantiateViewControllerWithIdentifier:#"FilterTasksTableViewController"];
fttvc.delegate = self;
UINavigationController * navVC = [self.storyboard instantiateViewControllerWithIdentifier:#"PopoverNavigationController"];
[navVC setViewControllers:#[fttvc]];
[self presentViewController:navVC animated:YES completion:nil];
and go back to TaskTableVC, the problem occurs.
When I "pull down to refesh", it goes back to normal.
in my code for TaskTableVC:
- (void)viewWillAppear:(BOOL)animated {
//other code
[self populate];
}
- (void)viewDidLoad {
dispatch_async(dispatch_get_main_queue(), ^{
self.refreshControl = [[UIRefreshControl alloc] init];
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:#" "];
[self.refreshControl addTarget:self action:#selector(refresh) forControlEvents:UIControlEventValueChanged];
[self setRefreshControl:self.refreshControl];
});
[self populate];
}
- (void)populate {
TaskHandler *handler = [[TaskHandler alloc] initWithContext:_context];
NSArray *allTasks = [handler getAll];
_tasks = [self filterTasks:allTasks];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"startDate" ascending:NO];
NSArray *descriptors = [NSArray arrayWithObjects:descriptor, nil];
_tasks = [[NSMutableArray alloc] initWithArray:[_tasks sortedArrayUsingDescriptors:descriptors]];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
[self.refreshControl endRefreshing];
});
}
I hope you can help me with this weird problem.
I solved my problem with the code.
I thought there was something wrong with the Refresh control so I moved it out of the dispatch_aysnc(dispatch_get_main_queue() and added [self.tableview reloadData . That fixed my problem. Thanks everyone for answering. :)
In view viewWillAppear set .
if ([self respondsToSelector:#selector(edgesForExtendedLayout)]) {
[self setEdgesForExtendedLayout:UIRectEdgeBottom];
}
hope it help you .
You Need to make Sure that your UITableViewCell has correct Height.
For doing that you can simply implement the UITableViewDelegate method :
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 90.0f; //CHANGE TO YOUR DESIRED HEIGHT.
}
I hope this helps.

Pass property from Modal View Controller to Parent

I have a tableview as a parent view controller with a child modal view controller. In the modal view controller, when users tap on a row, I'd like to set the parent's property 'filter.' However, it's just returning null.
How do I pass the NSString filter property back to its parent view? And should I be instantiating a parent view controller in the didSelectRowAtIndexPath method?
UPDATE: Solved using Delegates, followed this tutorial.
Below is the code for the modal view controller:
#import "FilterViewController.h"
#import "ContactsTableViewController.h"
#interface FilterViewController ()
#end
#implementation FilterViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.filterTable.dataSource = self;
self.filterTable.delegate = self;
[self performSelector:#selector(retrieveFilteredEvents)];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.filterEvents count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"filterTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
NSDictionary *tempDict = [self.filterEvents objectAtIndex:indexPath.row];
self.eventTitle = [tempDict objectForKey:#"eventType"];
cell.textLabel.text = self.eventTitle;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *tempDict = [self.filterEvents objectAtIndex:indexPath.row];
NSString *string = [tempDict objectForKey:#"eventType"];
ContactsTableViewController *contactVC = [[ContactsTableViewController alloc] init];
contactVC.filter = string;
[self dismissViewControllerAnimated:YES completion:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateParent" object:nil];
}
#pragma mark - Helper Methods
- (IBAction)done:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)retrieveFilteredEvents
{
PFQuery *retrieveEvents = [PFQuery queryWithClassName:#"eventTypes"];
[retrieveEvents orderByAscending:#"eventOrder"];
[retrieveEvents findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.filterEvents = [[NSArray alloc] initWithArray:objects];
}
[self.filterTable reloadData];
}];
}
#end
There are multiple ways to approach this problem. The easiest I think would be setting a property in your parent class that would change every time you update the child.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *tempDict = [self.filterEvents objectAtIndex:indexPath.row];
NSString *string = [tempDict objectForKey:#"eventType"];
self.parentViewController.filter = string;
[self dismissViewControllerAnimated:YES completion:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateParent" object:nil];
}
The other option would be to use delegates. This is probably a cleaner solution but is not as beginner friendly.
http://iosdevelopertips.com/objective-c/the-basics-of-protocols-and-delegates.html
What you are doing here:
ContactsTableViewController *contactVC = [[ContactsTableViewController alloc] init];
contactVC.filter = string;
is instantiating a whole new controller that has nothing to do with the actual controller that presented your FilterViewController.
Try using this, instead, which accesses the view controller that presented FilterViewController:
ContactsTableViewController *contactVC = (ContactsTableViewController*)self.presentingController;
contactVC.filter = string;
Hope this helps.
Alloc init gives you a brand new object, NOT a reference to your existing one. There are a couple of ways to get a reference to your table view. I recommend delegation, it's a flexible approach that will continue to work even if you present your view in a different way than modal.
create a protocol
#protocol PassSomeData
-(void)childViewController:(UIViewController *)viewController passedSomeData:(id)data;
#end
create a delegate in the childview controller
#interface ViewControllerThatIsPresentedModally :UIViewController
#property(nonatomic,assign) id<PassSomeData>delegate;
#end
in prepareForSegue of the view controller that is presenting the modal write this
UIViewController *vc = [segue destinationViewController];
if ([vc isKindOfClass:[ViewControllerTahIsPresentedModally class]){
[((ViewControllerThatIsPresentedModally *)vc) setDelegate:self];
}
when your user clicks something that allows some data to be collected
you do
[self.delegate viewController:self passedSomeData:yourData];
and your method in the parent view controller will be called with your data
your presenting view controller needs to conform to the PassedSomeData protocol and implement the given method as well
If it's a simple property set, you could do
[self.parentViewController performSelector:#selector(setSomething:) withObject:...]
or
[self.presentingViewController performSelector:#selector(setSomething:) withObject:...]
Delegates is a much better way, though :-)

How to link UITableView Cell to different ViewControllers in Storyboard

I have created a UITableView and set everything up so that it pulls the correct Data that I want on the table. The only issue I have is that when I click on an item, I want it to open that viewController
I have set the Storyboard ID up correctly so everything should work but please see my code below incase there's something obvious I've missed:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
UIViewController *newTopViewController;
if (indexPath.section == 0) {
NSString *identifier = [NSString stringWithFormat:#"%#", [self.section1 objectAtIndex:indexPath.row]];
newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
} else if (indexPath.section == 1) {
NSString *identifier = [NSString stringWithFormat:#"%#", [self.section2 objectAtIndex:indexPath.row]];
newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
}
else if (indexPath.section == 2) {
NSString *identifier = [NSString stringWithFormat:#"%#", [self.section3 objectAtIndex:indexPath.row]];
newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
}
}
I can provide more info if needed
You use instantiateViewControllerWithIdentifier to create view controller objects.
But you are not presenting the created view controller object so add:
[self.navigationController presentViewController:newTopViewController animated:YES completion:nil];
If Identifiers are correct and you set segues in your storyboard then you can just call
[self performSegueWithIdentifier:indentifier sender:nil];
You need to either Push yournewTopViewController
[self.navigationController pushViewController:newTopViewController animated:YES];
Or Modally Present your newTopViewController
[self.navigationController presentViewController:newTopViewController animated:YES completion:nil];
in the end of your didSelectRowAtIndexPath Method.
To answer your question I believe you should first import all your .h files First.
Then you can go
if (indexPath.section == 0) {
FirstViewController *firstController = [[FirstViewController alloc] init];
[self presentViewController:firstController animated:YES completion:nil];
}
And so on and so forth

Is there any way to pass a PFFile (image) to another controller and display it without querying the database in the new controller?

This is a question I have asked over at Parse forums and I have not had any reply from them for the past 3 days. Hence I am posting here.
Hi,
I have a PFQueryTableViewController and the table has custom cells with images and different labels. This Table navigates to another DetailViewController on the selection of a row. The DetailViewController need to display an image in its view, which we acquire from our query to the Parse database in the PFQueryTableViewController. How do I achieve that ?
TABLEVIEWCONTROLLER.M
#interface TableViewController ()
#end
NSMutableArray* detailObjects;
DetObj* clickedObj;
#implementation TableViewController
- (void)objectsDidLoad:(NSError *)error {
[super objectsDidLoad:error];
// This method is called every time objects are loaded from Parse via the PFQuery
for (PFObject* object in self.objects)
{
DetailObj* obj = [[DetailObj alloc ] init ];
[obj setDetName:[object objectForKey:#"detName"]];
[mov setDetImage:[object objectForKey:#"detImage"]];
[detailObjects addObject:obj];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *CellIdentifier = #"upCell";
customBigTableCell *cell = (customBigTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customBigTableCell alloc] init];
}
cell.name.text = [object objectForKey:#"detName"];
cell.image.file = [object objectForKey:#"detImage"];
//this is essential for loading the image from the database
[cell.image loadInBackground];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:[NSBundle mainBundle]];
newDetailController* newController = [storyboard instantiateViewControllerWithIdentifier:#"Detail"];
clickedObj = [[DetObj alloc] init];
NSIndexPath* path = [self.tableView indexPathForSelectedRow];
clickedObj = [detailObjects objectAtIndex:path.row];
[newController setDetInfo:clickedObj];
[[self navigationController] pushViewController:newController animated:YES];
}
I have tried using a PFImageView as stated below in the DetailViewController. detailInfo is the object passed from the previous PFQueryTableViewController.
newDetailController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
PFImageView* detImage = [[PFImageView alloc] initWithFrame:CGRectMake(300,0,320,150)];
PFFile* file = detailInfo.fullImage.file;
detImage.file = file;
[detImage loadInBackground];
[self.view addSubview:detImage];
}
But I have this error.
reason: '-[PFFile file]: unrecognized selector sent to instance
I dont want to query the database again to get the info I have already obtained in the previous controller.
Thanks Andrew
Once you have obtained the PFFile In the PFQueryTableViewController you should save that image into an image cache. It can be stored in the filesystem or in memory. It's best to use NSCache if you put it in memory as it automatically frees up memory when needed. Then in your DetailViewController get the image. You can use the parseId as the image key.
To specifically answer your question, you cannot pass a PFFile as it is not guaranteed to stay in memory or storage. I believe it may actually execute in certain conditions but it is not guaranteed. Each time you need a PFFile, you should get a PFObject by the parseId, then download the PFFile. This counts as two requests. However, Parse is smart enough that it keeps its own cache -- but the lifetime is not specified and not guaranteed. The best thing to do is to use filesystem, memory, or core data to persist the data.
I resorted to a different approach and it works well for me. But Thanks Eddie for telling me about NSCache and its usage.
I store the URL of the PFFile associated with the object, and then pass the URL to the next controller. Then I display the image using the following code in -(void)viewDidLoad()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL: imgURL];
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
self.upImage.image = [UIImage imageWithData:imageData];
});
});
Thanks to this thread Create UIIMAGE with URL in iOS
Thanks again.
Andrew
I did something like this recently.
ParseTableViewController.m
//----- CREATE SUBVIEW HERE -----//
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:nil action:nil];
PFObject *passObj = [self.objects objectAtIndex:indexPath.row];
HouseDataViewController *detailViewController = [[HouseDataViewController alloc] init];
[detailViewController setReceiveObj:passObj];
[self.navigationController pushViewController:detailViewController animated:YES];
}
DetailViewController.h
// include this to receive the object passed from the PFTableViewController
#property (nonatomic, strong) PFObject *ReceiveObj;
and on the DetailViewController.m
-(void)viewDidLoad {
if (self.ReceiveObj){
// House Image
PFImageView *houseView = [[PFImageView alloc] init];
houseView.frame = CGRectMake(border, y, 280, 196);
houseView.image = [UIImage imageNamed:#"big_frame.png"]; // placeholder image
houseView.file = (PFFile *)[detail objectForKey:#"house_picture"];
//[houseView loadInBackground];
[scrollView addSubview:houseView];
[houseView loadInBackground:^(UIImage *image, NSError *error) {
if (!error) {
// do code here
} else {
// ... error
}
}];
}
}

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