Why is self.navigationItem.hidesBackButton not working? - iphone

I have a UIViewController that is pushed onto a UINavigationController and is currently displayed. When I go to start some asynchronous task inside the view controller, I can set hidesBackButton on self.navigationItem to YES, and the back button is hidden correctly.
As soon as the task is finished, and I set hidesBackButton back to NO (on the UI thread, I might add, I've made sure of this), nothing happens. The back button remains hidden.
Has anyone seen this before? What drives me especially crazy is that in my application (the same application), in a different UINavigationController hierarchy, the exact same code works correctly!

Are you calling hidesBackButton = NO from a thread? All UI operations should be done on the main thread, otherwise they won't have any effect.

i have not been able to replicate your problem on my machine. however, i faced a similar issue with tableviews even when i was updating my ui on the main thread. but calling setNeedsDisplay fixed that issue.
Can you try this and see if this works:
[self.navigationController.navigationBar setNeedsDisplay];
I guess this should work, you need to do the same, BUT ON THE NAVIGATIONBAR instead. please let me know if this worked - as i cannot test my solution because i never get this problem :-)

Have you tried forcing the view to refresh by calling setNeedsDisplay?
Maybe the OS is not picking up the changes instantly and you need to force it.

Have you tried using the setHidesBackButton:animated: method instead? Perhaps that has a slightly different behavior.

In my case I simply had to give a title to the view, as in:
self.navigationItem.title = #"Menu";
Marinus

I have had a similar issue recently. I tried literally everything I found in SO and other forums- nothing worked.
In my case there was a modally shown UINavigationController with a simple root controller which would push one of two view controllers (A and B) on top of the controller stack when the button A or B was pressed, respectively. Controller B was the one which was not supposed to show the back button. But still, sometimes it did, sometimes it didn't.
After hours of debugging, I managed to track it down. Controller A was a UITableViewController. Each time I selected a cell in this controller, the delegate would pop Controller A off the stack. BUT. I made use of a UISearchDisplayController as well. Turned out that popping the view while the search controller was still active messed up something in the navigation controller that made it impossible to hide the back button in Controller B afterwards (well, it eventually stayed hidden between viewDidLoad and viewDidAppear: but then it always turned visible).
So the solution (rather workaround) was adding this line to where Controller A was dismissed:
controllerA.searchDisplayController.active = NO;
// ...
// [self.navigationController popViewControllerAnimated:YES];
Hope this spares someone a couple of hours.

Related

UITabViewController, viewWillAppear does not invoked?

New to iOS, kindly bare if the question is very basic? When I press the tab button multiple times, it is not invoking viewWillAppear function? If I am wrong, then which function gets invoked, each time a tab button is pressed being on the same tabview?
You are correct, viewWillAppear is a little special, it is usually called automatically but in some cases including when you are adding a view controllers view manually (view addSubview:), and also when adding this as a view controller to a UITabViewController it doesn't get messaged.
This however is only for the root view, as you navigate (maybe with a navigation controller) back and forth, that root view's viewWillAppear will get triggered as some point.
In short, if you need to implement something in viewWillAppear in these cases, you should message it yourself when you know it's going to be presented. You can handle this case in your view controller, check out the following article about the matter:
http://www.touchthatfruit.com/viewwillappear-and-viewdidappear-not-being-ca
Good luck.

UINavigationController issue. viewWillDisappear/viewDidDisappear of view controllers not call on application startup

I am writing an application(for iPhone) that uses the UINavigationController to show different view via pushViewController. I am trying to push multiple UIViewController immediately upon application startup without user interaction. When an user open up the application, they will be looking on a screen with a navigation bar on the top that already have a back button.
The problem I am experiencing is that viewWillDisappear and viewDidDisappear of a ViewController does not always get call if another ViewController is pushed on top of it immediately upon application start up without user interaction. If the ViewController being pushed on top is activate by the user with a button click, then viewWillDisappear and viewDidDisappear of the view being cover always get call.
Note. viewWillAppear and viewDidAppear of the covered view controller always get called.
In the code that is experiencing the problem, I am pushing the new view controller in viewDidAppear of the previous ViewController.
Anyone know what I might be doing wrong or if the behavior I am seeing is expected?
The problem I am experiencing is that viewWillDisappear and viewDidDisappear of a ViewController does not always get call if another ViewController is pushed on top of it immediately upon application start up without user interaction.
Sound pretty sensible to me. After all, the "covered" view never appeared on screen, so it cannot disappear. I would expect this behavior.

Weird Scrolling Issue Using UITableView

I am a working UINavigationController pushing two different UITableViews on and off the stack. All of my functionality is working correctly except scrolling. When either table is scrolled above the top row, or below the bottom row, it stays there exposing the margin above/below the table. I am looking for the table to "bounce" back so that only the table is visible and not the white space area beyond - just like any other iPhone app.
One of my UITableViews is being loaded by NIB and the other is being created programatically - both of which have the exact same result. I have tried all the bounce and scrolling settings in the Nib, but nothing seems to work.
Can someone tell me what I am doing wrong? Please let me know if I can be more specific in detailing my problem.
Thanks,
-Scott
I wanted to add a bit more information since I am still unable to figure out this problem. I should point out that the app I am trying to add this NavigationController to is NOT the main view. I have a SettingsViewController that uses a Nib to get loaded. Within that controller, I am wanting to create a new navigation based set of views. So From inside my SettingsViewController, if the user taps a button, I...
Within my button selector:
TextListViewController *controller = [[TextListViewController alloc] initWithStyle:UITableViewStylePlain];
controller.title = #"Test Table View";
navController = [[UINavigationController alloc] initWithRootViewController:controller];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:navController animated:YES];
[controller release];
The above code works perfectly in every way except scrolling. I am able to process all the delegate methods and push new controllers onto the stack. The ONLY issue I am having is that the root controller (and every other controller pushed) will not scroll correctly.
In every case, it allows me to scroll past the top and bottom of the table and does not snap back like it should. It also does not have a free-wheel feel to it when you flick scroll. Instead, it stops as soon as you remove your finger no matter how fast you scrolled. Very odd.
I have tried creating another project using the Navigation template - and of course it works fine - but the template assumes the NavigationController's view is attached all the time - which is not my case. I am creating all of this on the fly within a standard ViewController.
Thanks for your help Kevin, but I am still looking for some help here.
Thanks,
Well, I fixed the issue and it had nothing to do with how I was adding the UITableView to the UINavigationController or in how I was instantiating the view. It turns out it was completely unrelated to any of that.
I am using a tight while loop to process my game loop. I needed a way to process OS messages in the loop to keep touches and other events from being blocked. Here is what I was using in the middle of my loop:
while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, TRUE) == kCFRunLoopRunHandledSource);
Up until this scrolling problem, I had no problems with this implementation at all. For whatever reason, this is breaking the scrolling functionality with UITableView.
I changed my game loop so the above code is not needed and everything started working.
Thanks...
Your UITableView is a UIScrollView, whose property bounces is the key. Here's the reference. By default it is YES, though. Did you check this one?
Your question and comments seem to suggest that you are attaching a UITableView to your UINavigationController in some way other than by pushing a UITableViewController. If you're not doing it that way, you really should.
In general, if you can push a child view controller onto a UINavigationController, then you can also use the child view controller by itself just to test it out. Try making your UITableViewController's view the root view of your window, and see if that does what you expect. That way you can isolate the problem to either the table view part or the navigation part.
I've been having the same issue as you which as you state above was related to the way you were calling the runloop:
while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, TRUE) == kCFRunLoopRunHandledSource);
Seems like DefaultMode is the wrong thing to be waiting on, I changed my code to wait on UITrackingRunLoopMode and the TableView worked correctly. In my case I used:
[[NSRunLoop currentRunLoop] runMode:UITrackingRunLoopMode beforeDate:[NSDate distantFuture]];
And everything worked correctly.

UISplitViewController and complex view hierarchy

I'm doing an iPad tech demo and I'm running into a serious technical problem.
I have an app concept that leverages UISplitViewController, but NOT as the primary controller for the entire app.
The app flow could be described roughly as this:
Home screen (UIViewController)
List->Detail "Catalog" (UISplitViewController)
Super Detail Screen (UIViewController but could conceivable also be a child of SplitView).
The problem is in the flow between Home and Catalog. Once a UISplitViewController view is added to the UIWindow, it starts to throw hissy fits.
The problem can be summarized at this:
When a UISplitView generates a popover view, it appears to then be latched to its parent view. Upon removing the UISplitView from the UIWindow subviews, you will get a CoreGraphics exception and the view will fail to be removed.
When adding other views (presumably in this case, the home screen to which you are returning), they do not autorotate, instead, the UISplitView, which has failed to be removed due to a CG exception, continues to respond to the rotation instead, causing horrible rendering bugs that can't be just "dealt with". At this point, adding any views, even re-adding the SplitView, causes a cascade of render bugs.
I then tried simply to leave the SplitView ever present as the "bottom" view, and keeping adding and removing the Home Screen from on top of it, but this fails as SplitView dominates the Orientation change calls, and Home Screen will not rotate, even if you call [homeScreen becomeFirstResponder]
You can't put SplitView into a hierarchy like UINavigationController, you will get an outright runtime error, so that option is off the table. Modals just look bad and are discourages anyway.
My presumption at this moment is that the only proper way to deal with this problem is so somehow "disarm" UISplitViewController so that it can be removed from its parent view without throwing an unhandled exception, but I have no idea how.
If you want to see an app that does exactly what I need to do, check out GILT Groupe in the iPad app store. They pulled it off, but they seem to have programmed an entire custom view transition set.
Help would be greatly appreciated.
Apple states:
The split view controller’s view
should always be installed as the root
view of your application window. You
should never present a split view
inside of a navigation or tab bar
interface.
This does mean it should be root view and not subview of another view. Even though they add:
You should never present a split view inside of a navigation or tab bar interface
That does not mean you can add it as a subview of any other controller either. (sorry)
I have a feeling that what you are experiencing is the byproduct of trying to do so. I am actually surprised that GILT Groupe's app did not get rejected. Apple has a tendency to enforce these HIG guidelines rather strictly lately. They (as you found out already) cause a rather nasty runtime error when you attempt to add them to a NavigationController.
I've solved this for myself... actually worked around... by presenting all other possible full screen views as modals of the SplitView...
This is an unsavory way of doing things in my book, but Apple leaves you little choice if you want to leverage a SplitView only "sometimes" within an app.
I had some success by creating a second UIWindow. I associate the UISplitViewController with that, and switch it out with the main window when I want to show the splitview. It seems to work they way I wanted, except for a slight delay in rotations and a log message about "wait_fences".
Unless your developing for jail-broken devices then bending apples rules/wishes isn't a good idea. Like Jann and Jasconius state above this means keeping a splitView controller view root, not over-using modals (vague) and not using multiple windows.
Also, the Gilt app is only available in the US
I'v been trying to find a solution too and have ended up programatically removing views from the window like Tuannd talks about but the landscape rendering bug is unforgivable.
#Jasconius, What is the max number of modals are you are presenting at any time?
I am struggling with this same issue. I've been trying various things poking at the UISplitViewController as a black box and see how it reacts.
I seem to have come up with a solution to my case which seems to be working satisfactorily.
The key appears to be that the first view added to the UIWindow is the only view properly initialized. All the problems I've had tend to stem from incorrect notification of the orientation of the device. The first view added, apparently has this correctly configured.
In my case I didn't want the UISplitView as the first view. The following is working for me.
The app delegate application:didFinishLaunching method is special. Adding the view to the UIWindow must occur here. If it is done elsewhere it does not get configured properly.
Essentially the magic sauce, is have the split view be the first view added to the window. Its then ok to remove it as long as you retain the UISplitViewController. From then on you can swap other views in and out, including the UISplitView and most things seem to be ok.
I've still run into a few issues. Popovers on views other than the split view are confused on the views frames and location of toolbar buttons and will display in the wrong location. I place then in a specific location and that seems to handle that case.
If a popover on the split view is still displayed, and you try to view another view, the orientation of the second view is confused and shows up sideways. If that view is accessed before the popup is displayed, all is well. I've fixed this my manually dismissing the popover before switching to any other view.
Here's the code if it helps. All the controllers are instance variables of appDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// This also seems to work as good magic. Seems to set orientation and size properties that persist.
[window addSubview:splitViewController.view];
[splitViewController.view removeFromSuperview];
[self switchToNewViewController:firstController];
[window makeKeyAndVisible];
return TRUE;
}
- (void)switchToNewViewController:(UIViewController *)newViewController {
[popoverController dismissPopoverAnimated:FALSE];
if (newViewController != currentViewController) {
[currentViewController removeFromSuperview];
currentViewController = newViewController;
[window addSubView:newViewController.view];
}
}
Just wanted to say that I was running into these same issues, found this forum topic, and followed the advice from g051051 above. This is working perfectly for me. I am not seeing any glitch, and no messages about wait_fences in the console of the device.
I simply used IB to create two UIWindow objects in the main XIB, created as normal the UISplitViewController and then also an instance of my other controller derived from UIViewController (which I am using for full screen display). I have simply hooked them up by attaching the rootViewController for each UIWindow to its appropriate controller.
In application:didLaunch...: method I can decide which window to send the makeKeyAndVisible method and which to set to hidden. When the user want to switch back and forth I simply have to send makeKeyAndVisible to one and set the hidden property on the other, that's all there is to it.
As indicated all of the rotation related messages are sent to each controller appropriately, regardless of which one is associated with the currently visible window.
Anyway, works great for me, and actually quite easy to set up.

UINavigationController and switching next-prev item within one level

could anyone help me with the next question. I am working on an app with the navigation similar to Notes app has. The problem I faced i how to implement navigation within one level and keep root view as the previous screen. So the scenario I need to achieve:
1. Drill down from the top level list to the detailed view.
2. From this detailed view navigate to the detailed view of the next/previous item.
3. Back button should lead to the root view.
I've tried several approaches and no-one worked fine for me. The most right solution from my point of view is to create a middle controller between the root and detailed controllers to handle this next/prev redirects. But the main problem here is that switching from one detailed view to another the navigation pane stays the same, so it's not involved in the animation, whether Notes app work right in this case.
Could you point me how it' better to arrange controllers interaction in this case?
Thanks in advance. Sorry for quite big post, was trying to be as informative as possible.
Best regards,
Roman
I think the best implementation is this way:
Say you have a Note class where the data is stored.
You could have two view controllers, one with the list of notes and one with the details.
Both of these can be added to a navigation controller.
Then, when you want to switch to the next note, you retrieve that object somehow and assign it to your detail controller with a -setNote: call. That method will be responsible for changing what the outlets are displaying.
I've resolved this question - it's enough to pop current view and push new one, created for the next object in the list. The only think to remember is to save the reference to the navigation controller before the pop call, because after it self.navigationController becomes nil.
Yeah, it's misbehaving badly. Jongsma's reply is the best. In case you wanted to re-create controllers, I've found this to work:
[parent performSelectorOnMainThread:#selector(showMyItem:)
withObject:myNextItem
waitUntilDone:NO];
[self.navigationController popViewControllerAnimated:NO];
performSelectorOnMainThread delays execution of the method until the next run of the main loop, and by then the previous controller is removed already and navigation controller is in a stable state.