How to stop TableViewController hitting status bar? - uitableview

I have a new TableViewController that I dragged from the object library. It is now populated with data. When I run the app, the tableview is going all the way into the status bar. I don't think that is normal.
But I also don't see a way to resize the a tableview on the TableViewController scene. Any one have some suggestions how I should go about fixing this issue?

You can embed your TableViewController in a Navigation controller:
1. Select the TableViewController in the storyboard
2. Xcode top bar: Editor -> Embed in -> Navigation controller
If you don't want to embed your TableViewController in a nav. control, you can add padding to the table view in your viewDidLoad in your TableViewController class:
// ... in TableViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
// add top padding to table view
let myTopPadding: CGFloat = 50
self.tableView.contentInset = UIEdgeInsetsMake(myTopPadding, 0, 0, 0);
// ...
}
Note that with this solution, however, you data will still "hit" the status bar when scrolling, although not when entering your table view. This is apparently and issue that is hard to avoid (without navigation controller) when subclassing the UITableViewController:
iOS 7: UITableView shows under status bar
If you want padding also when scrolling then, based on the thread above, you're better off creating your own table via view controller -> insert table view -> ..., rather than using the finished UITableViewController "package".

Related

What is best approach to creating a persistant UIView nav bar in a UITabBarController?

I've added a custom UIView to my base UITabBarController. I start by hiding the default tabBar. The viewdidload looks like this in UITabBarController:
override func viewDidLoad() {
super.viewDidLoad()
//hide default tab bar
self.tabBar.isHidden = true
tabBarArea.frame.size.width = self.view.frame.width
tabBarArea.frame.origin.y = self.view.frame.height - tabBarArea.frame.height
self.view.addSubview(tabBarArea)
}
That works well. The tabBarArea is defined in the storyboard as a custom view for the UITabBarController. The custom view sits between the First Responder and the Exit icons in the top bar.
Now, the problem is that the UITabBarController will disappear as soon as we load a child view controller and this custom UIView area will vanish with it.
Is there a way to make this root custom view always present even when child view controllers are loaded in?
Thanks for input. I like the idea of this custom UIView area but this approach needs refinement. I also don't need it to be a traditional UITabBarController with tab bar items, etc. I'd like to break out of that mold and just have custom UIButtons are whatever in this view area.

Navigation controller with tab bar controller?

I have a tableViewController embedded in my tab bar controller. When a cell is tapped, a segue is launched to another view controller to show the details of that object. However, the Back button is not appearing in the viewDetail. I tried embedding the view into a separate Navigation Controller but that didn't change anything. What am I doing wrong? I currently have Tab Bar Controller -> tableView -> Navigation Controller -> viewDetail (need Back button here to return to tableView).
Here's what I have right now:
Thanks!!
Each UIViewController in UITabBarController could be embedded in an UINavigationController at your convenience, that way you'll be able to use all of the features that you need.
Basically, you need to select the tableViewController, click on Editor menu item, select Embed in and click on Navigation Controller, ta daa.
You can show or hide Navigation Bar if you need it using Interface Builder or programmatically in your Detail viewController as follows:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = true
// Do stuff
}
Set NavigationController to TabBarController, then set NavigationController's rootViewController to TableViewController.
You just have the organization wrong. Currently you have Tab Bar Controller -> tableView -> Navigation Controller -> viewDetail. It should be Tab Bar -> tableview -> View detail. Navigation should be separate pointing to table view. Nothing should be pointing to navigation. It should just point to tableview
It should look something like the above picture

TableView scroll underlying top bar

Since I updated my app to iOS 7 new GUI I have a problem that I can't solve.
My app consists in a scrollable TableView. Trouble is that TableView scrolls underlying top bar, means that table doesn't consider top bar and extends till the top and it's ugly to see.
I tried removing check on "Extend edges under Top Bars" but it's the same.
How can I solve this?
One solution is: set the table view's contentInset and scrollIndicatorInsets to have a top inset of 20. The table view will still underlap the status bar, but it will be completely visible when scrolled all the way.
If you don't like that solution, and you want a permanent empty area behind the status bar, you will have to change the way you pin/position the top of the table view, to allow for the status bar. How you do this depends on whether you are using auto layout. If you are, just pin to the top layout guide. If you are not, you will have to use the "delta" field provided in the nib editor.
If you are using a UITableViewController, however, you are not in charge of the top of the table view; it is a full-screen view and it is the view controller's main view. This is quite a troublesome situation, actually. I have resorted to two solutions:
Put the whole thing into a UINavigationController in order to get the nav bar to "run interference" for me.
Or, embed the table view controller in a custom parent view controller just so that I can position the top of the table view.
In UINavigationController I created an UIView, which goes under status bar but in front of embed controller (the Table), so table disappear behind this view and status bar is always on top.
var patch: UIView!
override func viewDidLoad() {
super.viewDidLoad()
patch = UIView(frame: CGRectMake(0, 0, view.bounds.width, 20))
patch.backgroundColor = UIColor.redColor()
self.view.addSubview(patch)
}
Then I make it disappear when screen goes in Landscape (in iOS9, status bar automatically disappear in Landscape) and make it reappear when screen goes in Portrait.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
patch.hidden = true
} else {
patch.hidden = false
}
}

Insert subview behind TabBar from child view of TabBarController

I have TabBar with 2 tabs. At some point, from either of the 2 tabs, I want to add a view that is visible on both tab views but behind the TabBar.
So I thought, insert a subview into the TabBarController but below the TabBar.
This works fine in principle and I have the view behind the TabBar but now covering my 2 tabs as I wanted. However, it doesn't actually load. Just its background loads and only viewDidLoad() is called, not viewWillAppear() or any others.
I have also tried calling addChildViewController(myVC) on the TabBarController which has no effect, and also manually calling viewWillAppear() on the view controller I add which also has no effect (and I'm also dubious about whether manually calling viewWillAppear() is permitted or not?).
Is what I'm trying to do possible? What am I missing? Or should I be attempting this some other way?
For some reason, when inserting a subview into a UITabBarController behind it's UITabBar, although the view is visible to the user, the system itself seems to think it is not and so although viewDidLoad() is called, viewDidAppear() and subsequent methods are not.
However, adding a subview above the UITabBar seems to work fine. So I solved this by adding my own new UITabBar as a subview to the UITabBarController (set up basically exactly as the default one would be) and then removing the UITabBarController's default UITabBar.
Then when later inserting my view into the UITabBarController, I insert it as I was doing originally but instead below/behind my custom UITabBar and it seems to load fine.
There is no need to remove and recreate the tabBar. What you need to do is after you insert your custom view, you can then bring the tabBar to the front again.
//bring the tabBar to the front after inserting new view
self.view.bringSubview(toFront: self.tabBar)
This would be a good way:
Add the function below and call it in viewDidLoad of your initial VC. It unwraps your tab bar controller instance (which is optional), and then inserts the view you always want visible just below the tab bar.
private func setupAlwaysVisibleView() {
guard let tabBarController = self.tabBarController else { return }
tabBarController.view.insertSubview(alwaysVisibleView, belowSubview: tabBarController.tabBar)
}
Avoid using optionals for tabBarController or removing current tabBar. Simple add your view below tabBar view. Swift 5, XCode 11.
class TabBarController: UITabBarController {
#IBOutlet var instructionsView: UIView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.view.insertSubview(instructionsView, belowSubview: self.tabBar)
}
}
you can also do this inside the init() method for your UITabViewController:
view.insertSubview(alwaysVisibleView, belowSubview: self.tabBar)
no need to dispatch to another method if you are using a subclass of UITabViewController.

Incorrect Popover size - when displaying UITableViews under Navigation Controller in Popover

I am writing an iPad app which features UITableViews displayed under a Navigation Controller within a UIPopoverController.
The popover is displayed when I pick a button in the Main View Controller of my app. The popover opens displaying a first TableViewController, which has two rows (UITableViewCells) - "Search" in the first row and "Advanced Settings" in the second row. On initial display, the popover is sized just enough to display the two rows.
I have coded this first TableViewController's didSelectRowAtIndexPath such that when I pick "Search", it pushes a second TableViewController onto the NavigationViewController. This next View Controller allows the user to perform a search, and search results then get populated in its tableview.) This (search results) table view controller is sized long enough to accommodate all the rows returned by the search. The search popover therefore becomes longer when displaying the search tableview controller.
When I cancel the search (or hit the back button in the navigation bar) the popover returns to displaying the first table view controller (the one with just two rows). However this first table view controller now has the longer size. In other words, the popover, instead of resizing itself back to a two row table, remains the size of the second (search results) table view controller (so it now has the two rows "Search" and "Advanced Settings" plus a number of empty rows)
My question is: how can I get each tableview controller in the hierarchy in this implementation (i.e. where table view controllers are displayed in a popover under a navigation controller) to be sized individually and to return to its original size when the user navigates back and forward. There is probably a simple solution to this, but it escapes me! Appreciate if someone can point me to a solution.
As the above solution does not work anymore, here's a more current (Swift) alternative.
You can pass along the popovercontroller to your destinationViewControllers.
Then call preferredContentSizeDidChangeForChildContentContainer in viewWillAppear() and the popover will resize automatically.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if let ppc = popoverController {
ppc.preferredContentSizeDidChangeForChildContentContainer(self)
}
}
If it doesn't work make sure you properly implement the preferredContentSize. For example calculating the size of the your tableViewController with a single section as such:
override var preferredContentSize: CGSize {
get {
let sectionFrame = self.tableView.rectForSection(0)
let titleOnTop = self.navigationController!.navigationBar.frame.height
let height = sectionFrame.height + titleOnTop
return CGSize(width: super.preferredContentSize.width, height: height)
}
set { super.preferredContentSize = newValue }
}
I implemented the answer from the following StackOverflow post by user #krasnyk :
Popover with embedded navigation controller doesn't respect size on back nav
It worked great for me with one change ...
Basically added the same function detailed in the above post with one modification (I hardcoded the size for each VC in my view heirarchy in the PopupController)
I referenced this function to set the correct popover size in the ViewDidLoad and ViewDidAppear functions for each VC in the chain of VCs displayed in my PopoverController.
- (void) correctPopoverContentSize {
//
// removed the following line from the original code in above post as it did not
// work for me
// CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
CGSize currentSetSizeForPopover = CGSizeMake(320.0f, 180.0f);
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f,
currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
}

Resources