Getting iPhone app to display one of two different views - iphone

I have a program where we are using a navigation controller and need the app to launch to one of two different views. Basically if certain info has previously been entered then we need the app to launch to view A, but if the info has never been entered then we need it to launch to view B. I am having difficulty getting this to work and am wondering what ways if any I could implement this. I am certain it needs to be done in the app delegate but I am not sure how. Thanks in advance!

Implement the following method in your app delegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[window makeKeyAndVisible];
if(condition) {
[window addSubview:[mainViewControllerA view]];
} else {
[window addSubview:[mainViewControllerB view]];
}
}
There you can choose which view to load depending on your condition.

From your question, it sounds as though your UINavigationController is defined inside a XIB along with your root view controller. In that case, you will need to load the appropriate view from within application:didFinishLaunchingWithOptions: of your App Delegate. From there, you can set the first view controller for the UINavigationController using setViewControllers:animated:.

I have met this issue and solved it.
In you navigation controller build a container view.
Then depending on your conditions you decided what view to put in the container. You might have built these two views beforehand. Then, you can add the view into the container view.
I think the "Elements" sample has an example of the container view.

Related

Change XIB View at app start

What is the right way to change XIB View which loaded at app start depending on some app settings. Of course I know how to get all settings I need.
In your application's delegate, in the method
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if(yourSettings)
myViewController = [[MyViewController alloc] initWithNibName:#"FirstNibName" bundle:nil];
else
myViewController = [[MyViewController alloc] initWithNibName:#"SecondNibName" bundle:nil];
self.window.rootViewController = self.myViewController;
[self.window makeKeyAndVisible];
return YES;
}
And that's it. Probably you may want to save your settings in your user defaults so you can load the view properly.
However, I would use 2 different view controllers, as you probably want them to do different things, not just modify some graphics.
EDIT-
Let me see if I understand. You will always load the first view controller and, if some conditions are met, you modally want to present the second view controller that will get dismissed at some point, returning the user to the first view controller. If this is the case, I suggest you move the code in your first view controller, in viewDidLoad or better yet in viewDidAppear, as this view controller will always get loaded. Also this way the user can see that he will eventually go to that view controller. I use something like this in applications the user needs to login to so that it will be obvious for him that he cannot continue until he does login.
I can't say that this is the right way to do it, because it's up to the programmer how he arranges his code, but it would seem to me that the place that controls what view and how it appears belongs in a view controller and not in the delegate, especially considering that your first view controller always gets loaded. It should be up to that view controller to see if it presents the second one or not.

iPhone dev - Navigation-based app with a start-up log-in screen?

I'm making a navigation-based app that I want to start out with a screen that let's the user enter a username/password before continuing on to the normal navigation hierarchy of views. I'm trying to think about the best way to go about doing this. Should I create a new viewController for the log-in screen and start out with that and then use presentModalViewController to go to the RootViewController of the navigation hierarchy? Or would it be better to start out in the RootViewController (a tableViewController) and immediately push the log-in view onto the screen, and then pop it if they enter a correct username/pw combo? I'm also curious as to HOW you would start your application with a different view from the RootViewController, because right now that's always the first one that shows up. Thanks!
If the user suspends/resumes your app or locks/unlocks the phone, are you going to make them login again? If so, then I'd choose your second option, pushing the LoginView above the NavigationView. Otherwise I think starting with the LoginView and replacing it with the NavigationView is fine.
Your ApplicationDelegate class specifies which controller will serve as the root controller for the application.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window.rootViewController = self.navigationController;
...
}
In this example, an instance of UINavigationController is set as the root view controller once the application has completed launching (e.g. all resources are loaded and the transition is made from the splash image).
If you want a different controller to serve as the root of your application, then this is where you'd do it.
To answer your question about the design, it depends on the desired workflow. Based on what you've described, I'd start with a barebones view controller that loads up a login view/controller. Upon authenticating the user, this view controller can switch to the main application view. With a design like this, you can easily switch back to the LoginViewController if you need to re-authenticate the user at any point.
ApplicationViewController (root)
- LoginViewController
- MainViewController (this could be a UINavigationController or whatever you need it to be)
Hope that helps!

iOS Root View Controller's viewDidAppear: called while splash screen (Default.png) still on screen

In my iOS app I want to run a series of operations in my Root View Controller after it has already appeared on the screen. However, it seems that the iOS app is calling viewDidAppear while the splash screen (i.e. showing the Default.png image) is still on the screen and before the root view controller is laid out on the screen. I've tried the same code in viewDidLoad as well, and had the same problem. How can I force code to run only once the root view controller is actually on-screen?
in viewdidload use this
[self performSelector:#selector(loadData) withObject:nil afterDelay:.5];
and then use your code inside loaddata method...
I just encountered a very similar problem myself, wherein I wanted to display a modal login view after my root view controller loaded. I'd previously been using viewDidAppear, but the behavior broke when I upgraded to the iOS 4.3 SDK.
I fixed it by calling a selector on my root view controller from the app delegate's application:didFinishLaunchingWithOptions: selector. Using a delay as in the other answer is a bit of a kludge, and probably not entirely reliable.
In yourAppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add the view controller's view to the window and display.
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
// Invoke operations here. For example, show login view:
[viewController showModalLoginView];
return YES;
}

Programmatically navigating in iOS

I'm in the process of porting an existing Android app to iOS, and I'm pretty inexperienced in this environment. The examples I've read so far for navigating multiple views all use some kind of visual user control for triggering the loading and unloading of views (tab bar, nav bar). This application's navigation needs to be pretty strict and not allow the user to freely move around between the three views.
The app needs to have a full screen splash view, a main view that the user interacts with, and a third view for data collection. The splash screen should appear, and the user should be navigated to the main view when tapping on the splash image. There will be custom logic in the main view controller for determining if data is required at which point it should navigate to the data collection view. Once valid data is entered and the user clicks OK, it should navigate back to the main view.
What I've read so far is that all Views should have an associated UIViewController class, and the easy way to do this is to create the XIB and UIViewController class in one shot and link them together (I have plenty examples/books/tutorials that I can reference for that part). I believe what I've read is that the app should have a root UIViewController that handles loading the others and navigating between them.
My questions are:
What class should I derive from for my main view controller that I use to load the others?
How do I wire that up to the app so that it knows to load this as the main controller?
What is the accepted standard way of having a navigation controller in the app and allowing the other views to obtain a reference to it? Should my UIViewControllers hold a reference to their parent controller, or should they ask the UIApplication for a reference to it when needed? How do I make sure I don't instantiate extra copies of the views and their controllers as the user navigates?
What class should I derive from for my
main view controller that I use to
load the others?
UIViewController
How do I wire that up to the app so
that it knows to load this as the main
controller?
Read the "Defining Your Subclass" section of View Controller Programming Guide for iOS. Scratch that -- read the whole thing. It's all important, you might as well start to learn it now. Also read App Programming Guide for iOS. Again, read the whole thing, but the application lifecycle part is the most relevant to your question.
What is the accepted standard way of
having a navigation controller in the
app and allowing the other views to
obtain a reference to it?
Again, this is explained well in View Controller Programming Guide. Views should never care about the navigation controller, but any view controllers that are part of a navigation stack have direct access to the nav controller via their respective navigationController properties.
Should my UIViewControllers hold a
reference to their parent controller,
or should they ask the UIApplication
for a reference to it when needed?
A view controller already has a reference to its parent controller in its (surprise!) parentController property. It's best for a controller to avoid assuming too much about its
parent, though. If the controller expects its parent to be a certain type or respond to certain messages, it becomes more difficult to reuse that controller or reorganize your application. Try to give the controller what it needs to do its thing when you create it. If the controller will need to ask for additional data or something like that, delegation is a good way to go.
How do I make sure I don't instantiate
extra copies of the views and their
controllers as the user navigates?
Exercise caution. There's not much danger of creating extra copies of views in a properly structured application because each view controller should take care of its own views. If you find yourself loading or otherwise creating views outside the context of the view controller that owns them, stop that.
It sounds like you can accomplish what you need with a couple of basic calls. To programmatically call a view controller:
- (void)showController {
MyViewController *myController = [[MyViewController alloc] initWithNibName:#"MyViewControllerXIB" bundle:nil];
[self.navigationController pushViewController:myController animated:YES];
[myController release];
}
To return to the previous view just call from any view controller:
- (void)goBack {
[self.navigationController popViewControllerAnimated:YES];
}
Read up on the documentation for the UINavigationController for more ways to move through views. This method is just one of many ways to do this and may not be suitable for all situations.
Not quite right — each UIViewController should know how to trigger its children. Apple's preferred navigation path through views is a branching tree, with the caveat of tab bars that collapse multiple view controllers into a single node on the tree.
You don't explicitly handle loading. Normally you have a sufficient relationship between your NIBs that the container classes are loaded automatically. Cocoa will then load the views whenever they're needed but not yet loaded (which is the purpose of loadView and viewDidLoad), and keep them unless and until a low memory warning requires them to be purged (leading to viewDidUnload). It's relatively rare that you explicitly load a NIB yourself (though table view cells are an obvious example where programmatically loading a NIB is quite common).
So you'd probably have:
a splash screen or preview of the first view controller, as the Default.png
a view controller that probably displays Default.png, and has two outlets going to the data collection controller and the main controller
when the user taps the button on the main screen, ask the model whether data collection is necessary. If so then navigate to the data collection controller, otherwise navigate to the main controller
give the data collection controller an outlet to the main controller and let it perform a navigation there at the appropriate moment
You get a MainWindow.xib for free when creating a new view based project. Probably the easiest thing to do is to put references to the three UIViewController subclasses in there, but set each of them to load from other files. Set the links between them in MainWindow.xib, set the links to things within the relevant views within the relevant XIBs.
That will prevent you from keeping multiple instances of any controllers about, and the built-in Cocoa loading mechanisms will ensure that the stuff that occupies significant amounts of memory — the views — is loaded only on demand and kept for no longer than space allows.
There's no need to link to parent view controllers. Every view controller already knows who presented it, via the parentViewController property. So if a view controller wants to dismiss itself and return to whoever presented it, you can just issue:
[self.parentViewController dismissModalViewControllerAnimated:YES];
Because the model is ideally a separate sovereign thing, all controllers really need to know is which other controllers they can present, how to populate themselves from the model and how to push data back to the model. You rarely end up with particularly complicated links between view controllers.
I think you should have the view loading/unloading in the application delegate and then each view should send notifications to the application delegate.
Here is the official introduction from Apple:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html
You can set before which ViewController to load first
If the application is navigation based use the following code:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
MainViewController *mainViewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
self.nav = [[UINavigationController alloc] initWithRootViewController:mainViewController];
[_window addSubview:nav.view];
[_window makeKeyAndVisible];
}
If the application is View based use the following code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}

iPhone - UIButton to select tab in different view

Fairly new to iPhone app development, so this might be really obvious (if so, apologies in advance!)
I'm building an app which has a tab bar. However, when the app first runs and 'launch screen' is shown with 3 UIButtons - each of these buttons points at a view of one of the tabs. What I need to do is:
Close the existing view
Open the selected view
Set the highlighted tab accordingly
This sounds like it should be quite easy, but a few hours of Googling has found nothing!
Thanks for your help,
Kev
Additional:
Sorry - I am using a tabBarController... But instead of immediately launching the tab bar views I'm using the code below to launch the home menu instead.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
homeViewController *splashView = [[homeViewController alloc] initWithNibName:#"homeView" bundle:nil];
[window addSubview:splashView.view];
// [window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
On the home menu there are UIButtons which need to link to individual tabs... Hope this clarifies...
Cheers!
Oh. Then it gets a bit more complex. What you want to do is basically this:
Add a method to your AppDelegate - (void)showTabBarWithSelectedTab:(NSUInteger)tabIndex. In this method, use tabBarController.selectedIndex to select the correct index, then remove homeViewController's view from the window and add tabBarController's view instead.
In homeViewController, have actions for the buttons that calls the newly created AppDelegate method with the correct tab index.
Generally I would say that this adds a bit too much logic to the AppDelegate. Ideally you'd implement this in a new view controller, surrounding and managing both homeViewController and tabBarController. However, having a UITabbarController inside of another view controller isn't officially supported - although you can get it to work anyway.
it's a bit hard to fully understand your question, but it sounds like you should be using UITabBarController instead of the stand-alone view UITabbar. This is essential reading! Good luck