MFMailComposeViewController in landscape - mfmailcomposeviewcontroller

My application is in landscape mod.When i call MFMailComposeViewController through Present model view, it comes in landscape mod.I rotate device and MFMailComposeViewController view goes to portrait mod i want to restrict this rotation it should always be in landscape mod only.Is there any way to do it..

Subclass the MFMailComposeViewController class, so that you can override its shouldAutorotateToInterfaceOrientation to display it however you like:
#interface MailCompose : MFMailComposeViewController {
}
#end
#implementation MailCompose
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
#end
Specify the new class in place of MFMailComposeViewController:
MailCompose *controller = [[MailCompose alloc] init];
controller.mailComposeDelegate = self;
[controller setSubject:#"In app email..."];
[controller setMessageBody:#"...email body." isHTML:NO];
[self presentModalViewController:controller animated:YES];
[controller release];

I found that a simple category for MFMailComposeViewController also worked. Since I like my app to rotate to any angle, I created & linked in MFMailComposeViewController+Rotate.h:
#import <Foundation/Foundation.h>
#import <MessageUI/MFMailComposeViewController.h>
#interface MFMailComposeViewController (Rotate)
#end
and MFMailComposeViewController+Rotate.m
#import "MFMailComposeViewController+Rotate.h"
#implementation MFMailComposeViewController (Rotate)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
// return the desired orientation mask from http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIApplication_Class/Reference/Reference.html
return /*mask*/;
}
#end
For my base line testing purposes (iOS 3.1.3), I don't seem to need to put it into the root view controller.
It is worth noting that BEFORE I did this, after my app would load MFMailComposeViewController, it would then stop rotating to any other orientation, even after MFMailComposeViewController was dismissed! Now my app remains freely rotatable.
Henry

This worked for me, perfectly, with one minor change:
MailCompose *controller = [[MailCompose alloc]
initWithRootViewController:self.navigationController];
...
And I was convinced that I had to call the base-class initWithRootViewController method. Otherwise, how would the MFMailComposeViewController know about it? But it turns out the example above that simply calls [[MailCompose alloc] init] is enough. The underlying MFMailComposeViewController "just knows" how to display itself.
I love happy surprises like this. I posted this in case it is equally illuminating for anyone else.

Create a new controller and inherit it from MFMailComposeViewController.In this controller just write one function shouldautorotate thing. Create instance of this.Now it will be working fine.

Related

How the detail view controller told the main controller about the changes made?

I'm reading the Big Nerd Ranch book and I'm at the 10th chapter about the navigation controller.
In the main controller, there is a TableView ( https://github.com/bignerdranch/iOS3eSolutions/blob/master/11.%20Homepwner/Homepwner/Homepwner/ItemsViewController.m ) where there are two methods that interact with the detail view :
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[self tableView] reloadData];
}
- (void)tableView:(UITableView *)aTableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *detailViewController = [[DetailViewController alloc] init];
NSArray *items = [[BNRItemStore sharedStore] allItems];
BNRItem *selectedItem = [items objectAtIndex:[indexPath row]];
// Give detail view controller a pointer to the item object in row
[detailViewController setItem:selectedItem];
// Push it onto the top of the navigation controller's stack
[[self navigationController] pushViewController:detailViewController
animated:YES];
}
In the detail view controller (https://github.com/bignerdranch/iOS3eSolutions/blob/master/11.%20Homepwner/Homepwner/Homepwner/DetailViewController.m), there is a method that "saves" the BNRItem being changed :
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Clear first responder
[[self view] endEditing:YES];
// "Save" changes to item
[item setItemName:[nameField text]];
[item setSerialNumber:[serialNumberField text]];
[item setValueInDollars:[[valueField text] intValue]];
}
The code works well :
I understand how the main controller set the object to edit but I don't understand how the main view controller knows that the BNRItem has been changed and then set it back to the tableview ?
I was excepting the author to write a setter in the main controller (ItemsViewController.m ) that could be called by the detail view controller (DetailViewController.m) giving the new BNRItem.
But this part works "automatically".
Thank you.
As you are pointing to the same memory chunk in both controllers, changed made in the DetailViewController are obviously reported in ItemsViewController.m.

Passing ManagedObjectContext to view controllers using storyboards with a root UITabBarController

Using storyboards you have no easy access to the first view controller in appDelegate (though once you do prepareForSegue makes it easy to pass the ManagedObjectContext down the navigation stack.
I've settled on giving each view controller (or superclass of each view controller) requiring Core Data access a moc member:
#synthesize moc = _moc;
#property (nonatomic) __weak NSManagedObjectContext *moc;
I'm uneasy about it because it doesn't seem a very elegant way to do it - too much code. But assigning directly requires specifying absolute indexes into the viewControllers arrays and changing appDelegate every time the requirement for ManagedObjectContexts change
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
// rootView gets a tab bar controller
for(UINavigationController *navController in tabBarController.viewControllers) {
for(UIViewController *viewController in navController.viewControllers) {
if([viewController respondsToSelector:#selector(setMoc:)]) {
[viewController performSelector:#selector(setMoc:) withObject:self.managedObjectContext];
NSLog(#"Passed moc to %#", [viewController description]);
}
}
}
return YES;
}
What are the pitfalls of this approach and is there a better way? Is it better to try and be more generic:
- (void)assignManagedObjectContextIfResponds:(UIViewController *)viewController {
if([viewController respondsToSelector:#selector(setMoc:)]) {
[viewController performSelector:#selector(setMoc:) withObject:self.managedObjectContext];
NSLog(#"Passed moc to %#", [viewController description]);
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSMutableArray *viewControllers = [NSMutableArray array];
UIViewController *firstLevelViewController = self.window.rootViewController;
if([firstLevelViewController respondsToSelector:#selector(viewControllers)]) {
NSArray *firstLevelViewControllers = [firstLevelViewController performSelector:#selector(viewControllers)];
for(UIViewController *secondLevelViewController in firstLevelViewControllers) {
if([secondLevelViewController respondsToSelector:#selector(viewControllers)]) {
NSArray *secondLevelViewControllers = [secondLevelViewController performSelector:#selector(viewControllers)];
for(UIViewController *thirdLevelViewController in secondLevelViewControllers) {
[viewControllers addObject:thirdLevelViewController];
}
} else {
[viewControllers addObject:secondLevelViewController];
}
}
} else {
// this is the simple case, just one view controller as root
[viewControllers addObject:firstLevelViewController];
}
// iterate over all the collected top-level view controllers and assign moc to them if they respond
for(UIViewController *viewController in viewControllers) {
[self assignManagedObjectContextIfResponds:viewController];
}
return YES;
}
Don't know if I understood properly, but why don't you left the managed object context directly in AppDelegate class and leave there all the logic for instantiate. And from then you can ask for it.
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
then you can recall it anytime from anywhere.
NSManagedObjectContext *moc = [(YourApplicationDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
For convenience I declared a define for it:
#define MOC [(YourApplicationDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext]
Therefore this become:
[MOC save:&error];
You can take this everywhere you like.
Just try to have a look at the auto generated code for a CoreData application in Xcode, you will see that many accessors with CoreData are in there, and the CoreData itself is lazily initialized at first request.
Adam,
Whilst I was exploring storyboards I pretty much did it the same way you did except I made each of my view controllers that had a MOC property conform to a protocol.
There's nothing significantly different there, so I'll move on.
I think the point is Storyboards, IMO, are half-baked. Coming from a .Net background what is obviously missing is an object builder framework coupled with an IoC container.
When Apple add that Storyboards will be awesome. When the storyboard framework can look at the destinationViewController, determine it's dependencies and resolve those from a container life will be great. For now, all it can really do is look at the destinationViewController and init you a generic one, which is of limited use.
Unfortunately, because it's a half-baked solution I'm sticking with the traditional approach for now so all my view controllers are alloc'd and init'd manually and more importantly I've added a method to each view controller to initWithMOC:(MOC *)moc;
The architect in me is telling me this code is more robust, I guess it's a matter of opinion as to whether it's worth the trade-off.
Anyone else come up with a better way?
CA.

IOS Modal View Controller Presents Black Screen

I am working on an IOS app and have been stumped by this problem for about a week now and cannot find a solution. Any help you can offer would be greatly appreciated. Here's my set-up:
I have a tab bar controller.
I have a TableViewController which has a navigation bar with a navigation item "Add".
After you press the "Add" selector I am modally presenting another viewController that has a picker on it.
I am using Core Data.
When the second view controller is modally presented it comes up with a black screen with a navigation bar. If I access the second view controller from an unrelated screen modally it comes up fine without the navigation bar.
No error message is logged, not even that the object wasn't saved when you press the "save" on the navigation bar. However, pressing "save" will bring you back to the TableViewController and it looks like the entity was added.
Here is the code in my TableViewController:
- (void)add:(id)sender {
SecondViewController *addController = [[SecondViewController alloc] init];
addController.delegate = self;
Entity *newEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Entity" inManagedObjectContext:self.managedObjectContext];
addController.entity = newEntity;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addController];
[self.navigationController presentModalViewController:navController animated:YES];
}
- (void)secondViewController:(SecondViewController *)secondViewController didAddEntity:(Entity *)entity {
if (entity) {
[self showEntity:entity animated:NO];
}
[self dismissModalViewControllerAnimated:YES];
}
- (void)showEntity:(Entity *)entity animated:(BOOL)animated {
EntityDetailTableViewController *detailViewController = [[EntityDetailTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
detailViewController.entity = entity;
[self.navigationController pushViewController:detailViewController animated:animated];
}
Here is the code in my second View Controller:
- (void) save {
entity.attribute = attributeTextField.text;
NSError *error = nil;
if (![entity.managedObjectContext save:&error])
{
NSLog(#"Problem saving attribute: %#", [error localizedDescription]);
}
NSLog(#"saveAttribute");
[self.delegate secondViewController:self didAddEntity:entity];
}
Any suggestions on where to go from here would be really helpful.
After much frustration I found the answer. If you are using storyboard you cannot navigate to the next view controller by the standard code. I put the prepareForSegue statement in my TableViewController.m file and then hooked up the connection on storyboard and identified the segue.
Now, when you press the Add button it segues to the new view controller screen and it's not black.
Here's a link to a useful tutorial
My guess is the way you are initializing is not right:
SecondViewController *addController = [[SecondViewController alloc] init];
should be initWithNIB:

Manually add more tabs to UITabController with Storyboard and Monotouch

I am currently using the new iOS 5 Storyboard approach to creating my Tabbed Application with Monotouch. I have developed two of my tab views in Xcode with Storyboard and linked them appropriately to the Tab Bar Controller. I also want to develop (in Xcode) a third tab view that would be shared among two additional tabs. I want to reuse the same layout, but display different data depending on which tab is selected (think something like a "Popular" and a "Recent" that would have the same layout but different data).
To do this, I figured I could add the tab manually twice after the Storyboard-driven tabs are added. How do I do this with the Storyboard approach? I'm not sure where in the code to do this since the loading of the Storyboard seems pretty transparent (i.e. no code in AppDelegate that I see). Or, is there another (easier/better) way to share a view between two tabs using the Storyboard approach?
I don't know Monotouch, but here's how I did it in Objective-c. I didn't find anything about this topic, so if something is wrong, people please comment :) By the way, I'm using ARC, so I don't manually manage memory! What I needed to achieve was like you, having a tab bar, loading the same viewController, but loading different data for each tab.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *root = (UITabBarController*)self.window.rootViewController;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
TeamViewController *home = [[mainStoryboard instantiateViewControllerWithIdentifier:#"Team"] initHome];
TeamViewController *visitor = [[mainStoryboard instantiateViewControllerWithIdentifier:#"Team"] initVisitor];
[root setViewControllers:[NSArray arrayWithObjects:home, visitor, nil] animated:NO];
UITabBar *tabs = root.tabBar;
UITabBarItem *homeTab = [tabs.items objectAtIndex:0];
UITabBarItem *visitorTab = [tabs.items objectAtIndex:1];
homeTab.title = #"Home team";
visitorTab.title = #"Visitor team";
return YES;
}
You can see I call initHome and initVisitor when I load my two TeamViewController, here is the code about it.
TeamViewController.h
#interface TeamViewController : UIViewController
{
enum
{
HOME,
VISITOR
};
int team;
}
TeamViewController.m
- (id)initHome
{
team = HOME;
return self;
}
- (id)initVisitor
{
team = VISITOR;
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if(team == HOME)
{
label.text = #"home data";
}
else if(team == VISITOR)
{
label.text = #"visitor data";
}
}
I don't know how well you can translate that to your project, but I hope you get the big picture of it :)
If you need to read a bit about how to access the first view controller using the storyboard: http://developer.apple.com/library/ios/#releasenotes/Miscellaneous/RN-AdoptingStoryboards/_index.html#//apple_ref/doc/uid/TP40011297
There is a section called "Accessing the First View Controller"

UIWebView display problem

I followed this tutorial http://bytedissident.theconspiracy5.com/2010/11/24/uiwebview-tutorial/ and when I run the simulation in Xcode, all that is displayed is a blank white screen. I'm wondering what could be wrong. Is there some sort of code connection to the internet that i'm missing? I really don't know what could be wrong.
WebPageViewController.m
#import "WebPageViewController.h"
#implementation WebPageViewController
#synthesize wView;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//a URL string
NSString *urlAddress = #"http://www.nd.edu";
//create URL object from string
NSURL *url = [NSURL URLWithString:urlAddress];
//URL Request Object created from your URL object
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//Load the request in the UIWebView
[wView loadRequest:requestObj];
//scale page to the device - can also be done in IB if preferred.
//wView.scalesPageToFit = YES;
}
Did you connect the .xib to the WebPageViewController class you created? You can check by selecting the .xib in the editor, and use identity inspector on 'File's Owner' in the Class field under Custom Class

Resources