How do I add constraints to my tabBarController? - ios

I have added a tabBarController and hooked it up to viewControllers and given the tabBarController its own class. It works, but I would like to customize it by changing the constraints so that it's not right at the bottom. From what I can see there's no way to add constraints in auto layout as it's all grayed out. I gave a shot at adding it programmatically, but nothing happens.
final class TabBarViewController: UITabBarController {
#IBOutlet var customTabBar: UITabBar!
override func viewDidLoad() {
self.selectedIndex = 2
let fontAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 20.0)]
UITabBarItem.appearance().setTitleTextAttributes(fontAttributes, for: .normal)
//This doesn't work
customTabBar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
//Neither does this
self.tabBar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
//Or this
if let tabC = self.tabBarController {
tabC.tabBar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
}
}
So how do I do this? I want the tabBar to be about 50p from the bottom.

You can create a container view controller to hold you UITabBarController.
Then, when you set the constraints on your container view, you can add 'padding' to the bottom, to have it go upwards. Below are two screenshots explaining the layout.

Related

why UITabbarController is going too bottom

I have Implemented top items a view with UICollectionview and UIPagecontroller to get android like pagetabs.
For each menu there is a container ViewController but in one of them need UITabBarController.
Why UITabBarController is going down.
I have tried moving menuVIew(Contacts,Recents,etc) up but still did not work.
For the top view, use UISegmentedControl, and make background color, same as border color.
For the tab bar, you mean that it is covered by home indicator?
Turn on Use Safe Area Layout Guides, for this UITabController, and you should be good to go.
let tabBar = UITabBar()
override func viewDidLoad() {
super.viewDidLoad()
addTabbar()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
addHeightConstraintToTabbar()
}
func addTabbar() -> Void {
self.view.addSubview(tabBar)
tabBar.translatesAutoresizingMaskIntoConstraints = false
tabBar.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
tabBar.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
tabBar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
let item1 = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.bookmarks, tag: 1)
let item2 = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 2)
tabBar.items = [item1, item2]
self.view.bringSubview(toFront: tabBar)
}
func addHeightConstraintToTabbar() -> Void {
let heightConstant:CGFloat = self.view.safeAreaInsets.bottom + 49.0
tabBar.heightAnchor.constraint(equalToConstant: heightConstant).isActive = true
}
Result:
I have faced this issue before for iPhone X. I found a weird solution of this as below:
Change the bottom constraint from TabBar to Safe Area not the Super view and change the constraint value to 1 as shown in the below image.
This works on both The normal devices and devices with notch display. Check out the screenshots :
Hope this helps you.

Adjusting button width in UITableViewController (Swift - Xcode)

i would like to know how can i adjust my UIButton to be stationary at the bottom so lets say even if there are a lot of tableview cells. The button will still stay put in the bottom.
Ok, to get a FIXED button on bottom on as you required follow the below steps:
Add a UIViewController on your Storyboard
Add a UITableView on UIViewController
Add a UIView at bottom of UIViewController with Fixed Height
Add Constraints to UIView for bottom, left, right to its super view & Make UIView.height Constraints a fix value
Add Constraints to UITableView for top, left, right to its superview & bottom with UIView
Add a UIButton on UIView with Horizontally & Vertically centre to its superview
for your reference demo project zip also added with these steps
Actually there are 3 ways to achieve that in my knowledge.
1 -> By Using UIViewController instead of UITableViewController
In UIViewController you can add UITableView , then add UIButton at the bottom of the UIViewController as per your requirement.
2 -> Add Button on the UINavigationController View
You can add a button on UINavigationController's view (if you are using UINavigationController) programmatically.
3 -> Add Button on the Application Window
You can add a button on UIWindow programmatically.
For 2nd & 3rd point
These cases are applicable when you want to use only UITableViewController. For this you need to create a button programmatically in your TableViewController.
let button : UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Redeem", for: .normal)
button.backgroundColor = .green
return button
}()
Then add the button either on UIWindow or on UINavigationController's view , like this:
If you want to add button UIWindow
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let window = UIApplication.shared.keyWindow
{
window.addSubview(button)
button.centerXAnchor.constraint(equalTo: window.centerXAnchor).isActive = true
let bottomSpaceConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[v0(40)]-20-|", options: .init(rawValue: 0), metrics: nil, views: ["v0" : button])
NSLayoutConstraint.activate(bottomSpaceConstraints)
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
button.removeFromSuperview()
}
and if you want to add it in UINavigationController.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationView = self.navigationController?.view
{
navigationView.addSubview(button)
button.centerXAnchor.constraint(equalTo: navigationView.centerXAnchor).isActive = true
let bottomSpaceConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[v0(40)]-20-|", options: .init(rawValue: 0), metrics: nil, views: ["v0" : button])
NSLayoutConstraint.activate(bottomSpaceConstraints)
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
}
}
Remember
In both cases 2nd & 3rd, You should remove that button in viewWillDisappear method, cause you don't want that button appear on every screen.
In All cases result would be something like this:

How can i add a UIView under my tab bar controller programatically?

I am trying to see how i can add a UIView under my UITabBarController so i can add ads to my app, I cant seem to figure out any way to constrain my UIView to the bottom of the tab bar. Is this possible?
EDIT: By bottom of the tab bar i mean below the tab bar
Try this add see:
Follow these steps to achieve it:
Add UIViewController in root of your storyboard
Add Container View inside UIViewController
Add AdView below Container view
Embed UITabbarController with Container view
I was able to create a UIView in my UITabBarController
lazy var bannerAd: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black
return view
}()
And then pin it to the bottom like so:
view.addSubview(bannerAd)
bannerAd.heightAnchor.constraint(equalToConstant: 44).isActive = true
bannerAd.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
bannerAd.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
then to move up the Tab Bar i did so like this:
override func viewWillLayoutSubviews() {
if !didStyleTabBar {
self.tabBar.invalidateIntrinsicContentSize()
var tabFrame = self.tabBar.frame
tabFrame.size.height = tabBarHeight
tabFrame.origin.y = tabFrame.origin.y - 44
self.tabBar.frame = tabFrame
didStyleTabBar = true
}
}

Add button on top of UITableViewController (Swift)

I am trying to add a button ontop of a uitableview controller table view. The view controller has a navigation controller and static cells, which is why it is a uitableviewcontroller and not a uiviewcontroller. Now I am trying to add a button at the bottom of the screen that is attached to the navigation controller so that it doesn't scroll with the table view.
I am trying to make something similar to what is below. It has a navigation controller for the top bar, a table view with static cells and then a button, but how did they do the button?
Image: http://postimg.org/image/ilsmqqrip/
Thanks!
UPDATE: How can I use a uiviewcontroller with a tableview with static cells using Swift?
I find Container Views very useful in this scenario! A clean solution and very easy to implement.
Just create a normal UIViewController, add your button and a ContainerView as subviews of this UIViewController (the middle one in the image below). Finally create Embed Segue from ContainerView to your UITableViewController (the one on the right).
This way you can use static cell prototypes, not being limited only to UITableView at the same time.
Result:
there is a better solution for this. you can do this by disabling the Auto Layout(button.translatesAutoresizingMaskIntoConstraints = false) property of the corresponding Button or any UIView for floating button:
Swift 4
//create a button or any UIView and add to subview
let button=UIButton.init(type: .system)
button.setTitle("NEXT", for: .normal)
button.frame.size = CGSize(width: 100, height: 50)
self.view.addSubview(button)
//set constrains
button.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
button.rightAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.rightAnchor, constant: -10).isActive = true
button.bottomAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
} else {
button.rightAnchor.constraint(equalTo: tableView.layoutMarginsGuide.rightAnchor, constant: 0).isActive = true
button.bottomAnchor.constraint(equalTo: tableView.layoutMarginsGuide.bottomAnchor, constant: -10).isActive = true
}
I did something similar with UITableViewController and a static datasource. I added the button in the footerview of my tableview.
To make it align to the bottom of the screen i needed this code in my viewcontroller:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Make footerview so it fill up size of the screen
// The button is aligned to bottom of the footerview
// using autolayout constraints
self.tableView.tableFooterView = nil
self.footerView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.tableView.frame.size.height - self.tableView.contentSize.height - self.footerView.frame.size.height)
self.tableView.tableFooterView = self.footerView
}
In short, I resize the footerview to take up all the remaining space after the contentsize of the table view is removed. Since the button is aligned to the bottom of the footerView with autolayout, it will stay in the bottom of the screen.
The Storyboard:
Here is the result:
The UITableViewController will take up the whole space, so you won't be able to add the button. Refactor your UITableViewController based code into UIViewController with UITableView manually added. This way you will be able to set the size of your table view and put the button to the bottom.
Unfortunately UITableViewController has a tableView as its top level view. Of course if you look in the view debugger you can see that the tableview is not the root view. Therefore you can add the buttons to the tableView's window programatically. If you have to do it after the fact, this is probably the easiest way to add a top level element over a UITableViewController. Otherwise if you are doing it in the initial design, you can use container view for your buttons and a UITableViewController for the TableView. The downside of this approach is you end up with two view controllers, one for the container and one for the table and its often necessary to pass information back and for between them. If you are using swift you can simplify this by nesting the tableViewcontroller inside the container view controller class.
If you want to add a button to the window, you can do this lazily once you are sure that the view has a window. Note that the buttons belong to the window and not to the view controller, so its your responsibility to remove them when the view controller disappears.
private weak var button: UIButton!
...
override func didMove(toParentViewController parent: UIViewController?) {
super.didMove(toParentViewController: parent)
guard self.button == nil, let window = tableView.window else {
return
}
let button = UIButton(frame: CGRect(x:0, y:40, width: 200, height: 20))
button.setTitle("This is a red button", for: .normal)
button.backgroundColor = UIColor.red
window.addSubview(button)
self.button = button
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
button?.removeFromSuperview()
}
Step 1 :-
Drag and drop one uiview to UITable View Controller (Static)
Automatically it sticks to the bottom.
If you need to, you can also add two buttons inside UIView... It depends on your requirements.
Step 2 :-
Connect the outlet for uiview (outletView)
Step 3 :-
Add this below code in View Will Appear.
override func viewWillAppear(_ animated: Bool) {
outletViewBottom.backgroundColor = .red
tableView.addSubview(outletViewBottom)
// set position
outletView.translatesAutoresizingMaskIntoConstraints = false
outletView.leftAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.leftAnchor).isActive = true
outletView.rightAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.rightAnchor).isActive = true
outletView.bottomAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.bottomAnchor).isActive = true
outletView.widthAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.widthAnchor).isActive = true
outletView.heightAnchor.constraint(equalToConstant: 50).isActive = true // specify the height of the view
}
Step 4 :-
Now run the code... Happy coding.
all you need to do is to add your Top view whichever it is to the navigationController.view like so:
self.navigationController?.view.addSubview(YOUR_TOP_VIEW)
so if you need a sticky button/view etc... on top of TableViewController which does not scroll with tableView, use this approach.
Here is a UIViewController, with a UITableView added as a subview. At the top right, you can see a dropdown that says Content: Dynamic Prototypes. Change it to Static Cells.

TableView Showing Behind Tab Bar

I am updating my app to use iOS 7 and I'm having a problem with a table view. My tab bar is translucent. The problem is when I scroll to the bottom of my table view, part of the last cell is still behind the tab bar. I'd like to have a bit of space between the last cell and the tab bar. I could fix this by using an opaque tab bar instead, but I want to keep it translucent.
Try setting
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.automaticallyAdjustsScrollViewInsets = NO;
Inside the tableview controller
Swift 4.x
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(0, 0, self.tabBarController!.tabBar.frame.height, 0)
self.yourTableView.contentInset = adjustForTabbarInsets
self.yourTableView.scrollIndicatorInsets = adjustForTabbarInsets
Check the screen shot
Check the under top Bar and Un-checke under Bottom Bar
SWIFT 3
put this inside viewDidLoad of your tableViewController:
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
self.automaticallyAdjustsScrollViewInsets = false
Swift 3.0
This is what worked for me. In your Custom ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(self.tabBarController!.tabBar.frame.height, 0, 0, 0);
//Where tableview is the IBOutlet for your storyboard tableview.
self.tableView.contentInset = adjustForTabbarInsets;
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets;
}
Not to sure I like the solution but it works for me.
With iOS 11 I have no issue, I simply use the following in viewDidLoad():
self.collectionView.bottomAnchor.constraint(self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
However on iOS 10 I need to hack my way like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let tabBarHeight: CGFloat = (self.parent?.tabBarController?.tabBar.frame.size.height)!
if #available(iOS 11.0, *) {
} else {
self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -tabBarHeight).isActive = true
}
}
This is working for me
override func viewDidLoad() {
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
}
If any view shows behind a UITabBar you can grab the bottomLayoutGuide and make adjustments at runtime. What I do is have a BaseViewController that all my view controllers inherit from. Then if the tab bar is visible we adjust the view like so:
import UIKit
class BaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
//Ensures that views are not underneath the tab bar
if tabBarController?.tabBar.hidden == false {
var viewBounds = self.view.bounds;
var bottomBarOffset = self.bottomLayoutGuide.length;
self.view.frame = CGRectMake(0, 0, viewBounds.width, viewBounds.height - bottomBarOffset)
}
}
}
Since I don't use storyboards (where you can click a checkbox in IB to fix this problem), this has been the best solution I have found.
It is really hard to resolve the issue without detail information or actual codes. I have similar issue of tabview behind UItabBar in my project. The solutions offered here do not work in my case. After exploring my codes, I found a solution for my case.
Here is brief explanation of my case. I have a UItabBar in main view with two tab buttons. In one tab view, there is table view. If user taps on a row, a detail view is presented by using navigation controller. In the detail view, the tab bar is hidden, and a toolbar is showing at the bottom.
In order to bring tab bar back and hide the toolbar when the main view is brought back, I have to explicitly show tab bar and hide toolbar in the event of viewWillAppear:
class myMainViewController: UITableViewController {
private var tabBarHidden: Bool? = {
didSet {
self.tabBarController?.tabBar.isHidden = tabBarIsHidden ?? true
}
}
private var toolBarIsHidden: Bool? {
didSet {
let hidden = toolBarIsHidden ?? true
self.navigationController?.toolbar.isHidden = hidden
self.navigationController?.setToolbarHidden(hidden, animated: true)
}
}
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarIsHidden = false
self.toolBarIsHidden = true
}
...
}
I finally realize that the visibility of bar at the bottom is set in the event of viewWillAppear. At that time, the tableView or scroll view's content insets are set already based on no bar at the bottom. That's why my tableView is behind the bottom bar.
The solution I found is to reset content insets in the event of viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
// In the event of viewWillAppear, visibilities of tool bar and tab bar are set or changed,
// The following codes resets scroll view's content insets for tableview
let topInset = self.navigationController!.navigationBar.frame.origin.y +
self.navigationController!.navigationBar.frame.height
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(
topInset, 0,
self.tabBarController!.tabBar.frame.height, 0)
self.tableView.contentInset = adjustForTabbarInsets
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets
}
The best approch would be to Embed TabBarController to your ViewController (Editor -> Embed In -> TabBar Controller)and set the bottom of the tableview to be bottom of safe area of viewcontroller. The other ways wont be as perfect as this one.
You need to adjust the height of the table view. Just leave 49px at the bottom, as the tabbar height is 49 px. Adjust the height of table view so that it leaves 49px space below it.

Resources