【问题标题】:Swift - Add same navigation bar items to every pageSwift - 向每个页面添加相同的导航栏项目
【发布时间】:2020-06-04 17:52:14
【问题描述】:

我正在尝试将相同的导航栏项目添加到我的应用程序中的每个选项卡。我目前在我的 homeController 中正确设置了它们,但我想将代码移动到一个单独的文件中,并在我想要的任何地方远程实现它。

例如:在导航栏的左侧添加一个 [搜索] 图标,而不必在每个 swift 文件中使用相同的代码。

我不知道我是否应该创建一个枚举、协议或类。最好的方法是什么?

let menuButton = UIButton(type: UIButtonType.system)
menuButton.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
menuButton.addTarget(self, action: #selector(openSearch), for: .touchUpInside)
menuButton.setImage(UIImage(named: "icon_search"), for: UIControlState())
let menuBarButtonItem = UIBarButtonItem(customView: menuButton)

navigationItem.leftBarButtonItems = [menuBarButtonItem]

func openSearch() {
 // Some code
}

【问题讨论】:

  • 您想在每个屏幕上都有不同的按钮操作还是在所有地方都完全相同?
  • 每个屏幕上的按钮操作相同。
  • 也许你应该尝试使用带有内置导航栏的 UINavigationController。

标签: ios swift


【解决方案1】:

对此我推荐的方法是创建一个基本视图控制器类,并让所有单独的视图控制器继承自此,而不是直接继承自 UIViewController。

虽然您可以按照 Umair 的建议进行快速而肮脏的扩展,但这对于其他应用程序并不实用,而基本视图控制器允许您基本上添加功能/自定义应用程序中所有视图控制器的任何方面的外观。

下面是一些示例代码:

class BaseViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let menuButton = UIButton(type: UIButtonType.system)
        menuButton.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
        menuButton.addTarget(self, action: #selector(openSearch), for:    .touchUpInside)
        menuButton.setImage(UIImage(named: "icon_search"), for: UIControlState())
        let menuBarButtonItem = UIBarButtonItem(customView: menuButton)

        navigationItem.leftBarButtonItems = [menuBarButtonItem
    }

    func openSearch() {

    }
}

然后对于您应用中的所有视图控制器,只需进行声明:

class SomeRandomViewController: BaseViewController { }

编辑:

正如 cmets 中正确指出的那样,您必须在每个 base 类中执行此操作。 (UIViewController、UITableViewController、UITabViewController 等......)有一种解决方法,但它通常被认为是 Objective-C 运行时的黑暗艺术之一。我当然是在谈论 method swizzling。以下代码实质上交换了UIViewController 的viewWillAppear: 和自定义方法的实现。如果正确完成是完全安全的,正确的实现如下所示。

extension UIViewController {
    public override class func initialize() {
        struct Static {
            static var token: dispatch_once_t = 0
        }

        // make sure this isn't a subclass
        if self !== UIViewController.self {
            return
        }

        dispatch_once(&Static.token) {
            let originalSelector = Selector("viewWillAppear:")
            let swizzledSelector = Selector("extended_viewWillAppear:")

            let originalMethod = class_getInstanceMethod(self, originalSelector)
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)

            let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

            if didAddMethod {
                class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
            } else {
                method_exchangeImplementations(originalMethod, swizzledMethod)
            }
        }
    }

    // MARK: - Method Swizzling

    func extended_viewWillAppear(animated: Bool) {
        self.extended_viewWillAppear(animated)

        // Call your code here that you want to run for all view controllers, table view controllers, tab view controllers etc...
    }
}

一些开发人员无法忍受上述概念,您应该意识到它可能会在未来的 iOS 版本中发生变化并中断。也就是说,它不会让您的应用被拒绝,因为它是一种实际的编程技术。

【讨论】:

  • 我应该如何处理我的应用程序中的某些控制器是 UITableViewControllers 甚至是 UICollectionViewControllers 的事实。这会产生很多继承错误。
  • @DennisvanMazijk 是的,这是唯一的缺点。 UITableViewController 不继承自 UIViewController,因此不会自动获得此功能。你需要为UITableViewController 和UICollectionViewController 创建一个类似的东西。它并不理想,但仍被广泛认为是实现此功能的正确/最佳方式。这是正确的 OO 方法。
  • 谢谢。我了解解决方案,所以我将看看如何调整我的代码以使其正常工作。
【解决方案2】:

对于 Swift 4

你可以通过扩展 UIViewController 轻松使用它

扩展 UIViewController {

func setupRemainingNavItems() {
    self.navigationController?.setNavigationBarHidden(false, animated: true)
    navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
    navigationController?.navigationBar.shadowImage = UIImage()
    let titleImageView = UIImageView(image: #imageLiteral(resourceName: "your-icon"))
    titleImageView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
    titleImageView.contentMode = .scaleAspectFit
    titleImageView.clipsToBounds = true
    navigationItem.titleView = titleImageView

    let button = UIButton(type: .custom)
    button.setImage(UIImage (named: "Group 6"), for: .normal)
    button.frame = CGRect(x: 0.0, y: 0.0, width: 30.0, height: 30.0)
    button.addTarget(self, action: #selector(tapbutton), for: .touchUpInside)
    let barButtonItem = UIBarButtonItem(customView: button)

    self.navigationItem.leftBarButtonItem = barButtonItem
}

@objc func tapbutton(){
    self.navigationController?.popViewController(animated: true)
    print("tap")
}

}

用途:

在 ViewDidLoad() 中调用它

setupRemainingNavItems()

更新了 Swift 5 的代码

首先全局声明一些变量,

 let window = UIApplication.shared.keyWindow

 let topPadding = window?.safeAreaInsets.top

 let bottomPadding = window?.safeAreaInsets.bottom

 let guide = window?.safeAreaLayoutGuide

 let height = guide?.layoutFrame.size.height

 let safeAreaWidth = guide?.layoutFrame.size.width

 let width = UIScreen.main.bounds.width/4

 let parentView = UIView(frame: CGRect(x: 0 , y:( UIScreen.main.bounds.height - 65) - bottomPadding!   , width: safeAreaWidth!, height: 65))

 let blurparentView = UIView(frame: CGRect(x: 0, y: 0, width: safeAreaWidth!, height: 60))

 let backImage = UIImageView(frame: CGRect(x: 7.5 , y: 20, width: 20, height: 20))

 let cartImage = UIImageView(frame: CGRect(x: 6.5 , y: 16.5, width: 25, height: 27))

 let cartLabel = UILabel(frame: CGRect(x: 14, y: -4, width: 14, height: 14))

 let lblWidth = UIScreen.main.bounds.width/2

 extension UIViewController {

 func navigationBarDesign() {

       let parentView = UIView(frame: CGRect(x: 0, y: topPadding!, width: safeAreaWidth!, height: 60))
       parentView.backgroundColor = hexStringToUIColor(hex: Colors.appColor)
       let back = UIView(frame: CGRect(x: 10, y: 0, width: 40, height: 60))
       let title = UIView(frame: CGRect(x: lblWidth - lblWidth/2, y: 0, width: lblWidth, height: 60))
       let cart = UIView(frame: CGRect(x: UIScreen.main.bounds.width - 50, y: 0, width: 40, height: 60))

       blurparentView.backgroundColor = hexStringToUIColor(hex: Colors.blackColor)
       backImage.image = #imageLiteral(resourceName: "Your-Icon")
       cartImage.image = #imageLiteral(resourceName: "Your-Icon")

       let titlelabel = UILabel(frame: CGRect(x: 0, y: 0, width: lblWidth, height: 60))
       titlelabel.font = UIFont(name: "Segoe UI", size: 20.0)
       titlelabel.text = "Home"
       titlelabel.textAlignment = .center
       titlelabel.textColor = .black



       let backButton = UIButton(frame: CGRect(x: 0, y: 0, width: width, height: 50))
       let cartButton = UIButton(frame: CGRect(x: 0, y: 0, width: width, height: 50))


       cartLabel.textAlignment = .center
       cartLabel.textColor = .white
       cartLabel.font = UIFont.boldSystemFont(ofSize: 8.0)
       cartLabel.setViewRounded()
       cartLabel.backgroundColor = hexStringToUIColor(hex: Colors.yellowColor)
       cartLabel.isHidden = false
       backButton.addTarget(self, action: #selector(clickBack), for: .touchUpInside)
      // cartButton.addTarget(self, action: #selector(clickCart), for: .touchUpInside)



       back.addSubview(backImage)
       back.addSubview(backButton)

       title.addSubview(titlelabel)

       cart.addSubview(cartImage)
       cart.addSubview(cartButton)
       cartImage.addSubview(cartLabel)

       parentView.addSubview(back)
       parentView.addSubview(title)
       parentView.addSubview(cart)
       parentView.addSubview(blurparentView)

       blurparentView.isHidden = true

       view.addSubview(parentView)
   }

   @objc func clickBack(){

       parentView.isHidden = false
       navigationController?.popViewController(animated: false)

   }
}

用途:

在 ViewDidLoad() 中调用它

导航栏设计()

【讨论】:

    【解决方案3】:

    对于未来的人

    如果subclassing 和hardcoding buttons 不是你的方式,这里是我的解决方案:

    1. 在Storyboard 中将所需按钮添加到navigation bar
    2. 将outlets/actions 连接到您的TabBarController
    3. 假设您的流量是TabBarController --> NavigationController --> tab1/tab2/tab3 in viewDidLoad() of TabBarController 添加:
    override func viewDidLoad() {
        super.viewDidLoad()
        if let viewControllers = viewControllers {
            for nvc in viewControllers.compactMap({$0 as? UINavigationController}) {
                let left = navigationItem.leftBarButtonItem
                let right = navigationItem.rightBarButtonItem
                nvc.navigationBar.topItem?.title = "Common Title"
                nvc.navigationBar.topItem?.leftBarButtonItem = left
                nvc.navigationBar.topItem?.rightBarButtonItem = right
            }
        }     
    }
    

    【讨论】:

      猜你喜欢
      • 2014-08-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-16
      • 1970-01-01
      • 2021-09-22
      • 2017-12-17
      • 1970-01-01
      • 2012-08-21
      相关资源
      最近更新 更多