I have a UINavigationController, complete with table view and associated magic.
The data I'm populating that table view from may have items from multiple categories, but the default view for the user will be one in which they are viewing all of the items, and then they have the ability to move backwards to a different table view that would allow them to select a different category, which would then return to the original table view with the appropriate data populated.
What's the proper approach for this? I can't seem to wrap my head around how I would make the navigation controller give me a back button (with appropriately wired up actions) without having come from a previous view in the stack (which wouldn't really exist at launch time if I start the user off from what is essentially the detail view, in stack terms.)
Also, the back button should be titled "Groups", not "Back", but that's really just an implementation detail. :)
Update: This issue finally manifested itself in production code, and here’s how I fixed it:
My UINavigationController is created in a nib, with the root view set as the “groups” view. Then, in my app delegate, I push the second view onto the stack while the app is launching.
That works fine for achieving the proper stack, but that doesn’t help with the back button title, because the navigation controller didn’t seem to want to grab the title from the root view, and instead was showing a back button with “Item” as the title.
So, on the pushed view, in viewDidLoad, I set:
self.navigationController.navigationBar.backItem.title = #"Groups";
and that did the trick.
The only potential downside of doing it this way would be if the pushed view controller were ever used in a scenario where the view below it wasn’t the groups view, but since the design of this particular application ensures that never happens, I’m accepting that failure. ;)
Another update:
I’m an idiot. Just set the title property of the navigationItem provided by the navigationController in Interface Builder, and boom, no issue. Or do it in code. It doesn’t matter, just don’t do it by setting the backItem.title way I show you above. That’s just dumb.
In your application delegate's .m file in the application:didFinishLaunchingWithOptions: method just push your view controllers like you normally would with[self.navigationController pushViewController:your_view_controller animated:YES]; and it should push them on before the application's first view controller appears.
To change the text of the button to Groups just call this before pushing your controllers.:
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle: #"Groups" style: UIBarButtonItemStyleBordered target: nil action: nil];
[[self navigationItem] setBackBarButtonItem: newBackButton];
[newBackButton release];
Related
I have an app I am working on that has a main screen with two buttons. One will take you to a view of a GPS (map) and then once there (new VC) it has options for setting that position or bringing up a list (tableview, another VC) of all locations already tagged.
At the list VC, if you click on the table cell, it will bring up the VC with the map. Problem is, this then adds the same VC bak on the stack. If a user clicks the Cancel button, they go back ones screen, then cancel goes back another screen, etc... until back to the main.
I know I can do the [self.navigationController popToRootViewControllerAnimated:YES]; to pop back to root but that is not always what I want.
Also, I know I can do: [[[self presentingViewController] presentingViewController] dismissModalViewControllerAnimated:YES];
I guess what I am saying is I want to "reuse" the GPS map view so I can call it from other VC's, so that is why I didn't go with the "pass back" to calling VC. So, is there away to either when a button is pressed and is to present a new VC, can I dismiss the prior one after the new one is shown? This way, a dismiss of current VC would take me back to where I need to be.
I hope makes sense and also that this question doesn't fall into the "Not an actual question" category.
Any help or better suggestions is greatly appreciated. Thx
Geo...
If you want to jump back some number of levels in a navigation controller's VC stack, you'll probably want to use its popToViewController:animated: method. To figure out if a particular view controller is on that stack, look at the navigation controller's viewControllers property. Be careful, though, as this kind of jumping around is a rather nonstandard UI behavior (even though there's API for it) which might confuse your users.
Also, using navigation controllers and presenting modally aren't the only ways to manage multiple view controllers -- you can always set the window's rootViewController yourself (and animate the change with UIView animations), even wrapping up your custom transition type in a custom UIStoryboardSegue if you like.
You can put a delegate in the table view. So that when a cell is pressed the info is passed to the delegate method in the VC which will dismiss the table view and reloads itself with the new info. You will have to implement refresh method in that VC.
I'm a new iOS developer with a simple question: I want to programmatically move from one view controller to the next, how do I write this code?
So far I have:
UINavigationController *navigationController;
navigationController = [[UINavigationController alloc] init];
[self.view addSubview:navigationController.view];
[navigationController pushViewController:viewController animated:NO];
I'm not even sure if this will work, ultimately, but my main question is "viewController" in line 4. The program doesn't know what that is. It is the name of my current view controller, but how do I set it up so that it knows what I mean by viewController?
As an aside, the above is part of an if/else statement that occurs and is connected to the NSUserDefaults class to make it such that the view controller I am referring to only loads if terms and conditions have not previously been accepted. Will that work? Thanks.
First the simple answer: pass self when you want to pass the "current" object.
The more important consideration is: is that nav controller on screen? It's likely that your code won't do much unless you use a navigation controller which is (probably) the window's root view controller.
This is fairly easy to setup in your storyboard ("embed in"->"navigation controller") and then you don't need to instantiate it in code, you simply use self.navigationController (usually).
Normally, you would instantiate viewController just before you push it, so the program will know what it is. And, sure, you can have an if statement, and push this new view controller based on how the if statement evaluates.
I'm not sure about the code you wrote -- whether that's right depends on the structure of your app, and where you're doing this. Often, the navigation controller is made the root view controller of the window, and you set the navigation controller with a root view controller of its own when you create it
I've set up a really simple project using storyboards including two views as shown here: http://i.stack.imgur.com/iRx21.png. The navigation can be done by either selecting a cell in the custom table view or hitting the back button labelled with "<<". Everything works fine except the following:
when I switch between the views, every time an instantiation happens. The profiling shows an increasing number of view objects. I would like to keep only one of each view and instantiation should be happen only once. What am I doing wrong? (I'm using ARC.)
Thanks in advance!
You should not link your back button to the parent view controller. This is what causes the new instantiation.
The way to go is to embed the table view into UINavigationController (in IB, choose Editor -> Imbed In -> Navigation Controller. Then change your segue to a Push segue. You can of course hide the navigation bar etc. to make things look exactly as you like. Then, link the back button to the controller with an IBAction and in the handler do a simple
[self.navigationController popViewControllerAnimated:YES];
This would be the appropriate logic of what you are doing. Of course, you can also push the web view modally and then handle the button click with
[self dismissModalViewControllerAnimated:YES];
Hi there and thank you in advice for your help. I have a really strange problem while working with ViewControllers in Xcode4. First of all I have to say that I'm not using storyboards and I prefer to create any UI element programmatically. So I've set a UIButton and I want that, when pressed, it brings me to a new view controller. This is the code I'm using for a button:
-(void)settingsAndExportHandle:(UIButton *)buttonSender {
SettingsViewController* settingView = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
settingView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:settingView animated:YES];
}
This buttons is initialized and allocated in the viewDidLoad method of the RootViewController. I want to switch to the other view controller (in this case SettingsViewController) when I press the button.
The strange thing is that when I press the button, the animation that flips the controllers goes well, but when it finishes I obtain the EXACT same things that I had on the RootViewControllers (same custom views, same buttons, same all!). The question is: what I'm missing?? I have to say that I use ARC (automatic reference counting) so I can't release or dealloc the views and buttons I've created on my RootViewController.
Any help will be appreciated. Thank you all!
Pushing and and modally presenting view controllers does not deallocate the view controller that presented them. It simply adds the additional view controller to the stack. You'll need to implement a callback method so that when the user hits the button to flip back to root view controller, your settings view controller lets the root view controller know what's about to happen so you can call a method you've written to reset the interface back to whatever state you need it at. You may also be able to use viewWillAppear: but that's a little messy.
However, according to the Apple Human Interface Guidelines, the user expects that when they push a view controller or modally present it, the view controller they were on will save state and be exactly the way they left it when they came back. It's disconcerting and annoying when state is not preserved while in a navigation controller context. It is especially annoying when it's modally presented.
Think about this - A user is in a hypothetical Mail app. They start typing out an email and set a font size and a color. They tap on the add attachment button, which brings up a modal view controller that allows them to select a picture. They select the picture and the modal view is dismissed, and in your implementation, the mail composing interface would have reset and the email content would be gone or at the very least the selected font size and color would be back to the default. That's not a good experience.
I'm writing a program with a UITableView with and add button in the Navigation Bar which leads to an edit page. When you click on an item in the table, a view (rView) is pushed with information pertaining to that item. This view has an edit button that also leads to the edit page. Is there a way that I could put an if statement for the done button on the edit page that says "if parentViewController is the UITableView to go to rView, else popViewController?" I would assume there is a way to do this, but I'm not sure of the syntax to do so. Thanks
If I understand correctly you have a UINavigationController and push onto it
a UITableView
an "rViewController" (you can't push a view, must be a controller)
an "EditController"
But there is a possibility that step 2 is omitted and you go directly to the edit screen.
Now when the last controller is popped, you want to be able to always go to a "rViewController", even if it's not on the stack.
First of all, the parentViewController is NOT the previous controller on the stack, but rather the UINavigationController itself, so it has nothing to do with the present problem.
The way to do this is by setting the UINavigationController's viewControllers property explicitly with an NSArray. I haven't tried this but this should work:
When a user presses the "add" button, instead of just pushing the edit view controller, do something like:
NSArray* stack = navigationController.viewControllers;
navigationController.viewControllers = [stack arrayByAddingObject:rViewController];
[navigationController pushViewController:editController animated:YES];
(By the way, I would suggest not using names like "rView" except maybe for very short-lived local variables, like in a loop. Using descriptive names is very much part of the Cocoa idiom and will help you a lot in the long run.)