I have a UIViewController on the More... tab. In viewDidLoad, I set toolbarHidden to NO, and the toolbar appears. However, if I select another tab from More..., then when I return to my original UIViewController, the toolbar is hidden (toolbarHidden is YES). I've checked to be sure it's the same view controller object.
My workaround is to set toolBarHidden to NO in viewWillAppear.
Is this documented behavior that I'm missing or is it a bug?
UPDATE
Here is some fairly trivial sample code. It turns on the toolbar in viewDidLoad and offers a button to toggle the toolbar on and off.
// Test_VC.m
#import "Test_VC.h"
#implementation Test_VC
- (void)viewDidLoad {
[super viewDidLoad];
// for testing to turn the toolbar on and off
UIBarButtonItem *sampleButton = [[UIBarButtonItem alloc]
initWithTitle:#"Toggle TB"
style:UIBarButtonItemStylePlain
target:self action:#selector(toggleToolbar:)];
self.navigationItem.rightBarButtonItem = sampleButton;
// the toolbar should re-appear whenver this view controller is selected
self.navigationController.toolbarHidden = NO;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// if on More..., this line will keep toolbar visible
//[self.navigationController setToolbarHidden:NO animated:NO];
}
- (void)toggleToolbar:(UIBarButtonItem *)sender
{
[self.navigationController setToolbarHidden:!self.navigationController.toolbarHidden animated:YES];
}
#end
To test:
Create a project with numerous tabs, each of which is an instance of this view controller, Test_VC. Have enough tabs so that at least two of these controllers are on the More tab.
Run the app.
Go to the More tab.
Select a "Test_VC" on More (I refer to this as "Test_1"). Notice that the toolbar appears.
Select another Test_VC from More. (I refer to this as "Test_2"). Notice that the toolbar also appears.
Touch "Toggle TB". Notice that the toolbar goes away on Test_2.
Touch More.
Reselect the first Test VC ("Test_1"). Notice that the toolbar is no longer there.
Repeat these tests using Main tab view controllers. Notice that there is no interactions with any others.
In step 4, the toolbar for Test_1 was present. In step 6, the Test_2 toolbar is removed. However, in step 8, the Test_1 toolbar is gone, too, but no change was made to the Test_1 VC.
This demo turns the toolbar on and off via the Toggle TB button. In my app, some of my view controllers simply hide the toolbar in viewDidLoad and some don't. This demo code could be modified to make two view controller versions: some that show the toolbar in viewDidLoad and some that hide the toolbar in viewDidLoad (no need for the Toggle button).
If these view controllers are main tabs (step 9), then each operates independently of the others, as expected.
What I have found: Toolbar visibility on all view controllers launched from More is set by the most recent More view controller that executes viewDidLoad. Thus, viewDidLoad is strange for More tab view controllers.
It's as if there's only one toolbar managed by More. I do not understand this behavior.
I'm kind new in all the Objective C stuff so try to understand :)
i want to use a navigation button to move to the next view but without any animation.
and i need to save the Current info in the source view and be able to have back button in the destination view.
in modal style i can't save my info and when i'm back to the screen all the info disappeared.
in push style i can use the back button but i cant stop the animation.
in custom style i didn't make it either
help?
btw - please try to give specific answer so i can understand :) THNKS
If you are having navigation controller you can easily do this by pusing viewController to current navigation stack. If you don't want to navigate with an animation you can pass NO to animated value like this.
[self.navigationController] pushViewController:controller animated:NO]; If you want pass YES.
Now when ever you will push view controller you will get a back button, its navigation controller duty to create it for you. Title of this button will be same as source view controller title, if there is no title button title will be back.
Now how you can do that, add a button to your source view controller and assign an action. Call this code in action of that button.
You can add button by interface builder and can set action by clt+drag to source file. Or you can add that button by code.
if you want to do it by code add this in viewDidLoadMethod
UIButton *next = [UIButton alloc] init];
[next setTarget:self action:#selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside];
And then add btnClicked method in
- (IBAction)btnClicked:(id)sender{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
Second* controller= (Second*)[storyboard instantiateViewControllerWithIdentifier:#"Second"];
[self.navigationController] pushViewController:controller animated:NO];
}
Edit
It would be better first you go to this link and read some basics.
You can use a UINavigationController to posh/pop all your view controllers. You can control the animation using
[[self navigationController] pushViewController:controller animated:(NO)];
or
[[self navigationController] popViewControllerAnimated:controller animated:(NO)];
I hope that helps. Here is a tutorial on using UINavigationController
http://bharanijayasuri.wordpress.com/2012/12/19/simple-uinavigationcontroller-tutorial-2/
I'm trying to achieve something similar to user of this post:
Xcode/iOS: How to hide Navigation- AND ToolBar on scroll down?
I'm able to hide (or unhide using NO) the navigation bar successfully using the code:
[[self navigationController] setNavigationBarHidden:YES animated:YES];
However, when I attempt to hide the toolbar using the code:
[[self navigationController] setToolbarHidden:YES animated:YES];
Nothing happens. I then noticed when unhiding the toolbar that I received an additional blue toolbar that I didn't realize existed. This screenshot shows this:
Screenshot
I do not want the blue bar. What I am trying to do is hide or unhide the Black toolbar with the icons on it. (the UITabBar).
I think what I need to do is somehow I need to access the navigation controller of one of the parent view controllers and call the setToolbarHidden on the navigation controller of that view. However, I can't seem to figure out how to do this.
I've tried the following and all seem to have no effect:
[[[self parentViewController] navigationController] setToolbarHidden:YES animated:YES];
or
[[[[[[UIApplication sharedApplication] delegate] window] rootViewController] navigationController] setToolbarHidden:YES animated:YES];
My view controller storyboard consists of the following:
The InitialViewController is a TabBarViewController. It contains three children. One of those children is a UINavigationController. This navigation controller gets several UITableViewController pushed onto it, and eventually a UIViewController is pushed. This last UIViewController is what is seen in the screenshot.
Rough Layout:
TabBarViewController
UIViewController
UITableViewController
UINavigationController
UITableViewController
UITableViewController
UITableViewController
UIViewController
I've tried using
[self parentViewController] parentViewController] parentViewController] ...
to attempt to get back to the top, but this hasn't seemed to work either.
Any suggestions?
I think the problem here might be related to UITabBarController not having a UIToolbar. The setToolbarHidden: method will only apply to the UINavigationController's built-in toolbar (see Apple's documentation). If it's the UITabBarController's tab bar that you actually want to hide, take a look at this post which links to a method using UIView animations directly on the UITabBar.
I am making an application that has a UITabBar and it works just fine, however I want to add a first screen which contains some instructions and a logo, then a button and when said button is pressed to show the UITabBar I've tried for a couple of days to do this on my own with no luck, I'm not even sure if it can be done, could anyone help me out? or give me a lead?
Try setting up the tab bar application, and then showing the first screen over it as a modalViewController. Inside your main app's viewController:
FirstViewController *firstViewController = [FirstViewController alloc] initWithNibName:#"FirstView" bundle:nil];
[self presentModalViewController:firstViewController animated:NO];
[firstViewController release];
When the button is pressed inside the modal view, call:
[[self parentViewController] dismissModalViewControllerAnimated:YES];
to get rid of it. You can adjust the animation style for the dismissal, or use NO instead to have it just disappear.
What does this error indicate:
"Popovers cannot be presented from a view which does not have a window."
the thing that saved my life:
if (self.view.window != nil)
[popoverController presentPopoverFromRect:CGRectMake(44, yCoord, 111, 111) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
by adding if condition it doesn´t crash anymore. I don´t really get it because the presentPopoverFromRect function is ALWAYS called. There is no situation where window would be nil but anyway it did the trick.
edit: I have this code in viewDidAppear.
Nevertheless in most cases it's enough to move presentPopoverFromRect to viewDidAppear or didMoveToWindow but in my case for some reason the if condition was necessary.
the view you're adding the popover to has to already have been added to a window with the "addSubview:" method.
Try waiting until
- (void) didMoveToWindow
is called for the view and then load the popover
I got this problem.
I had a UITabBarController as the detail view, and I set the barButtonItem as the leftBarButtonItem on all three navigation controllers in the tab bar.
vcChart.navigationItem.leftBarButtonItem = barButtonItem;
vcAnalysis.navigationItem.leftBarButtonItem = barButtonItem;
vcTechnicals.navigationItem.leftBarButtonItem = barButtonItem;
Turns out only the last one added is valid, and the previous two would throw the exception when tapped on.
To fix it, I only set the leftBarButtonItem for the visible view controller, and just switched the barButtonItem to the visible view controller every time the user switched tabs.
Just encountered this issue. Turned out that the inView: parameter was using an IBOutlet that wasn't connected in IB. Thus, an attempt was made to launch the popover in nil. That doesn't work.
So, make sure you are using a valid view.
There are many ways to get to this error. Basically you need to wait to call the presentPopover command until your calling view is added to a window. I did it this way.
- (void)viewDidAppear:(BOOL)animated
{
[self methodThatDisplaysPopOver];
}
My presentPopoverFromRect call is inside my methodThatDisplaysPopOver function.
You could protect every presentPopover call like MobiMaciek suggests with this.
if (self.view.window != nil)
[popoverController presentPopoverFromRect:CGRectMake(10, 10, 100, 100) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
However, I think it would be better to understand when self.view.window gets assigned and make sure that you present you popover after the view has a window.
I received the same error message when assigning the same UIBarButtonItem to multiple navigation items as did Lewis. My example was slightly more complicated as I was using a UISplitViewController.
In my RootViewController I have an array of arrays to accomplish multiple sections within my table. Each time that the user clicks a row in the table, a new "detail" view controller is placed in the right pane of my splitViewController. Prior to setting the leftBarButtonItem = nil, I would receive a segfault after 3-4 clicks of the "Menu" button with the same error as a111. I updated my code to actually retrieve the previous detail view controller and set the leftBarButtonItem item to nil.
allData is my NSMutableArray that contains several other NSMutableArrays as objects.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Retrieve the new detail view controller
UIViewController *detailViewController = [[self.allData objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
// Add the detail view controller to a navigation controller and set the bar style
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
detailNavigationController.navigationBar.barStyle = [[NSUserDefaults standardUserDefaults] integerForKey:#"UIBarStyle"];
// Retrieve previous detail view controller and remove the leftBarButtonItem
UINavigationController *previousDetailNavigationController = [splitViewController.viewControllers objectAtIndex:1];
UIViewController *previousDetailViewController = [[previousDetailNavigationController viewControllers] lastObject];
previousDetailViewController.navigationItem.leftBarButtonItem = nil;
// Update the split view controller's view controllers array.
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, detailNavigationController, nil];
splitViewController.viewControllers = viewControllers;
[detailNavigationController release];
[viewControllers release];
// Dismiss the popover if it's present.
if (popoverController != nil) {
[popoverController dismissPopoverAnimated:YES];
}
// This sets the left bar to nil when in landscape and equal to "Menu" when in portrait.
// We need to remove rootPopoverButtonItem from the previous viewController...
detailViewController.navigationItem.leftBarButtonItem = rootPopoverButtonItem;
}
The error message was slightly deceiving at first but the answers above helped me out. I wonder why I could click the "Menu" button up to 3-4 different times before the segfault... I'll investigate further.
This error also occurred when the inView: Parameter is incorrect - to test try self.view
yes, you are right but still we can add subview from parent class in it. so it can be represented from a view which have a window:
[popoverController.contentViewController.view addSubview:mySubView];
I had the same error message as the OP, in a very similar situation to that reported by TPoschel, except I had a split view controller with an embedded tab bar controller in the detail pane, and a navigation controller within this. The bar button item is added as the navigation bar leftBarButtonItem.
Only on iOS5.0 (not 5.1) does it seem to require you invalidate the bar button item on the tab bar you are leaving by setting it to nil. Before then adding the bar button to the navigation bar on the tab you are going to.
If I don't do that, from debugging my own code, the window property of the bar button item stays set to nil, and causes the exception, on returning to a screen you'd previously been to. I'm guessing as a side effect of setting the leftBarButtonItem in the navigation item, it goes off and sets the frame. But it doesn't seem to bother unless the button is different from what is currently set there. Hence, the need to set it to nil when leaving a tab, even though it is technically the same button that's being passed around.
I would upvote TPoschel's answer, except SO won't let me.
I had a problem like this. Received this message when clicking a customized UIBarButton item that invoked a selector method with did performSeque.
The problem was my segue was still attached to the UIBarButton item. It should have been attached to the main view of of the view controller. Changed this and worked fine.
P.S., all this got started because I wanted to add and "info" button to my UIToolBar. This isn't one in the system provided list and should be.
There will be a view from which you asks to display your popover.The reason for this error is because you didn't made this view as a subview of the window.
[self.view addSubview:displayPopOverVC];
where displayPopOverVC is the view controller from which the popOver appears
i had the same problem, after adding PresentPopOver in viewDidAppear this was solved
- (void) viewDidAppear:(BOOL)animated{
CGRect popoverRect = screenBounds;
popoverRect.size.width = MIN(popoverRect.size.width,0) ;
popoverRect.origin.x = screenBounds.origin.x;
[popoverController
presentPopoverFromRect:popoverRect
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
this was happening as inView:self.view should be called after viewDidLoad as suggested by #hey68You and MobiMaciek..
I replaced
[actionSheet showFromBarButtonItem:self.navigationController.navigationItem.leftBarButtonItem animated:YES];
with
[actionSheet showInView:self.view];