I want to create a modal view with the navigation item (right view on my screenshot) and I want it to have a 'back button'. My app is TabBar application and I don't want this view to have a tab bar, but I want to load a previous view (left view on my screenshot) with a segue similar to the type "push".
I can only create push segue to provide right navigation back to the view on the left, if it's loaded as a modal view, the NavigationBar & TabBar are gone. Any workarounds for this?
Thanks in advance!
Just put a Navbar on the new view with a bar button item. Create an action for the bar button item by control dragging from the bar button item to the .h of the view controller. You can then use a delegate and protocol method to tell the first controller when the button has been pressed and have it use [self dismissModalViewControllerAnimated:YES];
So in your second view create a protocol with a method done, like this:
#protocol SecondViewControllerDelegate <NSObject>
-(void) done;
#end
#interface SecondViewController : UIViewController {
...
id delegate;
}
...
#property (nonatomic, assign) id<SecondViewControllerDelegate> delegate;
-(IBAction)done:(id)sender; //this will be linked to your nav bar button.
#end
then in your action from your button call this:
-(IBAction)done:(id)sender{
[self.delegate done];
}
Your first view controller will need to implement the protocol <SecondViewControllerDelegate>
then in your first view controller, set it up as a delegate for your second view controller before you segue.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"Second View Modal Segue Identifier"])
{
SecondViewController *viewController = segue.destinationViewController;
viewController.delegate = self;
}
}
lastly, catch the done call from the delegate in your first view controller:
-(void) done
{
[self dismissModalViewControllerAnimated:YES];
}
That's how I have done it. If you don't have a lot of experience with protocols and delegates it may seem confusing at first, but it has worked well for me.
You will need to wrap your right hand side view controller in a new navigation controller. In IB, select it and choose the menu item Editor -> Embed In -> Navigation Controller and IB will show a nav bar which you can customize to your heart's content.
Related
I have been attempting to present a modal view from one of my views that I implemented following the following iDev tutorial/source code. The custom tab bar manages view by inserting them as subviews beneath the tabBar so when I call the modal view to be displayed from the subview it is covered up at the bottom by the tab bar. I have switched the following code to above the tab bar sub view which allows the whole screen to be displayed but it still creates animation problems.
// Set the view controller's frame to account for the tab bar
viewController.view.frame = CGRectMake(0,0,self.view.bounds.size.width, self.view.bounds.size.height-(tabBarGradient.size.height));
// Set the tag so we can find it later
viewController.view.tag = SELECTED_VIEW_CONTROLLER_TAG;
// Add the new view controller's view
[self.view insertSubview:viewController.view aboveSubview:tabBar];
http://idevrecipes.com/2011/01/04/how-does-the-twitter-iphone-app-implement-a-custom-tab-bar/
Please help this is driving me nuts!
So, i think you are presenting modal view from your subview like this:
[self presentModalViewController:someController animated:YES];
If so, your controller is presenting on current view (self) and if current view situated beneath other views - modal controller will be presented under them too.
You should get a reference to main parent view, so you could call presentModalViewController there.
In big projects with many subviews it's handy to have a reference to main view, I did it like this:
in MyAppDelegate #interface I have:
{
UINavigationController *navController;
}
+ (UINavigationController *) navController;
#property (nonatomic, retain) IBOutlet UINavigationController *navController;
And in the implementation:
// Used for showing modal windows over tabbar
static UINavigationController * _rootController = nil;
+ (UINavigationController *) navController {
return _rootController;
}
#synthesize navController;
in didFinishLaunchingWithOptions:
_rootController = navController;
(In this example my top view - navigation controller)
So anywhere in application I can call:
[[MyAppDelegate navController] presentModalViewController:someController animated:YES];
I have a main view.
From that main view I show a modal view (MV1).
MV1 may show another modal View (MV2).
From MV2, I may show another modal view (MV3).
All that MV are shown animated.
What I want, is to be able to first display (animated) the next modal view (MVx+1) before "killing" the previous one (MVx).
If I dismiss (animated) MVx before showing MVx+1 : MVx+1 does not appear.
If I dismiss (non-animated) MVx before showing MVx+1 : MVx-1 is seen.
If I show MVx+1 before dismissing (non-animated) MVx : MVx+1 does not appear.
How may I do ?
Some code sample would help if you have time, but just a detailed explanation would be enough.
According to the Apple docs, the accepted way to dismiss modal views is by letting the parent controller (i.e., the view controller that created the modal view) do the dismissing. The best way to do this is by setting the parent controller as the delegate of the modal view controller. The idea here is that the modal controller tells its parent that it's ready to be dismissed, and the parent decides what course of action to take from there.
In order to do this, you have to create a delegate protocol for the modal view controller that the parent controller implements. In your case, you can create a protocol at the top of each of your .h files for your modal views to do this (or a single protocol in a separate file if all of the modal views can use the same method for dismissal). For example:
#protocol MYModalViewDelegate <NSObject>
-(void)dismiss;
#end
Next, in each of your modal view controllers, create an instance variable for the delegate:
#interface MYModalViewController1 : UIViewController {
id<MYModalViewDelegate> delegate;
}
When you display a modal view from a current view controller, set the current controller as the delegate.
MYModalViewController1 * mvc1 = [[MYModalViewController1 alloc] initWithNibName:#"MYModalViewController1" bundle:nil];
mvc1.delegate = self;
[self presentModalViewController:mvc1 animated:YES];
[mvc1 release];
When you want to release the current modal controller, have the modal view controller call the appropriate protocol method on its delegate:
[self.delegate dismiss];
Now, the delegate can handle where to go next. In your case, you can close MV2 automatically when MV3 closes by calling [self.delegate dismiss] in MV3, then implement dismiss in MV2 as:
-(void)dismiss {
[self dismissModalViewControllerAnimated:YES];
[self.delegate dismiss];
}
I need to pop up a quick dialog for the user to select one option in a UITableView from a list of roughly 2-5 items. Dialog will be modal and only take up about 1/2 of screen. I go back and forth between how to handle this. Should I subclass UIView and make it a UITableViewDelegate & DataSource?
I'd also prefer to lay out this view in IB. So to display I'd do something like this from my view controller (assume I have a property in my view controller for DialogView *myDialog;)
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"DialogView" owner:myDialog options:nil];
myDialog = [nibViews objectAtIndex:0];
[self.view addSubview:myDialog];
problem is i'm trying to pass owner:myDialog which is nil as it hasn't been instantiated...i could pass owner:self but that would make my view controller the File's Owner and that's not how that dialog view is wired in IB.
So that leads me to think this dialog wants to be another full blown UIViewController... But, from all I've read you should only have ONE UIViewController per screen so this confuses me because I could benefit from viewDidLoad, etc. that come along with view controllers...
Can someone please straighten this out for me?
There is no such thing as a view controller being on the screen; its view is on the screen. With that said, you can present as many views as you want on the screen at once.
I would create a new view and view controller. You would not make a UIView be a UITableViewDelegate, you make a UIViewController be a UITableViewDelegate. But instead of doing that manually, instead make your new view controller a subclass of UITableViewController, if you're using iPhone OS 3.x+. You can then present this view controller modally.
You probably want to give the user a chance to cancel out of the selection. A good way to do that is to wrap your new dialog view controller in a UINavigationController and then put a "Cancel" button in the nav bar. Then use the delegate pattern to inform the parent view controller that the user has made their choice so you can pop the stack.
Here's what the code will look like inside your parent view controller, when you want to present this option dialog:
- (void)showOptionView
{
OptionViewController* optionViewController = [[OptionViewController alloc] initWithNibName:#"OptionView" bundle:nil];
optionViewController.delegate = self;
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:optionViewController];
[self.navigationController presentModalViewController:navController animated:YES];
[navController release];
[optionViewController release];
}
Your OptionViewController .h will look like this:
#protocol OptionViewControllerDelegate;
#interface OptionViewController : UITableViewController
{
id<OptionViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id<OptionViewControllerDelegate> delegate;
#end
#protocol OptionViewControllerDelegate <NSObject>
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSString*)selection;
// or maybe
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSUInteger)selection;
// etc.
#end
Your OptionViewController.m will have something like this:
- (void)madeSelection:(NSUInteger)selection
{
[delegate OptionViewController:self didFinishWithSelection:selection];
}
Which has a matching method back in your original view controller like:
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSUInteger)selection
{
// Do something with selection here
[self.navigationController dismissModalViewControllerAnimated:YES];
}
There are plenty of examples throughout Apple's sample source code that follow this general pattern.
here is a iPhone programming beginner's question:
How do I get to another view by pressing a button in my main view?
I have the following function which is executed when I press a button, and debugging it, he passes there, but my "Warning" view does not show up:
-(IBAction) showWarningView:(id)sender
{
if(self.showWarning == nil){
WarningViewController *nextView = [[WarningViewController alloc] initWithNibName:#"Warning" bundle:[NSBundle mainBundle]];
self.showWarning = nextView;
[nextView release];
}
[self.navigationController pushViewController:self.showWarning animated:YES];
}
My main RootViewController looks like this:
#import <UIKit/UIKit.h>
#import "WarningViewController.h"
#interface RootViewController : UIViewController {
IBOutlet UIButton *button1;
WarningViewController *showWarning;
}
#property(nonatomic,retain) WarningViewController *showWarning;
-(IBAction) showWarningView:(id)sender;
#end
I am using the navigation control of a UITableViewController but what do I have to use to just simply show my other view when I press a button in a view-based application?
Thanks a lot!
edited
if you're using the navigation control of a UITableViewController, you probably have to push the view in you tableviewcontrollers navigation controller
this means you have pass the navigation controller of your tableviewcontroller on to your viewcontroller, then you just push it
e.g.
[self.tableViewControllersNavigationController pushViewController:self.showWarning animated:YES];
(for passing the tableViewController's navigationController on, you might have to create a delegate pattern)
Does the title in your navigation bar change? Maybe you don't have a UIView associated with your UIViewController inside your Interface Builder file.
Trying to mimic/copy the built-in address book, specifically the behavior when editing a contact or viewing an existing contact's Info from inside the Phone app. When you navigate to another tab, the editing state is reset and the "New Contact" or "Info" view is popped so that when you come back to the Contacts tab, you are back at the root table view.
I have most of this working inside viewWillDisappear using setEditing: and popToViewController: however I get strange behavior when the user navigates from the Info view to the table view using the back button. Even if I pop to the root table view controller, it seems to be using the default UITableViewController class and not my subclass (e.g. standard selection behaviors instead of my overrides to push the detail view.)
Any hints? IPD
Here's some code to illustrate:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// This is to clean up from the colored bar in detail view
self.navigationController.navigationBar.tintColor = nil;
// These are to match the behaviour of Contacts app
[self setEditing:NO animated:NO];
// This is the tricky part: works when switching tabs, but not when back button was going to pop anyway!!
[self.navigationController popToViewController:rootViewControllerForTab animated:NO];
}
The -viewWillDisappear: method is not the best place for modifying the view controller stack for your navigationController because it is triggered both when you switch tabs and when a view is pushed on top of it.
I played around with this a bit and found that the best place for this is in the -[UITabBarControllerDelegate tabBarController:didSelectViewController:] method. So, first you need to designate an object to be the delegate for your tab bar (I used the app delegate). Bind the delegate property of your UITabBarController to an object implementing the UITabBarControllerDelegate protocol in code or in Interface Builder.
Then, implement the -tabBarController:didSelectViewController: method. The trick now is how to tell when your "address book" tab is being switched to. I kept track of the view controller for the tab in question using a property of type UINavigationController (the root view controller for the tab). After binding the tab1NavController property to the actual instance using Interface Builder, it can be used to compare to the viewController parameter to see what tab was just selected.
#interface Pop2RootTabSwitchAppDelegate : NSObject
<UIApplicationDelegate, UITabBarControllerDelegate> {
UINavigationController *tab1NavController;
}
#property (nonatomic, retain) IBOutlet UINavigationController *tab1NavController;
#end
#implementation Pop2RootTabSwitchAppDelegate
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController {
NSLog(#"[%# tabBarController:%# didSelectViewController:%#]", [self class],
tabBarController, viewController);
if (viewController == tab1NavController) {
NSLog(#"viewController == tab1NavController");
[tab1NavController popToRootViewControllerAnimated:NO];
}
}