I have an iPhone application that uses a tabbar on all my views.
Also, my first view controller has no navigationBarController but my second view does, as well as the tabbar.
My second view controller is a tableview and when I click on one of the cells, I want to be taken to the first view controller (passing along a parameter with it).
I've tried a few ways of doing with, which all kinda work, but don't.
secondViewController.m
FirstViewController *firstViewController = [[FirstViewController alloc] initWithNibName:#"FirstView" bundle:[NSBundle mainBundle]];
[firstViewController passThisAlong:#"id"];
//[self presentViewController:firstViewController animated:YES completion:nil]; //1
[self.navigationController pushViewController:firstViewController animated:YES]; //2
//[self.tabBarController presentViewController:firstViewController animated:YES completion:nil]; //3
[firstViewController release];
firstViewController = nil;
So as you can see above, I've tried 3 different methods of getting to the first view controller.
The second one loads the first view, drilled down inside the second view, as it keeps the nagigationBarController (even though first view doesn't have one) with the back button to the second view.
The other two do the same thing as each other. Which is it goes to the first view (without a navigationBar), but, there is no tabbar and the content of the first view has been shifted up about the height of the tabbar, and in it's place is just blackness.
I hope this all makes sense. I just want to be able to go from my second view when someone selects a cell in the table, to my first view as if I'd clicked the icons in the tabbar.
I've seen in the docs
'On iPhone and iPod touch, the presented view is always full screen. On iPad, the presentation depends on the value in the modalPresentationStyle property.' not sure if this is related to my issue and if so, if there's a way around it.
Many thanks,
It's normal, a modal View is supposed to work this way
From Apple Documentation
The ability to present view controllers is a tool that you have at your disposal for interrupting the current workflow and displaying a new set of views. Most commonly, an app presents a view controller as a temporary interruption in order to obtain key information from the user. However, you can also use presented view controllers to implement alternate interfaces for your app at specific times.
In your didSelectRowAtIndexPath you can use something like
YourAppNameDelegate *appDelegate = (YourAppNameDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate tabBarController].selectedIndex = 0;
Present modal view works this way. And why can't you use a Tab based application?
Related
I know this has been asked a million times, but I couldn't find a suitable answer in those many questions that I've examined.
I have a custom view controller, and I'm trying to display the view controller when the user taps a button (so no "infamous viewDidLoad problem" here).
Here is my code that runs when the user taps the button: (I have the NIB for the view controller, and I have a navigation controller)
ICLoginViewController *loginViewController = [[ICLoginViewController alloc] initWithNibName:#"ICLoginViewController" bundle:[NSBundle mainBundle]];
//assuming we have a navigation controller.
UINavigationController *navigationController= (UINavigationController*)[[UIApplication sharedApplication] keyWindow].rootViewController;
[navigationController.topViewController presentViewController:loginViewController animated:YES completion:nil];
I'm getting the Warning: Attempt to present <ICLoginViewController: 0xa08a810> on <UINavigationController: 0xa45de70> whose view is not in the window hierarchy! error when I try to present the view controller. Nothing happens on screen. If I tap multiple times I get the same error, and still nothing happens. I've set a breakpoint and verified that navigationController and navigationController.topViewController are not nil. I' using storyboard (if it helps) but not for the custom view controller that I'm trying to display. (I want to make it an app-independent library in the long run, so I'm not referencing any app-specific modules within) Why am I getting this error?
I've found the solution. The problem was, my modally displayed view controller was not the 'top' view controller in navigation controller. If I change the calling view controller to be pushed instead of being modal, then it becomes the top view controller and my app works well. Apparently, this had nothing to do with my custom view controller, but my navigation stack.
If its in an NSObject create a method inside the NSObject that takes your current viewController as an argument and present it there.
eg:
-(void)presentInViewController:(UIViewController *)controller{
ICLoginViewController *loginViewController = [[ICLoginViewController alloc] initWithNibName:#"ICLoginViewController" bundle:[NSBundle mainBundle]];
[controller presentViewController:loginViewController animated:YES completion:^(BOOL comp){}];
}
This way you can call that view controller wherever you want instead of trying to find your way through the navigation stack from UIApplication.
I would like to combine UITableView and UINaviationController in an app but as a newbie most apps I've seen just send you straight to the results view (UITableView). But, I guess a "normal" search application does not assume you have the results on the first screen. There should be a search form on first screen with input fields and a button that triggers the search process and show some results and navigation.
So, I'm just trying to replicate this normal behaviour in my app. I've already made the search form (no navigation shown on it, of course) and a seperated View called "ListingViewController" with its related View and containing a UITableView and where I think I should add the Navigation...The next idea will be to make a DetailViewController and possibly and ListingMapController to show the listing in a GoogleMap.
So, where I'm stuck at is how to add this Navigation Controller ?
Some suggested me to add it in the SearchViewController delegate...
But I don't want a navigation on search form of course...
Some suggested me to open the Navigation controller modally...
But, I"m also planning at adding a Tab Bar to allow user to see other informations (like About,etc...) and with a modal Nav controller I don't know if they will still see the bottom Tabbar...
Any suggestions? What do you think is of best practices especially to avoid my app of being rejected by Apple?
Thx in advance for reading and helping!
Stephane
You could init the navigationController with your View Controller as the root view Controller. Then hide the navigationBar (if you need to). You would then add the navigationController.view as the subview. This will basically look like the original view controller. Then you can pushViewController: animated: to push the results view Controller.
So, for example in your AppDelegate (or in the proper view controller):
Create a property and ivar for a UINavigationController and hook up its outlets in interface builder. Then set your search controller as the root view controller for the nav bar, and add it as a subview.
MySearchViewController* searchController = [[MySearchViewController alloc] init];
self.myNavigationController = [[UINavigationController alloc] initWithRootController:searchController];
[searchController release];
self.myNavigationController.navigationBarHidden = YES;
[self.window addSubview:self.myNavigationController.view];
[self.window makeKeyAndVisible];
Then of course in your searchController, you would simply say:
ResultsViewController* myResultsViewController = [[MyResultsViewController alloc] init];
//You may want to create another init method and pass in some arguments like an array:
// [[MyResultsViewController alloc] initWithResults:results];
then push the viewController
//This is in your search controller class
[self.navigationController pushViewController:myResultsViewController Animated:YES];
[myResultsViewController release];
from the results viewController, to get back you pop the view controller off of the navigationController view controller's stack.
//In results view controller perhaps in some IBAction for a back button:
-(IBAction)backButtonPressed:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
New to iPhone development, but I've been given a big project as a first go and I'm a bit stuck.
Basically the app will start with a settings screen, then you click a button to go to a dashboard with multiple option buttons. Each button will lead to a different Navigation View with tables.
The way I've approached this is to start with a UIViewController with a button, which I've got wired up but when you hit the button and I do:
[self.view removeFromSuperview];
UIViewController *newView = [[UIViewController alloc] initWithNibName:#"Dashboard" bundle:nil];
[self.view addSubview:newView.view];
the second view isn't loading. I just get a blank screen. Do I need to make a reference in the first controller to the second?
Also, am I approaching this in the right way? As long as I removeFromSuperview will I be able to load the navigation controllers on the press of a button?
Sorry if this isn't too clear, I've been through books and lots of websites but don't seem to be able to get my head around this.
Thanks
There is nothing here with the new view, rather the problem is with current view. You have removed the self.view from super view.
[self.view removeFromSuperview];
So anything added to self.view will not be shown, as self.view itself is removed.
When presenting child controller/view from a parent controller, you should consider using presentViewController. Eventually, use dismissViewControllerAnimated when you want child to disappear and parent to reappear.
In parent view controller:
ChildViewController * child = [[ChildViewController alloc] init];
[self presentViewController:child animation:YES completion:Nil];
In child view controller, ie. in some action handler:
-(IBAction)close:(id)sender
{
[self dismissViewControllerAnimated:YES completion:Nil];
}
IMHO you should also get in the habit of naming instance variables to what they are instantiated from. In your example you name the instance newView, when it should be something like newViewController. That way you make sure you don't mix up views with view controllers.
[self.view removeFromSuperview];
You've removed the view from the superview
[self.view addSubview:newView.view];
But you're adding the new view to the same view that you have just removed from the superview. It's not displaying anywhere.
Your third line adds newView as a subview of self.view, but you just removed self.view from it's superview.
I'd suggest reading more about view controllers. You'll want to have one view controller per "screen", so one for your settings screen, one for your dashboard, one for each table, and so on. Then, manage which one is visible by pushing and popping these view controllers from the nav controller's stack.
This removes self.view, which will most likely destroy the object since there will be no other references to it:
[self.view removeFromSuperview];
Here you are creating an UIViewController, and adding it's view to self.view, which is probably not what you want:
UIViewController *newView = [[UIViewController alloc] initWithNibName:#"Dashboard" bundle:nil];
[self.view addSubview:newView.view];
Look into UINavigationController so that you can easily swap screens in and out with some built in animations. Here's a bit more about them. Here's a tutorial.
The UIViewController's view should not be removed from or added to a view hierarchy outside the control of the view controller. While you might be able to get that manipulation to work now it won't in the future.
Read up on view controllers here.
The basic idea is that you present the view controller then it will take care of manipulating the view hierarchy for you.
So a better approach to get started would be to do something like this;
[viewController1 presentModalViewController:viewController2 animated:YES];
This line of code will present viewController2 with the default modal animation (slide in from the bottom). If you'd like a different animation you can change the modalPresentationStyle to one of the constants in the UIModalPresentationStyle enum on viewController1 (note thats a viewController1, not viewController2).
If you want something more like the Clock app look into the tab bar controller. If you want something more like the Mail app look into the navigation controller.
This is the situation:
I have a tab bar with 2 tabs. Tab01 and Tab02.
In Tab01 I have a button which pushes repVC:
repVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:repVC animated:YES];
[(UIViewController *)[tabController.viewControllers objectAtIndex:0] setView:repVC.view];
[repVC release];
Inside repVC I have another button which pushes an MFMailComposerViewController:
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
[self presentModalViewController:mail animated:YES];
[mail release];
The problem is: when mailView is shown(in Tab01) and I click Tab02, then back to Tab01, the mailView is hidden and even if I click the email button again, the view won't be presented.
So what I have is: Tab01.view -> repVC.view -> mail.view
For repVC, I use this line when I push the view so that even if I go switch tabs, that view will still be activated:
[(UIViewController *)[tabController.viewControllers objectAtIndex:0] setView:repVC.view];
But I can't do the same for mail because tabController is declared in another class which I cannot import. So I can't access the tabController and set the view for Tab01.
Hope the edit helped the understanding.
Hmm,
I still would suggest to use a Navigationcontroller. Would make things way easier, is conform to apple guidelines and suggestions and is pretty fast implemented. (Just create a Navigationcontroller, put the View of Tab1 as main view and hand it over to the TabbarController. Then for the mailView use [self.navigationController pushViewController:mail animated:YES]; Then the navcontroller "saves" the present view for you when u switch tabs)
But if for some Reason you have to use a modalViewcontroller you could either just deactivate the tabbar while the ModalView is shown or try to implement a switch or a simple if...else case in your ViewWillAppear where u check what screen to load.
Then Clean out the Window and load the right screen.
Hope you get the idea of what I mean, sometimes my way of writing seems to confuse people. ^^
A little more information would be great.
How did u set up your TabbarController?
How do u push the new view? Within a UINavigationController? If not, then do it with a navController, he should save the actual state of view and your problem should be solved.
If u already use a navController please post your ViewDidLoad and ViewWillAppear of the Viewcontroller of Tab 1
As #Amandir points out you could probably solve your problems by using a UINavigationController. I get a feeling that you are trying to abuse the modal view controller concept a bit and that's why it doesn't work as you expect. When you use presentModalViewController:animated: the intention should be that you are displaying a view that is modal, i.e. the user must interact and dismiss the modal view before she can continue.
What the paragraph above means that when you present a modal view controller it shouldn't be possible to use the tab bar. Since you are using the word push I'm guessing that you would like change the view of Tab01 while still being able to use the functionality of the tab bar. The problem is that there isn't any built-in method of pushing view controllers besides UINavigationController. persentModalViewController:animated: should only be used in case where you want a modal view, which on the iPhone means a full screen view.
The easiest way would probably be to use an UINavigationController and hide the navigation bar. Then you would get the functionality I think you are after. The other option is to manually add and remove sub views.
[self.view addSubview:repVC.view];
and
[repVC.view removeFromSuperview];
[self.view addSubview:mail.view];
You can use block animations if you want some fancy transitions.
I've been reading the Head First iPhone Development book and I understand how to get to a new view from a table but how exactly would I be able to get to a new view or view controller, by just simply pressing a button? Is that even possible?
I mean there are some apps where you click a button, not a table cell and it loads a new view. How exactly is that done? If someone could help out a newbie it would be greatly appreciated!
I think what you're looking for is a modal vew controller. THis presents a modal view like you described on top of everything else. If rootViewController is the view controller that is displaying your current view, and myNewViewController the view controller you want to display modally:
[rootViewController presentModalViewController:myNewViewController animated:YES];
There's plenty of examples of this kind of thing on the net, just search for presentModalViewController
Like bpapa said in the comments, it's hard to be specific without code. However, generally what you want to do is:
Build a navigation controller that contains one original view.
Create a button in your original view using the Interface Builder.
Build a callback method (usually defined with IBAction) that is run when the button is pushed.
In that callback method, create a new view and push it onto the navigation controller the same way you would using a table view cell.
Alternately, if you only want one level of hierarchy, you could use a modal view controller; instead of pushing onto the navigation controller in the last step, just present the modal view controller.
The general answer is that you have an object that manages which view controller loads when.
The most commonly used is the UINavigationController. It is a UIViewController that instead of controlling views, controls other view controllers. It works like a simple stack. You push views you want to display onto the nav's controller stack and when you want them to disappear you pop them off.
A common (though sloppy) way of using a nav is to make it a property of your app delegate. Then anywhere in your app you can references it by:
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
The view controller for the first the user sees is held in the nav's topViewController property. If you want to load a view based on a user action in the topViewController.view, you would have something like this:
- (IBAction) loadNextView:(id) sender{ // Action called by a a UI event such as a button press.
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
UIViewController *nextViewController=...// load from nib, connect with IBOutlet, create programmatically
[nav pushViewController:nextView animated:YES];
}
The first view disappears to be replaced by the next one. To return to the first view, you have a method in the next view controller like so:
- (IBAction) unloadSelf:(id) sender{ // Action called by a a UI event such as a button press.
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
[nav popViewControllerAnimated:YES];
}
... and the nav returns you automatically to the previous view regardless of what that view was.
When you first start out, especially if you use Interface Builder, the structure of the app is largely hidden. Behind the scenes all view controllers and their views exist in a hierarchy of some kind that leads back up to the app delegate. You should train yourself to think in hierarchal terms even if it is not immediately obvious how that hierarchy is constructed.