Issue with adding buttons to UINavigationController - ios

So this is how I make the navbar:
- (void)viewDidLoad
{
[super viewDidLoad];
UINavigationController *navBar = [[UINavigationController alloc] init];
[navBar willMoveToParentViewController:self];
navBar.view.frame = CGRectMake(0, 0, 320, 44);
[self.view addSubview:navBar.view];
[self addChildViewController:navBar];
[navBar didMoveToParentViewController:self];
...
And everywhere I have read says that this is how you add buttons:
UIBarButtonItem *button = [[UIBarButtonItem alloc]initWithTitle:#"test" style:UIBarButtonItemStyleBordered target:self action:#selector(print_message:)];
self.navigationItem.rightBarButtonItem = button;
[button release];
But the button does not show on the navbar. What is wrong with this code?

Unless you're building a custom container view controller (which is a relatively rare thing to do), you should not be building a UINavigationController inside your content controller's -viewDidLoad. While it will provide you a navigation bar, your view controller parent-child relationship will be backwards: your content controller will contain the navigation controller, rather than the other way around.
Instead, you need to create the navigation controller earlier in your app's startup process - maybe in your application delegate, or in your main storyboard if you're using one. Make sure that the new navigation controller has your content controller as its root controller (usually by way of -initWithRootViewController:). Then your self.navigationItem configuration will work properly.

You should create your navigationbar probably differently:
In your xxxAppDelegate.m edit this method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
//This is the ViewController of the view you want to be the root
xxxViewController *tvc = [[xxxViewController alloc]init];
//Now you have to initialize a UINavigationController and set its RootViewController
UINavigationController *nvc = [[UINavigationController alloc]initWithRootViewController:tvc];
//Now set the RootViewController to the NavigationViewController
[[self window]setRootViewController:nvc];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
So now you have a proper NavigationController. If you do this in the viewDidLoad method, the NavigationController will be made each time you reload your view.
Now in your xxxViewController.m edit your init method:
- (id)init
{
...
if (self) {
//Create a UINavigationItem
UINavigationItem *n = [self navigationItem];
//Create a new bar button item
UIBarButtonItem *button = [[UIBarButtonItem alloc]initWithTitle:#"test" style:UIBarButtonItemStyleBordered target:self action:#selector(print_message:)];
[[self navigationItem]setRightBarButtonItem:button];
}
return self;
}
This should now display a proper NavigationBar with a UIBarButtonItem.

Related

How to display all tabBar titles in ios7

Basically i cant get to display all tabBar Items when i run my app, just the first view controller is displayed:
I literally have to click on a tab to display its Item:
This my code in Appdelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Initialize window
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
// Set background colors for both NavBar and TabBar
[[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:0.157 green:0.718 blue:0.553 alpha:1]];
[[UITabBar appearance] setBarTintColor:[UIColor colorWithRed:0.141 green:0.216 blue:0.263 alpha:1]];
// Initialize your five tab controllers. with each tab has its own navigation controller
HomePageView *homePageView = [[HomePageView alloc]init];
UINavigationController *nav1 = [[UINavigationController alloc]initWithRootViewController:homePageView];
ProfileViewController *profileViewController=[[ProfileViewController alloc]init];
UINavigationController *nav2 = [[UINavigationController alloc]initWithRootViewController:profileViewController];
FeedViewController *feedViewController=[[FeedViewController alloc]init];
UINavigationController *nav3 = [[UINavigationController alloc]initWithRootViewController:feedViewController];
ListeningSessionViewController *listeningSessionViewController= [[ListeningSessionViewController alloc]init];
UINavigationController *nav4 = [[UINavigationController alloc]initWithRootViewController:listeningSessionViewController];
RecievedViewController *recievedViewController =[[RecievedViewController alloc]init];
UINavigationController *nav5 = [[UINavigationController alloc]initWithRootViewController:recievedViewController];
// initialize tabbarcontroller,set your viewcontrollers and change its color.
self.tabC = [[UITabBarController alloc]init];
NSArray* controllers = [NSArray arrayWithObjects: nav1,nav2,nav3,nav4,nav5, nil];
[self.tabC setViewControllers: controllers animated:NO];
[_window addSubview:self.tabC.view];
// Show window
[self.window makeKeyAndVisible];
return YES;
}
I'm guessing that you're setting the titles in the viewDidLoad or viewDidAppear methods of the controllers. That won't work, because, while all the controllers are instantiated in the app delegate, only the controller at index 0 has its view loaded, and thus viewDidLoad will not be run for the other controllers. Instead, you should set the titles on the navigation controllers in the app delegate,
ProfileViewController *profileViewController=[[ProfileViewController alloc]init];
UINavigationController *nav2 = [[UINavigationController alloc]initWithRootViewController:profileViewController];
nav2.tabBarItem.title = #"Profile";

Why is root view set to zero size after dismissing a modal VC, when Auto Layout constraints are installed on UIWindow?

I try to use Auto Layout for my application's root view, i.e. I install Auto Layout constraints in the application's UIWindow and enable Auto Layout on the root VC's view.
The problem: When I dismiss a modally presented VC, the view hierarchy "collapses" and only the UIWindow remains visible. I assume that the root VC's view is resized to zero.
If I do not use Auto Layout on the application's root view everything seems to work fine.
My question: Is it forbidden to use Auto Layout for an application's root view? If not, what am I doing wrong? If yes, is this restriction documented somewhere in the official Apple docs, or is it merely "common knowledge"?
The following code is a minimal sample application that demonstrates the problem. You can simply copy&paste the code into a new Xcode project (use the "empty application" template).
#pragma mark Interface declarations
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow* window;
#end
#interface PresentingViewController : UIViewController
#end
#interface PresentedViewController : UIViewController
#end
#pragma mark AppDelegate implementation
#implementation AppDelegate
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Set this to false and the problem goes away
bool useAutoLayout = true;
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
PresentingViewController* pvc = [[PresentingViewController alloc] init];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:pvc];
self.window.rootViewController = nc;
[self.window addSubview:nc.view];
if (useAutoLayout)
{
nc.view.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary* viewsDict = [NSDictionary dictionaryWithObjectsAndKeys:
nc.view, #"ncView",
nil];
NSArray* hConstraintsWindow = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[ncView]-0-|"
options:0
metrics:nil
views:viewsDict];
[self.window addConstraints:hConstraintsWindow];
NSArray* vConstraintsWindow = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[ncView]-0-|"
options:0
metrics:nil
views:viewsDict];
[self.window addConstraints:vConstraintsWindow];
}
nc.view.backgroundColor = [UIColor redColor];
[self.window makeKeyAndVisible];
return YES;
}
#end
#pragma mark PresentingViewController implementation
#implementation PresentingViewController
- (void) loadView
{
self.view = [[UIView alloc] initWithFrame:CGRectZero];
self.view.backgroundColor = [UIColor greenColor];
self.title = #"presenting vc";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(add:)];
}
- (void) add:(id)sender
{
PresentedViewController* pvc = [[PresentedViewController alloc] init];
UINavigationController* navigationController = [[UINavigationController alloc]
initWithRootViewController:pvc];
navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:navigationController animated:YES completion:nil];
}
#end
#pragma mark PresentedViewController implementation
#implementation PresentedViewController
- (void) loadView
{
self.view = [[UIView alloc] initWithFrame:CGRectZero];
self.view.backgroundColor = [UIColor yellowColor];
self.title = #"modal vc";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done:)];
}
- (void) done:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
EDIT: In response to trojanfoe's answer, here are my thoughts why I believe I should be allowed to use Auto Layout for the root view:
UIWindow is derived from UIView, so I see no reason why I should not be allowed to install constraints in it.
It's true that there is no UIViewController instance that manages a UIWindow - but if we talk about the roles of the MVC design pattern, from my point of view the application delegate clearly takes the controller role, and therefore should be allowed to set up constraints.
To make this clear: If it's not possible to use Auto Layout on the root view for some technical reason, I will, of course, accept that. In fact, I want to be convinced not to use Auto Layout, but I prefer rational argument to blind coding.
I believe you are over-complicating the set-up of the views:
Constraints should be set on views only, not on the window.
The view controller is responsible for managing the view hierarchy and you don't need to be involved at all.
I think removing your constraints/view manipulation code will solve your issues:
PresentingViewController* pvc = [[PresentingViewController alloc] init];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:pvc];
self.window.rootViewController = nc;
nc.view.backgroundColor = [UIColor redColor];
[self.window makeKeyAndVisible];
Also shouldn't that be PresentedViewController given the navigation view controller will do the presenting?

Can't pop iOS viewController. Not sure, but I think it's something with the Navigation Controller

I'm having trouble trying to pop a view
App Delegate
#implementation MAAppDelegate
#synthesize navController;
#synthesize detailViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Init the navController for the Master Detail View of the grade cells
UINavigationController *navController = [[UINavigationController alloc] init];
detailViewController = [[UIViewController alloc] init]; //step6
navController = [[UINavigationController alloc] initWithRootViewController:[[MAController alloc] init]]; //step7
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = navController; //step8
[self.window makeKeyAndVisible];
// Set MAController as rootViewController
//self.window.rootViewController = [[MAController alloc] init];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
// Use the insanely cool TSMessages to show network alerts
[TSMessage setDefaultViewController: self.window.rootViewController];
return YES;
}
First part of viewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationController setNavigationBarHidden:YES];
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:#"Home" style:UIBarButtonItemStyleBordered target:self action:#selector(home:)];
self.navigationItem.leftBarButtonItem=newBackButton;
Later, when I change the viewController
NSLog(#"Opened progress report");
UIViewController *detailViewControl = [[UIViewController alloc] init];
// Set progress report as the view controller
[self.navigationController pushViewController:detailViewControl animated:YES];
UIImage *background = [UIImage imageNamed:#"bg"];
// Add static image bg
self.backgroundImageView = [[UIImageView alloc] initWithImage:background];
self.backgroundImageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:self.backgroundImageView];
// Add blurred layer to image when tableView goes in front of it
self.blurredImageView = [[UIImageView alloc] init];
self.blurredImageView.contentMode = UIViewContentModeScaleAspectFill;
self.blurredImageView.alpha = 0;
[self.blurredImageView setImageToBlur:background blurRadius:10 completionBlock:nil];
[self.view addSubview:self.blurredImageView];
[self.navigationController setNavigationBarHidden:NO];
So I don't understand why that when I do this, a selector from the button (that I know fires, because I get Righthtere in my log):
-(void)home:(UIBarButtonItem *)sender {
NSLog(#"Righthtere");
// Set progress report as the view controller
[self.navigationController popToViewController:self animated:YES];
}
It doesn't go back to the initial view controller.
You seem to be confusing popToViewController and popViewControllerAnimated. popViewControllerAnimated removes the current view from the stack and brings the new stack top the active view controller. popToViewController pops the stack until the listed view controller is on top of the stack.
Since you are calling popToViewController with self, it will look and see that the requested view controller is already on top of the stack and do nothing. If you wish to go back one view controller then your call should be.
[self.navigationController popViewControllerAnimated:YES];
I use the below code to pop the previous viewcontroller in iOS 8.
[self presentModalViewController:viewcontroller animated:YES];

Best approach to switch between TabBarController and ViewController

I have been trying an app with initial login screen which then takes to a TabBarController.
I want to know whats the best approach to do this any example code would be appreciated. I have tried it but I am unable to switch from ViewController to TabController.
I'm not sure that this is the best way to do it, but it's quick and dirty and works. Present a modal view controller inside your applicationDidFinishLaunchineWithOptions: method. You should replace the #selector with something more appropriate to what you want to do. Background color is for effect only.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *viewController1 = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
UIViewController *viewController2 = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = #[viewController1, viewController2];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
// ***** The relevant code *****
UIViewController *viewController = [[UIViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
[[viewController view] setBackgroundColor:[UIColor redColor]];
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[dismissButton setFrame:CGRectMake(10, 10, 300, 44)];
[dismissButton setTitle:#"Dismiss" forState:UIControlStateNormal];
[dismissButton addTarget:[self tabBarController] action:#selector(dismissModalViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
[[viewController view] addSubview:dismissButton];
[[self tabBarController] presentViewController:navigationController animated:NO completion:nil];
return YES;
}
I normally wouldn't like to put this sort of code in the app delegate, but if it's a one-time thing like login details, maybe it's ok.
Assume your root view controller is also your login view.
Now from your root view controller, you can present the tab bar controller a number of ways. One way is to just call the presentViewController method from the root view controller.
Setup
From within the root view controller, sometime before presenting the tab bar, set it up:
myTabBarViewController = [[MyTabBarViewController alloc] init];
[myTabBarViewController setModalPresentationStyle:UIModalPresentationFullScreen];
[myTabBarViewController setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[myTabBarViewController setRootTabBarDelegate:self];
Presentation
When you are ready to present, just call this:
[self presentViewController:myTabBarViewController animated:YES completion:nil];
Notes
The view controller hierarchy looks like this:
AppDelegate
L RootViewController
L MyTabBarController

How do i make the more button go always to more view?

I'm developing an app for ios with a Tab Bar. I have more than 5 buttons on the bar, so on the iphone i have the button more.
Now, suppose i have this buttons: Button1 Button2 Button3 Button4 More (and inside More) Button5 Button6.
If i click More and then Button5 i go in the view relative to Button5. Then i click Button2 (that is not in the More) and i go in the view relative to Button2.
So far so good.
Now if i click More i go not to More Tab but back to the view relative to Button5.
How do i make the more button go always to the more view?
You don't need to add more button. Just set the view controllers to the UITabBarController
- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated
and it will automatically create a more button if you have more than 5 view controllers! i.e. the count of NSArray is greater than 5.
Another way you could do is, whenever the user presses more, the first button gets removed and other buttons gets added.
Basically you can create an array and keep all the buttons inside it. And then based on the button pressed you can navigate to that particular view.
For Ex:
Initially you have: Button1 Button2 Button3 Button4 Next
After Clicking Next: Prev Button3 Button4 Button5 Button6
I used this code in my app delegate.m to solve the problem
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
UITabBarController* tabBarController2 = (UITabBarController*)self.window.rootViewController;
if (tabBarController2.selectedIndex < 4) {
[tabBarController2.moreNavigationController popViewControllerAnimated:NO];
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *viewController1 = [[UIViewController alloc] init];
UIViewController *viewController2 = [[UIViewController alloc] init];
UIViewController *viewController3 = [[UIViewController alloc] init];
UIViewController *viewController4 = [[UIViewController alloc] init];
UIViewController *viewController5 = [[UIViewController alloc] init];
UIViewController *viewController6 = [[UIViewController alloc] init];
UIViewController *viewController7 = [[UIViewController alloc] init];
UIViewController *viewController8 = [[UIViewController alloc] init];
UIViewController *viewController9 = [[UIViewController alloc] init];
[viewController1.view setBackgroundColor:[UIColor whiteColor]];
[viewController2.view setBackgroundColor:[UIColor redColor]];
[viewController3.view setBackgroundColor:[UIColor greenColor]];
[viewController4.view setBackgroundColor:[UIColor grayColor]];
[viewController5.view setBackgroundColor:[UIColor blueColor]];
[viewController6.view setBackgroundColor:[UIColor yellowColor]];
[viewController7.view setBackgroundColor:[UIColor brownColor]];
[viewController8.view setBackgroundColor:[UIColor magentaColor]];
[viewController9.view setBackgroundColor:[UIColor purpleColor]];
[viewController1 setTitle:#"one"];
[viewController2 setTitle:#"two"];
[viewController3 setTitle:#"three"];
[viewController4 setTitle:#"four"];
[viewController5 setTitle:#"five"];
[viewController6 setTitle:#"six"];
[viewController7 setTitle:#"seven"];
[viewController8 setTitle:#"eight"];
[viewController9 setTitle:#"nine"];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = #[viewController1, viewController2, viewController3, viewController4, viewController5, viewController6, viewController7, viewController8, viewController9];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
I have added a sample AppDelegate code which I tried and its working absolutely fine for me. Let me know what problem your having in this.

Resources