I am a beginner iPhone developer so I apologise if there is a lack of information here. I have created a storyboard which has an initial view controller (for app login) and if I detect that there is already a user logged in (I have an API token for a user stored already) then I will load a navigation controller instead which has a root view controller defined which is a table view controller. Within the table view controller I have a navigation item which contains left and right bar button items. I added another view controller with a label on, then I ctrl dragged from the right navigation bar button item to the simple view controller to create a segue. This segue is the one which does not work when I build and run the app. I also tried to create an IBAction and hook that up to the right bar button item and NSLog "Hello world" but that doesn't work. Here is how I am initialising the storyboard:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Instantiate the UIStoryBoard
UIStoryboard *initiialStoryBoard = [UIStoryboard storyboardWithName:#"iPhoneStoryboard" bundle:nil];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
// Set the first view controller of the storyboard as the root controller.
User *user = [[User alloc] init];
if ([user token] == nil) {
[self.window setRootViewController: [initiialStoryBoard instantiateInitialViewController]];
} else {
UINavigationController *feedNavigationController = [initiialStoryBoard instantiateViewControllerWithIdentifier:#"feedNavigationController"];
[self.window setRootViewController:feedNavigationController];
}
[self.window makeKeyAndVisible];
}
Can anyone suggest what I could be doing wrong?
First, remove all your code within the method application:didFinishLaunchingWithOptions: and just return YES;.
Create your storyboard like in the image below.
Use a UINavigationController as your entry point.
Set your authentication view controller as the rootViewController of the UINavigationController
Use another segue from your authentication view controller to your feed view controller and give it the segue identifier segueFeed
After that add the following code to your authentication view controller:
- (void) awakeFromNib
{
// Set the first view controller of the storyboard as the root controller.
User *user = [[User alloc] init];
if ([user token] == nil)
{
// do nothing because the correct view controller will be shown
}
else
{
// show the feed view controller
[self performSegueWithIdentifier:#"segueFeed" sender:nil];
}
}
Now the authentication view controller will be shown automatically if no token is given and otherwise the feed view controller will be shown immediately. At this point you can add your own navigation items (like Action 1 and Action 2) and connect them with other view controllers via segues.
Related
It seems there are several approaches to creating a UINavigationController to be the very first controller. Perhaps the easiest way is to simply click the View Controller in Storyboard and embed it in a navigation controller. But I would like to know the best approach when doing this only in code.
You can subclass UINavigationController, import the first view controller, and in viewDidLoad alloc and init an instance, then add it as a childViewController. In Storyboard replace the default view controller with a navigation controller and set the class to your nav controller. Note that in previous versions of iOS it was not recommended to subclass UINavigationController.
Or you can create another UIViewController, alloc init the first view controller, then alloc init a UINavigationController with that view controller as the root, add the navigation controller as a child view controller of this view controller, and add the navigation controller's view as a subview of this view controller's view. Change the class of the view controller in Storyboard. This is an awkward setup though, because you create a view controller whose purpose is to add a nav controller but it's not a nav controller itself.
I've read about another approach which involves creating the UINavigationController in the AppDelegate. Perhaps there are even more solutions.
What is the most appropriate approach, working in the latest development environment, targeting iOS 8+?
If you're starting with a controller in the storyboard, you only need to add two lines in the app delegate to embed that controller in a navigation controller. If you want to do it in code, I think this is the simplest way,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.window.rootViewController];
self.window.rootViewController = nav;
return YES;
}
If you want to do it entirely in code, with no storyboard, then you need to create the window, the navigation controller, and its root view controller. You also need to click on the project icon in the files list, and in the "General" tab, delete the word "main" from the "Main Interface" pull down (that entry tells the system to start with a storyboard named "main.storyboard").
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController *vc = [ViewController new]; // You need to create this controller's view in its loadView method
vc.title = #"Root View Controller";
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
[self.window setRootViewController:nav];
[self.window makeKeyAndVisible];
return YES;
}
I'd like to show the tableview controller embedded in the navigation controller from the app delegate.
The reason I need to do this is I'm building a wrapper over an api, so to login I direct users to a relevant webpage where they can login to grant my app access to their credentials, then they are sent to a relevant url end point which triggers my app. At this point I want to load the tableviewcontroller in the navigation and tabbarcontroller.
Is it possible to define multiple entry points on storyboards?
A screenshot of my current storyboard configuration is shown here:
https://www.dropbox.com/s/6wfo5werrs6vwy1/Screenshot%202014-10-09%2008.43.15.png?dl=0
You cannot define multiple entry points on storyboards. To achieve this, you should use [UIStoryboard instantiateViewControllerWithIdentifier:] and set the view controller to root view controller in your app delegate based on your condition.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString *identifier
if (/* path A */){
identifier = #"vc1";
} else {
identifier = #"vc2";
}
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:identifier];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
}
I have a main view controller using a navigation controller and i was making my application over that. Now i want to add a welcome view controller for my app and make it show first instead of my main view controller. Is there any way to do it.
What I did was adding a view controller to my storyboard and added two classes of the same name then i made it my root view controller and unchecked the root view controller from the main view but it is not showing on the window. Please help me, that how can i make my welcome view to appear before the main view controller. Thanks
Inside Interface Builder, under the attributes section (looks like a small slider) about a quarter of the way down the list of settings, there is a section labeled "View Controller." The second item in that section is a checkbox "Is Initial View Controller," check that box and you should see the starting arrow of the story board move to the specified view controller and the app should launch to that page.
Hope this Helps.
You could do this programmatically by setting the app window's rootViewController. From the app delegate's application:didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIScreen *mainScreen = [UIScreen mainScreen];
self.window = [[KTAppWindow alloc] initWithFrame:mainScreen.bounds];
[window makeKeyAndVisible];
window.rootViewController = [[WelcomeViewController alloc] init];
return YES;
}
Then you just need to set up your trigger to switch from the Welcome view to the main view. This could be scheduling an NSTimer, for example. Whatever the trigger is, once it occurs just change the window's rootViewController instance to your MainViewController.
I hope this helps.
It calls "Splash Screen" you just need to add a new ViewController in your Storyboard or if you don't have a Navigation Controller add.
In the Splash class, in the viewDidLoad method you need to put this:
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
HomeViewController *vc = [sb instantiateViewControllerWithIdentifier:#"Home"];
[[[[UIApplication sharedApplication] delegate] window] setRootViewController:vc];
[self performSegueWithIdentifier:#"Home" sender:self];
here you can add conditions if you needed
I am trying to push a new root controller to a navigation stack, but using a side reveal menu.
My app delegate has the following:
welcomeViewController = [[MyWelcomeViewController alloc] initWithNibName:#"MyWelcomeViewController" bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:welcomeViewController];
navController.navigationBarHidden = YES;
// Then we setup the reveal side view controller with the root view controller as the navigation controller
self.revealSideViewController = [[PPRevealSideViewController alloc] initWithRootViewController:navController];
[self.revealSideViewController setDirectionsToShowBounce:PPRevealSideDirectionNone];
[self.revealSideViewController setPanInteractionsWhenClosed:PPRevealSideInteractionContentView | PPRevealSideInteractionNavigationBar];
// Then we make the window root view controller the reveal side view controller
self.window.rootViewController = self.revealSideViewController;
Once the welcome view controller is displayed, the user logs in. Once logged in the following process runs again from the App Delegate.
self.navController.navigationBarHidden = NO;
[self.navController setTitle:#"Home"];
[self.navController pushViewController:homeViewController animated:NO];
I then have a side view controller setup which is a table view with custom cells setup.
When a row is selected I need to push a new root controller onto the navigation controller. I try this by using the following in the table view for the cell selected.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[self.navigationController setViewControllers:[NSArray arrayWithObject:accountViewController] animated:NO];
Unfortunately this does not do anything. If I add the code to the App Delegate and then call the method from the table view controller then it works, however not from the .m file for the table view itself. Adding a log I can see the above is run, just does not do anything.
I am unsure if I need to do anything different on the above. For example, completely pop the views currently shown, then create the navigation controller and PPRevealSideViewController all over again. If I am supposed to, I am unsure how to pop all the current views to then push the new to the window, not from the AppDelegate.
The reason I do not want this in the App Delegate is because it is the incorrect way to approach this, and I would then need a separate method for each new root controller I would like to push from the menu, so the App Delegate would become very large.
Check UINavigationController.h:
#interface UIViewController (UINavigationControllerItem)
#property(nonatomic,readonly,retain) UINavigationController *navigationController; // If this view controller has been pushed onto a navigation controller, return it.
It means when you do myViewController.navigationController you will either get nil if myViewController is not pushed to any navController or the navController reference myViewController is pushed into.
As I understand your tableViewController is not pushed into the navController stack, that means you can't get the navController with tableViewController.navigationController. Instead you'll need to use anyViewControllerInTheStack.navigationController or if the navController is the rootViewController of your keyWindow, by
((UINavigationController*)[[UIApplication sharedApplication] keyWindow].rootViewController)
Add something like this to your AppDelegate.h:
#define XAppDelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate])
Now you can access any iVar of AppDelegate from any .m file in your project.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[XAppDelegate.navController pushViewController:accountViewController animated:NO];
Make sure you add the correct imports.
One more thing: It's good to pop the login window from your navcontroller once you are done Logging in.
Hope this helps.
I have a split-view app that allows a user to select and display a thumbnail of a chosen image. I have placed a UIButton in the detailViewController using Interface Builder. When this button is pressed, I would like to have it change to a full screen view of the image. I have set up a new View Controller, called FullViewController and thought I had everything connected. The problem is that the navigation controller is null. I adjusted the AppDelegate.m to the following:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after app launch.
// Set the split view controller as the window's root view controller and display.
self.window.rootViewController = self.splitViewController;
UINavigationController *nvcontrol =[[UINavigationController alloc] initWithRootViewController:fullViewController];
[window addSubview:nvcontrol.view];
[self.window makeKeyAndVisible];
return YES;
}
This is the function in the DetailViewController.m which is called when the button is pressed. The navigation controller comes up null in here.
//Function called when button is pressed - should bring up full screen view
- (IBAction) pressFullViewButtonFunction: (id) sender{
//viewLabel.text = #"Full View";
if (fullViewController == nil){
FullViewController *fullViewController = [[FullViewController alloc] initWithNibName:#"FullViewController" bundle:[NSBundle mainBundle]];
NSLog(#"fullViewController is %#", fullViewController);
self.fullViewController = fullViewController;
}
NSLog(#"self.navigationController is %#",self.navigationController);//this is null
[self.navigationController pushViewController:self.fullViewController animated:YES];
}
I'm not sure how to fix this. I've tried adding in the couple lines in the AppDelegate, but when it runs, the table in the root view doesn't show up and it no longer properly switches between portrait and landscape views.
I have the rest of the code readily available if that would help clarify. Just let me know!
Thanks.
From the code you post it is not possible to identify the problem, but two common reasons for self.navigationController to be nil are:
you did not push the object behind self on to the navigation controller in the first place; indeed it seems so, since the navigation controller is added as a subview of the split view controller; possibly you mean the opposite... not sure...
(sub-case of 1) you showed the object behind self using presentViewControllerModally.
When I say "the object behind self" I mean the instance of the class where pressFullViewButtonFunction is defined.
If you need more help, post the code where you push your controllers on to the navigation controller...
On a side note, if you do:
UINavigationController *nvcontrol =[[UINavigationController alloc] initWithRootViewController:fullViewController];
and nvcontrol is not an ivar, then you have a leak.
Hope this helps...