Swift remove space of Navigation Bar from UITabBarController - ios

I'm making an iOS application, creating the UI programmatically, so far what I'm trying to do is display a map with MapKit inside one tab of UITabBarController, and I'm trying to make the map to use the full space below the notch in an iPhone X (and above) but it's below it, as if the title of a NavigationController were there.
Even if I try to remove the titleView it's still there.
TabBarViewController
import UIKit
class TabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
UITabBar.appearance().barTintColor = .systemBackground
tabBar.tintColor = .label
setupVCs()
}
func setupVCs() {
viewControllers = [
createNavController(for: MapViewController(), title: NSLocalizedString("Search", comment: ""), image: UIImage(systemName: "magnifyingglass")!),
createNavController(for: ViewController(), title: NSLocalizedString("Home", comment: ""), image: UIImage(systemName: "house")!),
createNavController(for: ViewController(), title: NSLocalizedString("Profile", comment: ""), image: UIImage(systemName: "person")!)
]
}
fileprivate func createNavController(for rootViewController: UIViewController, title: String, image: UIImage) -> UIViewController {
let navController = UINavigationController(rootViewController: rootViewController)
navController.tabBarItem.title = title
navController.tabBarItem.image = image
//rootViewController.navigationItem.title = title
rootViewController.navigationController?.isToolbarHidden = true
navigationItem.titleView?.removeFromSuperview()
navigationItem.title?.removeAll()
return navController
}
}
MapViewController
import UIKit
import MapKit
class MapViewController: UIViewController {
var mapView: MKMapView!
let annotation = MKPointAnnotation()
override func viewWillAppear(_ animated: Bool) {
navigationController?.setToolbarHidden(true, animated: false)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
mapView = MKMapView()
mapView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(mapView)
setupConstraints()
}
fileprivate func setupConstraints() {
let mapViewLeadingAnchor = mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
let mapViewTrailingAnchor = mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
let mapViewTopAnchor = mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
let mapViewBottomAnchor = mapView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
NSLayoutConstraint.activate([mapViewLeadingAnchor, mapViewTrailingAnchor, mapViewTopAnchor, mapViewBottomAnchor])
}
}
This is how the map looks like when having the title set and without it, I just set the title to see if it was due to this.
How can I remove that extra space below the notch?

So basically you need to do two things here in order to hide the navigation bar just in the map tab:
Hide the nav bar either in your viewWillAppear or viewDidLoad inside MapViewController with:
navigationController?.setNavigationBarHidden(true, animated: false)
Change your top constraint from safeArea to the view itself with:
let mapViewTopAnchor = mapView.topAnchor.constraint(equalTo: view.topAnchor)

Related

How to adjust sublayer on UINavigationController?

In my Swift project, I added a sublayer to UINavigationController.
But after adding this GAGradientLayer, I can't see the navigation title text or back button.
The weird thing is that in the view hierarchy, the CAGradientLayer(which was added as sublayer) is behind the title and button.
I tried to reload navigationController layer with LayoutIfNeeded, setNeedsLayout or setNeedsDisplay but nothing worked.
And I also just tried to change the navigation title but it doesn't work.
(Actually The text of navigation title is loaded on the view controller behind, so I don't want to change this on this VC.)
So, How can I show my navigation title text and button with CAGradientlayer above?
Here's the screenshot
Here's the codes needed
import UIKit
import SnapKit
class BulletinBoardViewController: UIViewController {
// ...
var backgroundGradientLayer: CAGradientLayer?
let bulletinBoardView = BulletinBoardView()
// MARK: - Lifecycles
override func viewDidLoad() {
super.viewDidLoad()
setBulletinBoardView()
setCells()
}
override func viewWillAppear(_ animated: Bool) {
setupBackgroundLayer()
}
override func viewWillDisappear(_ animated: Bool) {
self.backgroundGradientLayer?.removeFromSuperlayer()
}
// MARK: - Helpers
func setupBackgroundLayer() {
DispatchQueue.main.async {
if let backgroundGradientLayer = self.backgroundGradientLayer {
backgroundGradientLayer.frame = CGRect(x: 0, y: -59, width: 500, height: 103)
self.navigationController?.navigationBar.layer.addSublayer(backgroundGradientLayer)
}
}
}
func setBulletinBoardView() {
self.view.addSubview(bulletinBoardView)
bulletinBoardView.snp.makeConstraints { make in
make.right.left.top.equalTo(self.view.safeAreaLayoutGuide)
make.bottom.equalTo(self.view)
}
}
// ...
}
The origin navigation controller setting is below
class MainPageViewController: UIViewController {
// ...
func setupNav() {
navigationController?.navigationBar.tintColor = .black
navigationItem.rightBarButtonItem = listButton
navigationItem.leftBarButtonItem = settingButton
let backBarButtonItem = UIBarButtonItem(title: "",
style: .plain,
target: self,
action: nil)
self.navigationItem.backBarButtonItem = backBarButtonItem
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .systemGray3
appearance.titleTextAttributes = [NSAttributedString.Key.font: UIFont(name: AppFontName.bold, size: 20)!]
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance =
navigationController?.navigationBar.standardAppearance
}
// ...
}

How to change prompt color in Swift 5 iOS16

I am trying to change the color of the prompt in my navigation controller so that it is white not black for iOS16.
The following code changes the title but not the prompt. My code is:
import UIKit
class ParentViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = UIColor.blue //UIColor.lincsNavBarBlueColor()
appearance.titleTextAttributes[NSAttributedString.Key.foregroundColor] = UIColor.white
navigationItem.standardAppearance = appearance
navigationItem.scrollEdgeAppearance = appearance
navigationItem.title = "Hello there"
navigationItem.prompt = "This is the prompt"
}
}
What do I need to add to change the prompt color? Thanks.
This seems like a bug and I doubt Apple will fix it.
I've worked around it by subclassing the UINavigationController and diving for the label.
#objc
final class NavigationControllerWithPrompt: UINavigationController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
changePromptColor()
}
private func changePromptColor() {
let promptView = navigationBar.subviews.first { view in
return String(describing: type(of: view)) == "_UINavigationBarModernPromptView"
}
let promptLabel = promptView?.subviews.compactMap{ $0 as? UILabel }.first
promptLabel?.textColor = UIColor.white
}
}

Navigation bar title truncated after relaunching application

Recently I getting e wired problem
When I run my application from XCode navigation title text is showing perfect.
But when I close the application and reluanch again text cuts off with
...
I tried the following questions but no luck.
Here is my BaseViewController
import UIKit
import SnapKit
open class BaseViewController: UIViewController {
public lazy var navigationShadowView: UIView = {
let view = UIView()
DispatchQueue.main.async {
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor.Blue10.cgColor, UIColor.Blue0.withAlphaComponent(0.0).cgColor]
gradientLayer.frame = view.bounds
view.layer.addSublayer(gradientLayer)
}
return view
}()
public override func viewDidLoad() {
loadDefaults()
setupUI()
}
}
extension BaseViewController {
private func loadDefaults() {
view.backgroundColor = .white
tabBarController?.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
// MARK: Navigation bar bottom shadow
view.addSubview(navigationShadowView)
navigationShadowView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
make.leading.equalTo(view.snp.leading)
make.trailing.equalTo(view.snp.trailing)
make.height.equalTo(10)
}
}
#objc open func setupUI() {
}
}
I populate the view in viewcontroller like below
import UIKit
import CoreModule
import SnapKit
import Combine
class CustomerProjectListVC: BaseViewController {
lazy var refreshControl: UIRefreshControl = {
let refresh = UIRefreshControl()
refresh.addTarget(self, action: #selector(refreshProjects(_:)), for: .valueChanged)
return refresh
}()
lazy var jobsTableView: UITableView = {
let tableView = UITableView()
tableView.showsHorizontalScrollIndicator = false
tableView.showsVerticalScrollIndicator = false
tableView.separatorStyle = .none
tableView.rowHeight = 220
tableView.backgroundColor = .Blue0
tableView.addSubview(refreshControl)
tableView.register(ProjectListTableViewCell.self, forCellReuseIdentifier: ProjectListTableViewCell.identifier)
tableView.dataSource = self
tableView.delegate = self
return tableView
}()
private let viewModel = CustomerProjectListViewModel()
private var subscription = Set<AnyCancellable>()
override func viewDidAppear(_ animated: Bool) {
tabBarController?.navigationItem.title = "Project List"
tabBarController?.navigationItem.rightBarButtonItem = nil
}
override func setupUI() {
view.addSubview(jobsTableView)
jobsTableView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
make.bottom.equalToSuperview()
}
populateData()
}
}
Here is the CustomeTabBarController
class CustomerTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
tabBar.backgroundColor = .white
viewControllers = [
createNavController(for: sideMenuController(), title: "Home", image: UIImage(named: .TabBarHome)!),
createNavController(for: ProfileVC(), title: "Profile", image: UIImage(named: .TabBarProfile)!),
createNavController(for: NewPostVC(), title: "Post", image: UIImage(named: .TabBarPost)!),
createNavController(for: CustomerProjectListVC(), title: "Chatbox", image: UIImage(named: .TabBarChatBox)!),
createNavController(for: HomeVC(), title: "Notification", image: UIImage(named: .TabBarNotification)!)
]
}
}
extension CustomerTabBarController {
fileprivate func createNavController(for rootViewController: UIViewController,
title: String,
image: UIImage) -> UIViewController {
rootViewController.tabBarItem.title = title
rootViewController.tabBarItem.image = image
return rootViewController
}
}
extension CustomerTabBarController {
private func configureSideMenu() {
SideMenuController.preferences.basic.menuWidth = UIScreen.main.bounds.width - 80
SideMenuController.preferences.basic.position = .above
SideMenuController.preferences.basic.direction = .right
}
private func sideMenuController() -> SideMenuController {
configureSideMenu()
return SideMenuController(contentViewController: HomeVC(), menuViewController: SideMenuVC())
}
}
And I am initiating the viewcontroller like following
let viewController = CustomerTabBarController()
let navigationViewController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navigationViewController
window?.makeKeyAndVisible()
Navigation Bar Shrinking after relaunching app
Navigation Bar Title truncated
Here I attached some screenshots.
Following one is launching from XCode
https://pasteboard.co/HHl1vFJYbzeX.png
2nd one is after relaunch
https://pasteboard.co/kw3zRZqic9q7.png
My question is why this does not happen when runs from XCode but when the app relaunchs.
I have tried many ways like setupui from viewdidappear methods and others. But no luck.
Please help me out.
It seems like you're setting the title of a wrong view controller and it's also worth checking if you have the correct navigation hierarchy. My best be would that this is causing the problems.
The typical hierarchy of tab-based apps is the following: UITabBarController (root) → UINavigationController (one per each tab) → YourViewController.
Now, I see that you're setting the navigation title as follows:
tabBarController?.navigationItem.title = "Project List"
This is strange and unusual. The view controller is supposed to set its own title like this:
navigationItem.title = "Project List"
Then its parent UINavigationController will be able use this title.
It's also worth setting the title in viewDidLoad, not it viewDidAppear so that the title is visible before the transition animation and not after it.
So check the hierarchy of the view controllers in the app and make sure each view controller only sets its own navigation title.
If that doesn't help, I'll be happy to retract my answer to avoid confusion.

Passing a value to a previous view controller doesn't reflect the change

I have MainViewController and DetailViewController that are stacked together by a navigation controller. I want to pass a value from DetailViewController back to the previous controller, which is MainViewController.
First, I tried it with UINavigationControllerDelegate:
class DetailViewController: UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
(viewController as? MainViewController)?.myClass = myClass
}
}
which was to be called as DetailViewController is popped:
_ = navigationController?.popViewController(animated: true)
But, the new value doesn't get reflected on MainViewController:
class MainViewController: UIViewController {
var myClass: MyClass
private lazy var commentLabel: UILabel = {
let comment = UILabel()
comment.text = myClass.comment
comment.numberOfLines = 0
return comment
}()
}
even though when I log myClass in MainViewController, I can see that it's being passed properly.
I also tried it with a property observer so that DetailViewController can pass it to a temporary property observer instead:
var temp: MyClass? {
willSet(newValue) {
myClass = newValue
}
}
but, the view controller's interface still doesn't change.
Finally, I tried creating a delegate in MainViewController:
protocol CallBackDelegate {
func callBack(value: MyClass)
}
where the function simply passes the argument:
func callBack(value: MyClass) {
myClass = value
}
I set the delegate to self:
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
and invoking the function in DetailViewController:
delegate?.callBack(value: MyClass)
but, still doesn't update the interface. It seems as though passing the value isn't the issue, but having it be reflected is.
This is an example of using the protocol / delegate pattern. It's about as basic as it gets...
Start a new single-view project
add the code below
Set the class of the default view controller to MainViewController
embed it in a Navigation Controller
run the app
Then:
Tap the button labeled "Push to next VC"
Enter some text in the "Edit Me" field
Tap the "Pop back to previous VC"
See that the label has been updated with your entered text.
protocol CallBackDelegate: class {
func callback(_ val: String)
}
class MainViewController: UIViewController, CallBackDelegate {
let btn = UIButton()
let theLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
btn.translatesAutoresizingMaskIntoConstraints = false
theLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(theLabel)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
theLabel.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0),
theLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
theLabel.backgroundColor = .yellow
btn.backgroundColor = .red
theLabel.text = "Default text"
btn.setTitle("Push to next VC", for: [])
btn.addTarget(self, action: #selector(self.pushButtonTapped(_:)), for: .touchUpInside)
}
#objc func pushButtonTapped(_ sender: Any?) -> Void {
let vc = DetailViewController()
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
func callback(_ val: String) {
theLabel.text = val
}
}
class DetailViewController: UIViewController {
weak var delegate: CallBackDelegate?
let textField = UITextField()
let btn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
btn.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(textField)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textField.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
textField.centerXAnchor.constraint(equalTo: g.centerXAnchor),
textField.widthAnchor.constraint(equalToConstant: 200.0),
btn.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
textField.backgroundColor = .yellow
textField.borderStyle = .roundedRect
btn.backgroundColor = .blue
textField.placeholder = "Edit me"
btn.setTitle("Pop back to previous VC", for: [])
btn.addTarget(self, action: #selector(self.popButtonTapped(_:)), for: .touchUpInside)
}
#objc func popButtonTapped(_ sender: Any?) -> Void {
if let s = textField.text {
delegate?.callback(s)
}
self.navigationController?.popViewController(animated: true)
}
}
Doesn't seem that you are updating the UILabel value anyhow
var myClass: MyClass? {
didSet {
self.commentLabel.text = myClass?.comment
}
}
You have to update the label text itself, right now it's constant with the first load data

Switching View Controllers using MDCBottomNavigationBar on iOS

I'm trying to create an iOS app that uses the Material Design library's bottom navigation feature. I can get a view controller with the bottom navigation bar to compile and display, but I'm unable to add other view controllers and switch between them when clicking the different tabs. I have everything simplified down to two files: One is my entry view controller with the bottom navigation stuff, and the other is just a dead simple view controller that I instantiate 3 times to use as the targets of the three tabs.
Currently, my specific error is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'headerViewController does not have a parentViewController. Use [self addChildViewController:appBar.headerViewController]. This warning only appears in DEBUG builds'
As far as I can tell, this is related to my tab views. If I comment out the line that does self.viewControllers = [...] then it will load just fine, but not switch between view controllers.
I'm not married to this approach. If there is another way to accomplish this I'd love to know it. I have been unable to learn much from the docs, but if there is documentation for other tab-like Material Design features that works significantly similarly, I think that would point me in the right direction.
Here is my entry view controller. I used one of the examples as a base and heavily modified it.
import Foundation
import MaterialComponents
import UIKit
class ICEBottomNavController: UITabBarController, MDCBottomNavigationBarDelegate
{
let appBar = MDCAppBar()
var colorScheme = MDCSemanticColorScheme()
// Create a bottom navigation bar to add to a view.
let bottomNavBar = MDCBottomNavigationBar()
init()
{
super.init(nibName: nil, bundle: nil)
initCommon()
}
#available(*, unavailable)
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
initCommon()
}
func initCommon()
{
self.title = "Bottom Navigation (Swift)"
let statusVC = ICEChildBottomBarViewController( title: "Status", color: UIColor.orange )
let eventsVC = ICEChildBottomBarViewController( title: "Events", color: UIColor.blue )
let contactsVC = ICEChildBottomBarViewController( title: "Contacts", color: UIColor.cyan )
self.viewControllers = [ statusVC, eventsVC, contactsVC ]
self.addChildViewController( appBar.headerViewController )
let color = UIColor(white: 0.2, alpha:1)
appBar.headerViewController.headerView.backgroundColor = color
appBar.navigationBar.tintColor = .white
appBar.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
//appBar.headerViewController.viewControllers
commonBottomNavigationTypicalUseSwiftExampleInit()
}
func bottomNavigationBar(_ bottomNavigationBar: MDCBottomNavigationBar, didSelect item: UITabBarItem)
{
print( "did select item \(item.tag)" )
self.selectedIndex = item.tag
//self.viewControllers?[item.tag].addChildViewController( appBar.headerViewController )
//self.selectedViewController = self.viewControllers?[item.tag]
// self.viewControllers
}
func commonBottomNavigationTypicalUseSwiftExampleInit()
{
view.backgroundColor = .lightGray
view.addSubview(bottomNavBar)
// Always show bottom navigation bar item titles.
bottomNavBar.titleVisibility = .always
// Cluster and center the bottom navigation bar items.
bottomNavBar.alignment = .centered
// Add items to the bottom navigation bar.
let tabBarItem1 = UITabBarItem( title: "Status", image: nil, tag: 0 )
let tabBarItem2 = UITabBarItem( title: "Events", image: nil, tag: 1 )
let tabBarItem3 = UITabBarItem( title: "Contacts", image: nil, tag: 2 )
bottomNavBar.items = [ tabBarItem1, tabBarItem2, tabBarItem3 ]
// Select a bottom navigation bar item.
bottomNavBar.selectedItem = tabBarItem1;
bottomNavBar.delegate = self
}
func layoutBottomNavBar()
{
let size = bottomNavBar.sizeThatFits(view.bounds.size)
let bottomNavBarFrame = CGRect( x: 0,
y: view.bounds.height - size.height,
width: size.width,
height: size.height )
bottomNavBar.frame = bottomNavBarFrame
}
override func viewWillLayoutSubviews()
{
super.viewWillLayoutSubviews()
layoutBottomNavBar()
}
#if swift(>=3.2)
#available(iOS 11, *)
override func viewSafeAreaInsetsDidChange()
{
super.viewSafeAreaInsetsDidChange()
layoutBottomNavBar()
}
#endif
override func viewDidLoad()
{
super.viewDidLoad()
self.selectedIndex = 0
appBar.addSubviewsToParent()
// Theme the bottom navigation bar.
MDCBottomNavigationBarColorThemer.applySemanticColorScheme(colorScheme, toBottomNavigation: bottomNavBar);
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden( true, animated: animated )
}
}
// MARK: Catalog by convention
extension ICEBottomNavController
{
class func catalogBreadcrumbs() -> [String] {
return ["Bottom Navigation", "Bottom Navigation (Swift)"]
}
class func catalogIsPrimaryDemo() -> Bool {
return false
}
func catalogShouldHideNavigation() -> Bool {
return true
}
}
And my simple view controllers that should be switched out by the tabs:
import Foundation
import MaterialComponents
import UIKit
class ICEChildBottomBarViewController: UIViewController
{
//let appBar = MDCAppBar()
//var colorScheme = MDCSemanticColorScheme()
var color: UIColor?
init( title: String, color: UIColor )
{
super.init(nibName: nil, bundle: nil)
self.title = title
self.color = color
}
#available(*, unavailable)
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = self.color
//appBar.addSubviewsToParent()
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden( true, animated: animated )
}
}
you can use this to switch the controller, the main code
class MaterialTabBarSimple: UITabBarController {
let bottomNavBar = MDCBottomNavigationBar()
// other code copy from Material doc
override func viewDidLoad() {
bottomNavBar = [ your tabBarItems ]
bottomNavBar.delegate = self
self.viewControllers = [ your controllers ]
}
}
extension MaterialTabBarSimple: MDCBottomNavigationBarDelegate {
func bottomNavigationBar(_ bottomNavigationBar: MDCBottomNavigationBar, didSelect item: UITabBarItem) {
print("did select item \(item.tag)")
self.selectedViewController = self.viewControllers![item.tag]
}
}
This code works fine for me for making an MDCBottomNavBar and switching between viewcontrollers. But make sure you are using TabBarController.
import UIKit
import MaterialComponents
class TabBarController: UITabBarController, MDCBottomNavigationBarDelegate {
let bottomNavBar = MDCBottomNavigationBar()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden( true, animated: animated )
}
//Initialize Bottom Bar
init()
{
super.init(nibName: nil, bundle: nil)
commonBottomNavigationTypicalUseSwiftExampleInit()
}
#available(*, unavailable)
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
commonBottomNavigationTypicalUseSwiftExampleInit()
}
// Bottom Bar Customization
func commonBottomNavigationTypicalUseSwiftExampleInit()
{
view.backgroundColor = .lightGray
view.addSubview(bottomNavBar)
// Always show bottom navigation bar item titles.
bottomNavBar.titleVisibility = .always
// Cluster and center the bottom navigation bar items.
bottomNavBar.alignment = .centered
// Add items to the bottom navigation bar.
let tabBarItem1 = UITabBarItem( title: "Status", image: nil, tag: 0 )
let tabBarItem2 = UITabBarItem( title: "Events", image: nil, tag: 1 )
let tabBarItem3 = UITabBarItem( title: "Contacts", image: nil, tag: 2 )
bottomNavBar.items = [ tabBarItem1, tabBarItem2, tabBarItem3 ]
// Select a bottom navigation bar item.
bottomNavBar.selectedItem = tabBarItem1;
bottomNavBar.delegate = self
}
func bottomNavigationBar(_ bottomNavigationBar: MDCBottomNavigationBar, didSelect item: UITabBarItem)
{
self.selectedIndex = item.tag
}
override func viewWillLayoutSubviews()
{
super.viewWillLayoutSubviews()
layoutBottomNavBar()
}
#if swift(>=3.2)
#available(iOS 11, *)
override func viewSafeAreaInsetsDidChange()
{
super.viewSafeAreaInsetsDidChange()
layoutBottomNavBar()
}
#endif
// Setting Bottom Bar
func layoutBottomNavBar()
{
let size = bottomNavBar.sizeThatFits(view.bounds.size)
let bottomNavBarFrame = CGRect( x: 0,
y: view.bounds.height - size.height,
width: size.width,
height: size.height )
bottomNavBar.frame = bottomNavBarFrame
}
}
Just create a regular navigation bar at the storyboard with the amount of items that you want. and then connect it to a controller like that one.
(here I used 3 items bottom bar. so as in the storyboard.)
//
// TabViewController.swift
// Test Navigation Bar
//
// Created by ido cohen on 25/11/2018.
// Copyright © 2018 IdoCohen. All rights reserved.
//
import UIKit
import MaterialComponents.MaterialBottomNavigation_ColorThemer
class TabViewController: UITabBarController, MDCBottomNavigationBarDelegate {
var colorScheme = MDCSemanticColorScheme()
let bottomNavBar = MDCBottomNavigationBar()
override func viewDidLoad() {
colorScheme.backgroundColor = .white
view.backgroundColor = colorScheme.backgroundColor
let tabBarItem1 = UITabBarItem(title: "Home", image: UIImage(named: "Home"), tag: 0)
let tabBarItem2 = UITabBarItem(title: "Messages", image: UIImage(named: "Email"), tag: 1)
let tabBarItem3 = UITabBarItem(title: "Favorites", image: UIImage(named: "Cake"), tag: 2)
tabBarItem3.selectedImage = UIImage(named: "Favorite")
bottomNavBar.items = [ tabBarItem1, tabBarItem2, tabBarItem3 ]
bottomNavBar.selectedItem = tabBarItem1
view.addSubview(bottomNavBar)
bottomNavBar.delegate = self
MDCBottomNavigationBarColorThemer.applySemanticColorScheme(colorScheme, toBottomNavigation: bottomNavBar)
}
func bottomNavigationBar(_ bottomNavigationBar: MDCBottomNavigationBar, didSelect item: UITabBarItem){
guard let fromView = selectedViewController?.view, let toView = customizableViewControllers?[item.tag].view else {
return
}
if fromView != toView {
UIView.transition(from: fromView, to: toView, duration: 0.3, options: [.transitionCrossDissolve], completion: nil)
}
self.selectedIndex = item.tag
}
func layoutBottomNavBar() {
let size = bottomNavBar.sizeThatFits(view.bounds.size)
let bottomNavBarFrame = CGRect(x: 0,
y: view.bounds.height - size.height,
width: size.width,
height: size.height)
bottomNavBar.frame = bottomNavBarFrame
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
layoutBottomNavBar()
}
}
I also used a transition in this example for incase you would like to have one. if not just delete it.

Resources