【问题标题】:Switching View Controllers using MDCBottomNavigationBar on iOS在 iOS 上使用 MDCBottomNavigationBar 切换视图控制器
【发布时间】:2018-10-24 20:04:54
【问题描述】:

我正在尝试创建一个使用 Material Design 库底部导航功能的 iOS 应用。我可以获得一个带有底部导航栏的视图控制器来编译和显示,但是我无法添加其他视图控制器并在单击不同选项卡时在它们之间切换。我将所有内容简化为两个文件:一个是带有底部导航内容的入口视图控制器,另一个只是一个简单的视图控制器,我将其实例化 3 次以用作三个选项卡的目标。

目前,我的具体错误是:

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'

据我所知,这与我的标签视图有关。如果我注释掉 self.viewControllers = [...] 的行,那么它会加载得很好,但不会在视图控制器之间切换。

我不喜欢这种方法。如果有另一种方法可以做到这一点,我很想知道。我无法从文档中学到很多东西,但如果有其他类似选项卡的 Material Design 功能的文档,它们的工作原理非常相似,我认为这将为我指明正确的方向。

这是我的入口视图控制器。我使用其中一个示例作为基础并对其进行了大量修改。

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

还有我的简单视图控制器应该被标签切换出来:

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

【问题讨论】:

  • 你搞清楚了吗?

标签: ios swift material-design


【解决方案1】:

你可以用这个来切换控制器,主要代码

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

【讨论】:

    【解决方案2】:

    这段代码对我来说非常适合制作 MDCBottomNavBar 和在视图控制器之间切换。但请确保您使用的是 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
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      只需在情节提要上创建一个常规导航栏,其中包含您想要的项目数量。然后将其连接到类似的控制器。 (这里我使用了 3 个项目底部栏。所以在情节提要中。)

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

      我还在这个例子中使用了一个过渡,以防你想要一个。如果不只是删除它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-06
        • 1970-01-01
        • 1970-01-01
        • 2023-03-06
        • 1970-01-01
        • 2012-04-26
        相关资源
        最近更新 更多