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
Related
I have a situation here which I am trying to resolve, but it seams I am missing something.
My architecture of the application is as following:
AppDelegate (TabBarController)
Navigation Controller
Viewcontroller one
Viewcontroller two
Viewcontroller three
Viewcontroller
Since I have lot of text validations and scrolling enabled, I am using a custom uiscrollview for viewcontroller one, two and three. In the custom uiscollview I am utilizing the code from Apple which causes scrollview to scroll if the textfield is being hidden behind the keyboard. The problem which I am occuring at this point is that I have the viewcontroller one working fine, but when it comes to viewcontroller two and three, it does see's that custom view controller after debugging, and reaches the point of "setContentOffSet" but not animating the scrollview, but just displaying the keyboard.
If anyone had this issue before, I would like to see what might I be missing here?
Basically i wanted to implement a popup UIView so i followed what was posted here
POP-UP UIView "IMDB App" style
This works very well. However i have one query. My main view is a tableView. so when a view is popped up i disable scrolling in the table. Now when the popup subView is removed, i need to re-enable scrolling. How do i achieve that? i can't use willRemoveFromSuperview because the popup view is loading a different NIB altogether.
Should i use Notifications?
hope i was clear with explaining the scenario.
Thanks in advance!
Feloneous Cat has the correct answer. This is the perfect use a #protocol in your popup view along with a registered delegate. Something is triggering that popup view to close. Whatever that trigger is, call the protocol and the delegate can handle the situation as needed.
Furthermore, when protocols are used correctly, your code becomes very reusable within a project as well as in other projects.
What you could do is subclass UIView and override removeFromSuperview to send a notification. I don't think there's ever a case where a view gets removed without using the removeFromSuperview method.
I have a UITableViewCell with UITextField. In the edit mode, when the keyboard comes up, the row I am trying to edit gets overlapped by the keyboard. Ideally the row should scroll up so that I can edit the cell.
I have searched stackoverflow throughly and found several different solutions. Most of them has to do with calculations to move or scroll the view up. Now there is a sample code from apple called TaggedLocations. They have the exact same behavior. And there is no code doing any complex calculations to move the view up.
I also thoroughly checked the IB interface and could not find any fancy thing going on either. If you download and run the code, it beautifully pushes up the row to a perfect position for the edit.
Does anybody know what trick is there in the TaggedLocations project which does this so elegantly? Location of the project:
http://developer.apple.com/library/ios/#samplecode/TaggedLocations/Introduction/Intro.html%23//apple_ref/doc/uid/DTS40008914
For the complex calculations I was referring, look at the following thread for example:
How to make a UITextField move up when keyboard is present?
thanks
This line in RootViewController.m is what causes the scroll:
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
Put that in your cellForRowAtIndexPath: method and it should work.
I figured out the problem.
Initially I had
TabBarController
- UIViewController
-- UINavigationController
--- UITableViewController
- UIViewController
- -UINavigationController
--- UITableViewController
With this kind of a hierarchy the table cells do not roll up when keyboard is displayed. But if I change the hierarchy to the following the rolling up happens just fine without any additional code!
TabBarController
- UINavigationController
-- UITableViewController
- UINavigationController
-- UITableViewController
When I duplicated the Apple code, I somehow messed up the hierarchy. Now on switching to the new hierarchy, it works fine.
No additional code or calculation necessary.
or call viewWillAppear if you have your own hierarchy of controllers... then table views will automatically scroll cells to be visible when keyboard appears.
I had a similar confusion with Apple's sample code TaggedLocations, although in my case I realized that my table view was being managed by a UIViewController subclass as opposed to a UITableViewController subclass, and UITableViewController apparently manages the scrolling for you. It works quite well, so a lot of these complex calculations are unnecessary. I wish I didn't spend so much time trying to figure them out.
I don't see why the hierarchy you describe would matter. As long as the UITableViewController is managing the text fields, it should automatically take care of the scrolling to avoid the keyboard.
I have application with multiple UIViewControllers using navigation controller. UIViewController contains tableView, searchbar (that I can show/hide) and toolbar. All of this is added as subviews to its view. All this subviews are created after UIViewController is initialized and their content depend on UIVievController's content.
It works fine expect one problem. When I play with my app a little, move back and forth, open some modal views etc sometimes after navigating back to my root VC all it's subviews dissapear and all I get is white screen.
I double checked all my code and I can't find source of problem (I certainly don't remove them myself). I wasn't able to find exact patern how to reproduce this, it seems random. Any idea why iphone would remove my subviews from VC? I would post some code, but I don't want to put it all here and I am not sure which part is important, so if you wish to see some, let me know
Add your views in loadView or viewDidLoad: when viewDidUnload is called, the view is released, so they need to be created again when the view is shown again.
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.