Switching View Controllers using MDCBottomNavigationBar on iOS

时光毁灭记忆、已成空白 提交于 2019-12-23 03:40:06

问题


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 )
    }
}

回答1:


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.




回答2:


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]
    }
}



回答3:


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
    }

}


来源:https://stackoverflow.com/questions/50342566/switching-view-controllers-using-mdcbottomnavigationbar-on-ios

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!