popViewController / viewWillAppear not animated in iOS 5 - iphone

I wasn't lucky with searching for this, so here we go ;)
I have a UIViewController with a custom UINavigationBar which pushes another UIViewController as subview.
Everything works fine except when I click the back button on the subview. The previews (first) view appears correctly, but not animated. The animation of the UINavigationBar is correct, only the views switch immediately.
The function - (void)viewWillAppear:(BOOL)animated of the first UIViewController gets called with NO for animated. This only happens when I test with iOS 5, not with iOS 4.
Does anyone know how to fix this?
Thanks for your help! Hannes
UPDATE 1
I just removed all custom code and just used the plain UINavigationBar (so no extra settings) and it still doesn't work with iOS 5. This is my code I use in the first ViewController to push the second ViewController:
[self.navigationController pushViewController:secondViewController animated:YES];
As I already mentioned - when I click the back button in the navigation bar on the second view the first view appears immediately without animation.
Any help would be appreciated! Thanks!
UPDATE 2
I feel like I'm getting closer to the issue, but still no solution:
I just added a custom UINavigationController where I just call [super popViewControllerAnimated:animated]. This get's called correctly (animated is YES) but the viewWillAppear of the first UIViewController gets NO as value for animated...

I was having a similar problem today where the UIViewController was getting NO in viewWillAppear, except with the standard UINavigationBar and UINavigationController.
It turned out to be due to manually calling viewWillAppear:YES somewhere it shouldn't have been. This item suggests that it can also be caused by calling the wrong super method somewhere (e.g. [super viewWillAppear:animated] instead of [super viewDidAppear:animated] inside of viewDidAppear).
As for using a custom UINavigationBar, I ran across this link today that may help your case: http://sloshire1.posterous.com/simple-fix-for-viewwillappear-in-ios5

Apple implemented official ways to create custom navigation bars in iOS 5. Unfortunately, they also broke most of the non-official ways of doing it in iOS 4. iOS 5 won't call drawRect for you anymore. You need to have two ways of doing it, one for iOS 5 and greater, using the new calls, and one for iOS 4 and earlier, using the old calls. Check out the documentation for custom navigation bars in iOS 5 for more info.

Did you try to remove all your custom code and go with the native navigation bar? Does the behavior stay the same? This way you can check if your custom bar messes with the transition.

Related

editing is disabled in UITextfield in a static TableView - iOS 6.0 only

I have two UITextFields inside a static TableView (in a Storyboard) for loggin in. The first time go to the tableView via a modal segue, the TextFields are editable as they should be. When I then go back and forth to that view again, the TextField seem to be non-editable. If I read out the textField.isEnabled, it is set to YES.
Even more strange: this behavior is only with iOS 6.0. On iOS 5.0 everything is fine.
I've already added this in viewWillAppear
TextField.userInteractionEnabled = YES;
I've also commented out all of my viewDidLoad and viewWillAppearCode, but still no avail.
Any idea is welcome.
EDIT: after closing and restarting the app, I once again have one shot. I looks like something is screwed, once I opened and closed the view.
I actually had a very similar problem with my TextFields in one view not being editable after doing a back and forth segue with another view. For me, it was a problem with the first responder status, and things started working fine after adding the following in prepareForSegue:
[self.textFieldName1 resignFirstResponder];
Hope that helps.

Making a UITabView show the second level of a navigation controller by default

(Apologies for not being able to embed my images yet).
Using iOS storyboards, I have a UITabBarController with a UINavigation Controller/UITableView(1) embedded in it. This UITableView(1) then calls another UITableView(2):
What I'm trying to do is to make UITableView(2) appear when the Tab Bar is changed to that tab, and then have the UINavigationBar left arrow button exist to get back to UITableView(1).
The existing functionality I can think of which does this is the iPhone Mail app, where when you launch it you see your Inbox, and you can hit the left-arrow Mailboxes button to get back to your mail box list.
I've tried attaching the tab bar directly to UITableView(2) but it doesn't work as expected, there's no left arrow back button to get back to the previous view when the app is run.
I've also tried adding a navigation controller to that UITableView(2) and the Navigation controller correctly appears, but still without any back button:
Any suggestions as to what I'm doing wrong would be greatly appreciated, I'm fairly new with storyboards and it's difficult to find what to search to get this working.
If it's not possible with just storyboards themselves, is there some kind of automatic (non-visible) push to the 2nd UITableView I could do?
Thanks!
Nick.
This tutorial will definitely help you : http://maybelost.com/2011/10/tutorial-storyboard-in-xcode-4-2-with-navigation-controller-and-tabbar-controller-part1/
I ended up implementing it the following way, as I wanted to perform the majority of the work within storyboards.
I set up the storyboard with the tab bar embedding the UINavigationController, which contained UITableView(1) which then contained a custom segue to UITableView(2):
Then within the class associated with UITableView(1) I added this single line:
- (void)viewDidLoad {
[self performSegueWithIdentifier:#"campaigns" sender:self];
...
}
On load of the tab, the viewDidLoad of UITableView(1) instantly calls UITableView(2) without any kind of animation, showing the back button to UITableView(1), which is exactly what I wanted.
Thanks to those who responded!
You can implement the delegate method as below.
(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
In this method you can check for the tabBarController.selectedIndex or viewController object. This ensures the selection of correct tab , then push the controller having table 1, then push the controller having table 2.

hidesBottomBarWhenPushed and a pushy UISearchBar on short UITableViews

I'm working on an application that has a view hierarchy that is very similar to Apple's Music or iPod aps. It has a UITabBarController containing UINavigationControllers presenting UITableViews that eventually lead to a UIViewController that sets hidesBottomBarWhenPushed to YES in its designated initializer (to hide the UITabBar). The previous UITableViews all have a UISearchBar in their tableHeaderView that I move out of sight in viewWillAppear:.
The UISearch normally remains hidden until pulled down except when backing out of the UIView to a short UITableView. It remains hidden through the UITableView's viewWillAppear: and viewDidAppear: methods and the UIView's viewWillDisappear: and viewDidDisappear: methods and then finally pops down of its own accord. If I comment out self.hidesBottomBarWhenPushed = YES; in the UIView the UISearchBar stays hidden when backing out of that view.
What is causing the UITableView to reveal the UISearchBar after I have hidden it? Is there a delegate method higher up in the view hierarchy that I can use to prevent it from happening?
Update I've created a simple project with the minimum amount of code needed to reproduce the problem. Download PushySearchBar.zip. (Sorry, I had a mod_rewrite rule preventing downloads from third-party domains. I've added Stack Overflow to the whitelist. Download should work now.)
Does the UISearchBar have text in it when backing out of the UIViewController? Seems plausible that this might cause it to be displayed by default.
EDIT: (adding below comment to body of answer for easier scanning / finding, plus adding some explanation.)
Duplicate your viewWillAppear: method body in viewDidAppear: in SearchableTableViewController. This fixes the issue in my testing.
Basically, all this does is ensure that the offset is set correctly on either side of the navigation animation.
Shaun - I'm still learning Objective-C too so I might be off on this, but I think the issue is that ViewWillAppear: fires once when you get to the SearchableTableView 3 view, and then when you try to go backwards from Terminal to SearchableTableView 3 it's not firing again. I duplicated the code in ViewWillAppear: and put it in ViewDidAppear: and the search bar appears to be hiding properly.
UPDATE: This post seems to verify the above and offer cleaner solutions: iPhone viewWillAppear not firing

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.

Why is self.navigationItem.hidesBackButton not working?

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.