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.
Related
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
}
// ...
}
I am trying to add a button in my custom navigation bar, Problem is when I am adding bar button item to the navigation bar it doesn't allow me to create an #objc method, to avoid it I created a different class for objc methods but that doesn't seem to work either, code accepts the selector method but doesn't trigger when tapped during runtime.
Plan is to launch a Side menu on the Tap of this button, The button is added and visible, but the action doesn't work, any work around or help will be greatly appreciated.
struct CustomNavigationBar: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return CustomNavigationBar.Coordinator(parent: self)
}
// Properties
/// Ease of Use
var view : AnyView
var title : String //Will be location of the user
/// On Search and On Cancel Closures
var onSearch : (String) -> ()
var onCancel : () -> ()
var didTapMenu : () -> ()
// Require Closures on initialization
init(view: AnyView, title : String, onSearch : #escaping (String)->(), onCancel : #escaping () -> (), didTapMenu : #escaping () -> ()) {
self.view = view
self.title = title
self.onSearch = onSearch
self.onCancel = onCancel
self.didTapMenu = didTapMenu
}
// Integrating UIKit Navigation Controller with SwiftUI View
func makeUIViewController(context: Context) -> UINavigationController {
//Requires SwiftUIView
let childView = UIHostingController(rootView: view)
let controller = UINavigationController(rootViewController: childView)
// Navigation Bar Data
controller.navigationBar.topItem?.title = title
controller.navigationBar.prefersLargeTitles = false
// Navigation Bar Customization
controller.navigationBar.barTintColor = UIColor(Color.grubSoulRed)
controller.navigationBar.tintColor = UIColor.white
controller.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white, .font: UIFont(name: "Poppins-Bold", size: 17)!]
//Adding Navigation Button // ===> Problem Here
let menuButton = UIBarButtonItem(image: UIImage(named: "hamburgerMenu"), style: .plain, target: nil, action: #selector(navigationActions.hamburgerTapped(_:)))
childView.navigationItem.leftBarButtonItem = menuButton
// Search Bar
let searchController = UISearchController()
searchController.searchBar.placeholder = "Search here for products..."
/// Search Bar Customization
searchController.searchBar.searchTextField.backgroundColor = UIColor.white
searchController.searchBar.searchTextField.layer.masksToBounds = true
searchController.searchBar.searchTextField.layer.cornerRadius = 17
searchController.obscuresBackgroundDuringPresentation = true
/// Setting search bar delegate
searchController.searchBar.delegate = context.coordinator
/// Adding Search Bar
controller.navigationBar.topItem?.searchController = searchController
controller.navigationBar.topItem?.hidesSearchBarWhenScrolling = false
return controller
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
}
// Search Bar delegate
class Coordinator: NSObject, UISearchBarDelegate {
var parent : CustomNavigationBar
init(parent: CustomNavigationBar) {
self.parent = parent
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.parent.onSearch(searchText)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
self.parent.onCancel()
}
}}
open class navigationActions {
#objc func hamburgerTapped(_ sender : UIButton) {
print("hamburger tapped")
// Launch Hamburger menu
}}
No need to create a new class,
Declare hamburgerTapped function inside the Coordinator class and set the target as Coordinator class and access from the Coordinator
let menuButton = UIBarButtonItem(image: UIImage(named: "hamburgerMenu"), style: .plain, target: context.coordinator, action: #selector(context.coordinator.hamburgerTapped(_:))) // <--- Here!!
and inside the Coordinator class
// Search Bar delegate
class Coordinator: NSObject, UISearchBarDelegate {
// Other code
#objc func hamburgerTapped(_ sender : UIButton) { // <-- Here!!
print("hamburger tapped")
// Launch Hamburger menu
}
}
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)
I know this question has been asked before but nothing worked for me and I had to ask it again.
I want an image as my back button in navigation bar, just want to change the appearance of the back button. I don't want to add a button and add selectors for it.
I tried the following code:
let backImage = UIImage(named: "Back_button")
let backAppearance = UIBarButtonItem.appearance()
backAppearance.setBackButtonBackgroundImage(backImage, for: .normal, barMetrics: .default)
navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItem.Style.plain, target: nil, action: nil)
I also tried setting the back image and back mask using storyboard but both these approaches place a black circle on my back image.
I tried setting another image as back mask by setting its alpha content equal to zero using the code but it didn't work either.
please help.
let backButton = UIBarButtonItem()
backButton.title = "Back"
backButton.image = UIImage(named: "Back_button")
self.navigationController?.navigationBar.topItem?.backBarButtonItem = backButton
You can do this to customize your Back button. And you don't have to worry about adding selectors.
This code works with Swift 5.
let backButton: UIButton = UIButton()
backButton.setImage(UIImage(named: "back"), for: UIControl.State())
backButton.addTarget(self, action:#selector(SearchResultsViewController.onBack), for: UIControl.Event.touchUpInside)
let leftBarButtonItem = UIBarButtonItem(customView: backButton)
navigationItem.leftBarButtonItem = leftBarButtonItem
I used this code to customize the back button on only one of my views:
self.navigationController?.navigationBar.topItem?.backButtonTitle = ""
let backButton = UIBarButtonItem(image: UIImage(named: "back"), style: .plain, target: self, action: #selector(goBack))
navigationItem.leftBarButtonItem = backButton
#objc func goBack() {
self.navigationController?.popViewController(animated: true)
}
Create a custom class for define navigation bar traits
Create an extension to UINavigationController for configure it
import UIKit
private final class MyNavigationBarTraits {
public var backIndicatorImage: UIImage?
public var backIndicatorTransitionMaskImage: UIImage?
public func apply(to navigationBar: UINavigationBar) {
navigationBar.backIndicatorImage = backIndicatorImage
navigationBar.backIndicatorTransitionMaskImage = backIndicatorTransitionMaskImage
}
public init(navigationBar: UINavigationBar) {
backIndicatorImage = navigationBar.backIndicatorImage
backIndicatorTransitionMaskImage = navigationBar.backIndicatorTransitionMaskImage
}
}
public typealias Callback<T> = (_: T) -> Void
public extension UINavigationController {
private struct AssociationKeys {
static var navigationBarTraits = "ws_nc_navigationBarTraits"
}
private var navigationBarTraits: MyNavigationBarTraits? {
get {
return objc_getAssociatedObject(self, &AssociationKeys.navigationBarTraits) as? MyNavigationBarTraits
}
set {
objc_setAssociatedObject(self, &AssociationKeys.navigationBarTraits, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
func configureBar(block: Callback<UINavigationBar>) {
navigationBarTraits = MyNavigationBarTraits(navigationBar: navigationBar)
block(navigationBar)
}
func resetBar() {
navigationBarTraits?.apply(to: navigationBar)
navigationBarTraits = .none
}
}
And then you can configure your navigation bar in your ViewController's viewWillAppear (for example tintColor)
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.configureBar { navigationBar in
// You can customize your navigation bar in here!
navigationBar.tintColor = .red
}
}
If you want to use this customization just in one View Controller you should reset bar in your View Controller's viewWillDisappear
public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.resetBar()
}
Simply Add Below Methods in Your ViewController :
func setLeftBarBackItem() {
let leftBarBackItem = UIBarButtonItem(image: #imageLiteral(resourceName: "imgBack"), style: .plain, target: self, action: #selector(self.clickToBtnBackItem(_:)))
self.navigationItem.leftBarButtonItem = leftBarBackItem
}
func clickToBtnBackItem(_ sender: UIBarButtonItem) {
view.endEditing(true)
_ = navigationController?.popViewController(animated: true)
}
func setTranspertNavigation()
{
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.view.backgroundColor = .clear
}
Inside Your ViewController's ViewDidLoad Method, Set backButton As :
self.navigationController?.isNavigationBarHidden = false
AppDelegate.shared().setupNavigationBar()
setLeftBarBackItem()
setTranspertNavigation()
self.title = "Title Here"
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.