I have some authentication view controllers that use my begin navigation controller, like so in AppDelegate.m:
#property (strong, nonatomic, retain) BeginViewController *beginViewController;
#property (strong, nonatomic, retain) BeginNavigationController *beginNavigationController;
...
_beginViewController = [[BeginViewController alloc] init];
_beginNavigationController = [[BeginNavigationController alloc] initWithRootViewController:_beginViewController];
self.window.rootViewController = _beginNavigationController;
[self.window makeKeyAndVisible];
After a certain point, the user will autnenicate themselves and if there are no errors this happens:
ATabBarController *tab = [[ATabBarController alloc] init];
[self.navigationController pushViewController:tab animated:YES];
Then a tab bar controller init's:
#property (nonatomic, strong, retain) InsideNavigationController *nav1;
....
#property (nonatomic, strong, retain) InsideNavigationController *nav4;
_view1 = [[AViewController alloc] init];
...
_view2 = [[DViewController alloc] init];
_nav1 = [[InsideNavigationController alloc]initWithRootViewController:_view1];
...
_nav4 = [[InsideNavigationController alloc]initWithRootViewController:_view4];
Is this the proper way to do things? I wanted to have a navigation controller handle authentication and all unauthenticated views and then a navigation contoller for the tab bar after authenticating.
How do I get back to the first view controller?
Thanks.
Related
So I'm trying to add a viewController to a tabBarController that should force the moreViewController since I have 5 already and I would be adding a 6th. I'm setting the viewControllers property of the tabBarController using an array literal. This seems like something that's really simple, but I can't seem to find anything on stack or in Apple's docs that say it isn't possible to initialize it like this.
Here's the code below and the output of the NSLog's when I was trying to debug the issue. I can't figure out why I can initialize the viewControllers property to be 6 objects instead of 5 like it's showing:
#property (nonatomic, retain) UINavigationController* navVC1;
#property (nonatomic, retain) UINavigationController* navVC2;
#property (nonatomic, retain) UINavigationController* navVC3;
#property (nonatomic, retain) UINavigationController* navVC4;
#property (nonatomic, retain) UINavigationController* navVC5;
#property (nonatomic, retain) UINavigationController* navVC6;
#property (nonatomic, retain) customVC1* vc1;
#property (nonatomic, retain) customVC2* vc2;
#property (nonatomic, retain) customVC3* vc3;
#property (nonatomic, retain) customVC4* vc4;
#property (nonatomic, retain) customVC5* vc5;
#property (nonatomic, retain) customVC6* vc6;
self.vc1 = [customVC1 loadFromNib];
self.vc2 = [customVC2 loadFromNib];
self.vc3 = [customVC3 loadFromNib];
self.vc4 = [customVC4 loadFromNib];
self.vc5 = [customVC5 loadFromNib];
self.vc6 = [customVC6 loadFromNib];
self.navVC1 = [[[CustomNavController alloc] initWithRootViewController:self.vc1] autorelease];
self.navVC2 = [[[CustomNavController alloc] initWithRootViewController:self.vc2] autorelease];
self.navVC3 = [[[CustomNavController alloc] initWithRootViewController:self.vc3] autorelease];
self.navVC4 = [[[CustomNavController alloc] initWithRootViewController:self.vc4] autorelease];
self.navVC5 = [[[CustomNavController alloc] initWithRootViewController:self.vc5] autorelease];
self.navVC6 = [[[CustomNavController alloc] initWithRootViewController:self.vc6] autorelease];
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
self.tabBarController.viewControllers = #[self.navVC1, self.navVC2, self.navVC3, self.navVC4, self.navVC5, self.navVC6];
NSArray* tBArray = #[self.navVC1, self.navVC2, self.navVC3, self.navVC4, self.navVC5, self.navVC6];
NSLog(#"Here's the number of VCs %d",[self.tabBarController.tabBar.items count]); // this outputs 5
NSLog(#"Here's the array count %d",[tBArray count]); // this outputs 6
Here's an excerpt from another method where I style the tabBar:
int index = 0;
UITabBarItem* tb = nil;
tb = [self.tabBarController.tabBar.items objectAtIndex:index++];
[tb setTitle:#"vc1"];
tb = [self.tabBarController.tabBar.items objectAtIndex:index++];
[tb setTitle:#"vc2"];
tb = [self.tabBarController.tabBar.items objectAtIndex:index++];
[tb setTitle:#"vc3"];
tb = [self.tabBarController.tabBar.items objectAtIndex:index++];
[tb setTitle:#"vc4"];
tb = [self.tabBarController.tabBar.items objectAtIndex:index++];
[tb setTitle:#"vc5"];
tb = [self.tabBarController.tabBar.items objectAtIndex:index++]; // this is the out of bounds crash
[tb setTitle:#"vc6"];
So the app is crashing on me and here's the out of bounds error:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 5 beyond bounds [0 .. 4]'
EDIT: Here's the solution:
Thanks for the help everyone. Figured out that can't apply attributes directly to the tabBarItems using objectAtIndex once have more than 5 tabs since it seems the 5th tab once you go over 5 becomes an array of viewControllers which is the moreViewController. I just rewrote the code to apply all attributes directly to the viewControllers before adding them to the tabBar as follows:
UIImage* vc1TabIconSelected = [[UIImage imageNamed:#"tabBarIconVC1Selected.png"]
imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.savedTabViewController.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"VC1"
image:[UIImage imageNamed:#"tabBarIconVC1.png"]
selectedImage:vc1TabIconSelected];
[self.vc1TabViewController.tabBarItem setTitleTextAttributes:#{ UITextAttributeTextColor : [UIColor colorFromHex:#"424242"] } forState:UIControlStateSelected];
Even though there are 6 view controllers in viewControllers, tabBar still only has 5 items.
From the UITabBarController documentation:
You should never access the tab bar view of a tab bar controller directly. To configure the tabs of a tab bar controller, you assign the view controllers that provide the root view for each tab to the viewControllers property.
Set the view controllers' title property; don't access tabBar directly.
UITabBarController can only feature a maximum of 5 tabs. If you assign more than 5 view controllers to viewControllers the tab bar will only shows the first 4 tabs and a 5th tab called More, which shows a table view with rows for each remaining tab. The point here is, in this case, the UITabBarController shows that it has the full number of view controllers, while its UITabBar shows a maximum of 5 items.
[tabBarController.tabBar.items objectAtIndex:5] is what made your app crash.
Not sure why that does not work, but you could also use
[self.tabBarController addChildViewController:viewController];
for each one
Hi everyone I have been building an app and have met some problems.
My app has two viewControllers. One is MenuViewController and another one is MainViewController.
I want to pass a string from MainViewController to a mutableArray in MenuViewController, but have no idea how.
Here are my codes:
<MenuViewController.h>
#import <UIKit/UIKit.h>
#interface MenuViewController : UITableViewController {
NSMutableArray *secondFavourite;
}
#property (nonatomic, strong) NSMutableArray *secondFavourite;
#end
.
<MenuViewController.m>
#import "MenuViewController.h"
#import "MainViewController.h"
#interface MenuViewController ()
#property (strong, nonatomic) NSArray *menu;
#end
#implementation MenuViewController
#synthesize menu;
#synthesize secondFavourite;
- (void)viewDidLoad
{
[super viewDidLoad];
self.secondFavourite = [[NSMutableArray alloc] init];
self.menu = self.secondFavourite;
}
.
<MainViewController.h>
#import <UIKit/UIKit.h>
#import <social/Social.h>
#interface MainViewController : UIViewController {
IBOutlet UIImageView *imagepost;
UILabel *predictionLabel;
}
- (IBAction)sampleSelector:(UIButton *)sender;
- (IBAction)showAllClick:(id)sender;
#property (nonatomic, retain) IBOutlet UILabel *predictionLabel;
#property (strong, nonatomic) NSArray *predictionArray;
#property (strong, nonatomic) UIButton *menuBtn;
#property (strong, nonatomic) NSMutableArray *fav;
#property (strong, nonatomic) IBOutlet UILabel *favLabel;
#property (strong, nonatomic) IBOutlet UITableView* tableView;
#property (nonatomic, strong) NSMutableArray *favourite;
#end
.
<MainViewController.m>
- (void)viewDidLoad
{
[super viewDidLoad];
self.predictionArray = [[NSArray alloc] initWithObjects:#"Hey gurl", nil];
}
//Add to favourite
- (void) addToFav {
self.favourite = [[NSMutableArray alloc] init];
[self.favourite addObject:self.predictionLabel.text];
[self.tableView reloadData];
NSLog(#"%#", self.favourite);
}
//add to favourite button action
- (IBAction)addToFavButton:(id)sender {
[self addToFav];
//pass data from favourite here to secondFacourite in MenuViewController (found on stack overflow)
MenuViewController *menuViewController = [[MenuViewController alloc]initWithNibName:#"MenuViewController" bundle:nil];
menuViewController.secondFavourite = [[NSMutableArray alloc]initWithArray:self.favourite];
[self.navigationController pushViewController:menuViewController animated:YES];
}
I used NSLog to check that the menuViewController.secondFavourite in MainViewController successfully added the string into the array, isn't the array the same array in MenuViewController? Why doesn't the menu.tableView update and show the new string added? I am very confused and I hope someone will help me out.
Thanks for reading this.
This has to do with the fact that your menu viewDidLoad is overwriting the value in these two lines:
self.secondFavourite = [[NSMutableArray alloc] init];
self.menu = self.secondFavourite;
That first line is setting your secondFavourite property to an empty NSMutableArray instance. And since viewDidLoad will be called only after the view has been loaded into memory (in this case, when you try to push the view controller onto the stack), your initial values in the secondFavourite property will be lost.
Instead, you should move that initialization code into the an init method.
I'm having a main view controller containing a UIScrollView called containerScrollView. This scrollview has on each page another scrollview with the size of the screen containing two view controllers: MessagesViewController and InfoViewController. Here's a schema.
The personScrollView in the containerScrollView works fine but the problem is in the adding of the two view controllers' view to the personScrollView.
#property (nonatomic, retain) MessagesViewController *matchesVC;
#property (nonatomic, retain) InfoViewController *standingsVC;
for (int i = 0; i < 3; i++) {
UIScrollView *personScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(i*320, 0, 320, self.containerScrollView.frame.size.height)];
NSArray *colors = #[[UIColor blueColor], [UIColor orangeColor], [UIColor greenColor]];
[personScrollView setBackgroundColor:[y objectAtIndex:i]];
[personScrollView setPagingEnabled:YES];
[personScrollView setContentSize:CGSizeMake(self.view.frame.size.width * 2, personScrollView)];
[self.containerScrollView addSubview:personScrollView];
/* Populate the scrollview */
// Messages
if (self.messagesVC == nil)
{
self.messagesVC = [[MessagesViewController alloc] init];
[self.messagesVC setFrame:CGRectMake(0, 0, 320, self.containerScrollView.frame.size.height)];
}
[personScrollView addSubview:self.messagesVC.view];
// Info
if (self.infoVC == nil)
{
self.infoVC = [[InfoViewController alloc] init];
[self.infoVC setFrame:CGRectMake(320, 0, 320, self.containerScrollView.frame.size.height)];
}
[personScrollView addSubview:self.infoVC.view];
}
[self.containerScrollView setContentSize:CGSizeMake(320*3, self.containerScrollView.frame.size.height)];
The problem is that the two view controllers (messages and info) only get added once, and to the last personScrollView of containerScrollView.
How to get the view controllers added to all of my personScrollViews? Something wrong with the property declaration?
I have read something about this abusing view controllers, but this is the only solution. There is really a lot of code in the two view controllers and I can't add it to my rootviewcontroller.
The problem is with your understanding of the difference between view controllers and views. You need to read up on Creating Custom Container View Controllers.
Apple doc says:
Views can have only one superview. If view already has a superview and that view is not the receiver, this method removes the previous superview before making the receiver its new superview.
You create your controllers once, but you want to add theirs views three times, to three different parent views. You can't do that.
I ended up creating multiple instances of my view controllers and storing them in an array. Not a great solution, but the best I could find.
#property (strong, nonatomic) MessagesViewController *messagesVC1;
#property (strong, nonatomic) MessagesViewController *messagesVC2;
#property (strong, nonatomic) MessagesViewController *messagesVC3;
#property (strong, nonatomic) MessagesViewController *messagesVC4;
#property (strong, nonatomic) MessagesViewController *messagesVC5;
#property (strong, nonatomic) MessagesViewController *messagesVC6;
self.messagesVC1 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC2 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC3 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC4 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC5 = [[MessagesViewController alloc] initWithData:data];
self.messagesVC6 = [[MessagesViewController alloc] initWithData:data];
self.messagesVCArray = #[self.messagesVC1, self.messagesVC2, self.messagesVC3, self.messagesVC4, self.messagesVC5, self.messagesVC6];
MessagesViewController *messagesVC = [self.messagesVCArray objectAtIndex:i];
[messagesVC setFrame:CGRectMake(0, 0, 320, leagueScrollView.frame.size.height)];
[leagueScrollView addSubview:messagesVC.view];
I am trying to pass textfield text from one controller to another controller so I can email it. I am following an example, but I must be missing some little details. Basically the goal is to pass data textfield text from enteryournamecontroller controller to option controller's nsstring. The nsstring's string variable is being returned null.
EnterYourNameController.h
#interface EnterYourNameController : UIViewController{
UITextField *textField;
id <EnterYourNameController> delegate;
}
#property (strong, nonatomic) NSString *stringEntered;
#property (strong, nonatomic) UITextField *textField;
#property (strong, nonatomic) id delegate;
EnterYourNameController.m
-(void)button{
stringEntered=textField.text;
((Options *) (self.delegate)).string = stringEntered;
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
Options *vc = [storyboard instantiateViewControllerWithIdentifier:#"Options"];
[self.navigationController pushViewController:vc animated:YES];
}
options.h
#interface Options : UIViewController <MFMailComposeViewControllerDelegate> {
NSString *string;
}
#property (strong,nonatomic) NSString* string;
#end
option.m
NSString *emailBody = [NSString stringWithFormat:#"%#",string];
In -(void) button method, initialize the Options controller and set the value.
-(void)button{
stringEntered=textField.text;
Options *optViewController = [[Options alloc] init];
optViewController.string = stringEntered;
//call your optViewController here
}
I hope, your string in option VC wouldn't live, when you switch between views. So, you must retain string property like this:
-(void)button
{
Options *optViewController = [[Options alloc] init];
optViewController.string = [[NSString stringWithString:textField.text] retain];
//call your optViewController here
}
I have a subview that is linking to a detail subview. See below. This .h is EXACTLY the same in the two header files I am currently using, except for the #interface _ (JohnAdams on one and GeorgeWashington on the other.
#interface JohnAdams : UIViewController {
NSString *selectedCountry;
IBOutlet UIButton *bio;
IBOutlet UIButton *wiki;
}
#property (nonatomic, retain) NSString *selectedCountry;
#property (nonatomic, retain) UIButton *wiki;
#property (nonatomic, retain) UIButton *bio;
-(IBAction)wikiButtonPressed:(id)sender;
-(IBAction)bioButtonPressed:(id)sender;
#end
However, take a look at the two .m's
//George Washington.m
-(IBAction)wikibuttonPressed:(id)sender {
WebView1 *detailvc = [[WebView1 alloc] initWithNibName:#"WebView1" bundle:nil];
[detailvc setTitle:#"Wikipedia"];
[self.navigationController pushViewController:detailvc animated:YES];
[detailvc release];
}
-(IBAction)biobuttonPressed:(id)sender {
WebView2 *detailvc2 = [[WebView2 alloc] initWithNibName:#"WebView2" bundle:nil];
[detailvc2 setTitle:#"The White House"];
[self.navigationController pushViewController:detailvc2 animated:YES];
[detailvc2 release];
}
//JohnAdams.m
-(IBAction)biobuttonPressed:(id)sender {
WebView4 *detailvc4 = [[WebView4 alloc] initWithNibName:#"WebView4" bundle:nil];
[detailvc4 setTitle:#"The White House"];
[self.navigationController pushViewController:detailvc4 animated:YES];
[detailvc4 release];
}
-(IBAction)wikibuttonPressed:(id)sender {
WebView3 *detailvc3 = [[WebView3 alloc] initWithNibName:#"WebView3" bundle:nil];
[detailvc3 setTitle:#"Wikipedia"];
[self.navigationController pushViewController:detailvc3 animated:YES];
[detailvc3 release];
}
In John Adams.m, rather than loading WebView3 and WebView4, it loads WebView1 and WebView2. Additionally, John Adams.m receives a warning that method definition for both -wikiButtonPressed and bioButtonPressed are undefined (but not on GeorgeWashington.m). What am I doing wrong?
Are you using the interface builder?
(Apparently you do because you are declaring IBOutlets)
If so then check the class name of the files owner of JohnAdams.xib.