This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I am new to iOS, i am using Xcode 4.3.2, and i have created a project with tabView, where by default it has two view controllers. I am creating a new one now,
ThirdView.h
#import <UIKit/UIKit.h>
#interface ThirdView : UIViewController
#end
#import "ThirdView.h"
#implementation ThirdView
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Third", #"Third");
self.tabBarItem.image = [UIImage imageNamed:#"Third"];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
and in the appDelicate.m
viewController3 = [[ThirdView alloc] initWithNibName:#"ThirdView" bundle:nil];
and added viewController3 to tabBarController.
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, viewController3 ,nil];
The Problem is:
When i execute the code in the device, i could see the third tab, but the moment i click, it crashes.
Where i am doing wrong ?
Double check the class name of the third view controller in you nib file.
Related
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.
I am trying to make an application that counts the number of clicks on a button, and plays a sound when the number of clicks is 10 ... I've tried a lot of different code, but it's not working. Any help is appreciated.
This is the .m file
What should I add inside the if statement?
#import "ViewController.h"
#import "AVFoundation/AVAudioPlayer.h"
#interface ViewController ()
#end
#implementation ViewController
-(IBAction)Add{
count++;
number.text =[NSString stringWithFormat:#"%i", count];
if (count==10) {
}
}
-(IBAction)Reset{
count=0;
number.text =[NSString stringWithFormat:#"%i", count];
}
- (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
And this is the .h file
#import <UIKit/UIKit.h>
int count;
#interface ViewController : UIViewController
{
IBOutlet UILabel *number;
}
-(IBAction)Add;
-(IBAction)Reset;
#end
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.
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"
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.