例外:无法在由控制器管理的UINavigationBar上手动设置委托 [英] Exception: Cannot manually set the delegate on a UINavigationBar managed by a controller

查看:161
本文介绍了例外:无法在由控制器管理的UINavigationBar上手动设置委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序以登录屏幕开始,该屏幕分割为 CreateRequestTableViewController ,并且所有内容都嵌入在导航控制器中,因此 CreateRequest的后退按钮 vc返回登录界面。我想询问用户他们是否确定他们在注销之前是否确定他们并且navc弹出vc以再次显示登录屏幕。

My app starts with a login screen that segues to CreateRequestTableViewController, and everything is embedded in a navigation controller, so the back button for the CreateRequest vc goes back to the login screen. I want to ask the user if they're sure they before they're logged out and the navcon pops the vc to show the Login screen again.

我已经得到了它可以使用下面的代码,除了在我重新登录并返回到 CreateRequest VC(创建新实例)后,我收到致命错误:

I've gotten it to work with the code below, except that after I log back in and move back to the CreateRequest VC (creating a new instance) I get a fatal error:

'NSInternalInconsistencyException',原因:'无法在由控制器管理的UINavigationBar上手动设置委托。'

这让我有点过头了。我已经尝试添加下面代码中包含的 deinit 方法,但没有运气。

This puts me in just a little bit over my head. I've tried adding the deinit method that's included in the code below, with no luck.

特别奇怪的是,它不会导致我分配代表的第一次时间崩溃(或者当我将其设置为nil时),因为错误的文本会建议。

It's especially strange that it doesn't crash the first time I assign the delegate (or when I set it to nil either), as the text of the error would suggest.

override func viewDidLoad() {
    super.viewDidLoad()
    navigationController?.navigationBar.delegate = self
}

deinit {
    navigationController?.navigationBar.delegate = nil
}

func confirmLogout() {
    let alert = UIAlertController(title: "Log Out", message: "Are you sure you want to log out?",  preferredStyle: .alert)

    let yesButton = UIAlertAction(title: "Log out", style: .destructive) { (_) in
        if let loginVC = self.navigationController?.viewControllers.first as? SignInTableViewController {
            self.navigationController?.popViewController(animated: true)
            loginVC.logOutAll()
        }
    }

    let noButton = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
    alert.addAction(yesButton)
    alert.addAction(noButton)
    present(alert, animated: true, completion: nil)

}

func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {        
    if navigationController?.viewControllers.last is CreateRequestTableViewController {
        confirmLogout()
        return false
    }
    navigationController?.popViewController(animated: true)

    return true
}


推荐答案

我通过派生自定义导航控制器来设置它自己的专用导航栏代表来解决这个问题。

I've solved this problem by deriving custom navigation controller that sets up it's own specialised navigation bar delegate.

此委托(转发器):


  • 仅设置一次,然后导航控制器对导航栏进行
    控制(在导航控制器的
    初始化程序中)。

  • 接收UINavigationBarDelegate消息并尝试
    首先调用导航栏代理,然后最终导致原始导航
    bar委托(UINavigationController)。

自定义导航控制器添加一个新的navigationBarDelegatepr可用于设置委托的operty。你应该在viewDidAppear中执行:animated:和viewWillDisappear:animated:methods。

The custom navigation controller adds a new "navigationBarDelegate" property that you can use to setup your delegate. You should do that in viewDidAppear:animated: and viewWillDisappear:animated: methods.

这是代码(Swift 4):

Here is the code (Swift 4):

class NavigationController : UINavigationController
{
    fileprivate var originaBarDelegate:UINavigationBarDelegate?
    private var forwarder:Forwarder? = nil

    var navigationBarDelegate:UINavigationBarDelegate?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        if navigationBar.delegate != nil {
            forwarder = Forwarder(self)
        }
    }
}

fileprivate class Forwarder : NSObject, UINavigationBarDelegate {

    weak var controller:NavigationController?

    init(_ controller: NavigationController) {
        self.controller = controller
        super.init()

        controller.originaBarDelegate = controller.navigationBar.delegate
        controller.navigationBar.delegate = self
    }

    let shouldPopSel = #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:))
    let didPopSel = #selector(UINavigationBarDelegate.navigationBar(_:didPop:))
    let shouldPushSel = #selector(UINavigationBarDelegate.navigationBar(_:shouldPush:))
    let didPushSel = #selector(UINavigationBarDelegate.navigationBar(_:didPush:))

    func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        if let delegate = controller?.navigationBarDelegate, delegate.responds(to: shouldPopSel) {
            if !delegate.navigationBar!(navigationBar, shouldPop: item) {
                return false
            }
        }

        if let delegate = controller?.originaBarDelegate, delegate.responds(to: shouldPopSel) {
            return delegate.navigationBar!(navigationBar, shouldPop: item)
        }

        return true
    }

    func navigationBar(_ navigationBar: UINavigationBar, didPop item: UINavigationItem) {
        if let delegate = controller?.navigationBarDelegate, delegate.responds(to: didPopSel) {
            delegate.navigationBar!(navigationBar, didPop: item)
        }

        if let delegate = controller?.originaBarDelegate, delegate.responds(to: didPopSel) {
            return delegate.navigationBar!(navigationBar, didPop: item)
        }
    }

    func navigationBar(_ navigationBar: UINavigationBar, shouldPush item: UINavigationItem) -> Bool {
        if let delegate = controller?.navigationBarDelegate, delegate.responds(to: shouldPushSel) {
            if !delegate.navigationBar!(navigationBar, shouldPush: item) {
                return false
            }
        }

        if let delegate = controller?.originaBarDelegate, delegate.responds(to: shouldPushSel) {
            return delegate.navigationBar!(navigationBar, shouldPush: item)
        }

        return true
    }

    func navigationBar(_ navigationBar: UINavigationBar, didPush item: UINavigationItem) {
        if let delegate = controller?.navigationBarDelegate, delegate.responds(to: didPushSel) {
            delegate.navigationBar!(navigationBar, didPush: item)
        }

        if let delegate = controller?.originaBarDelegate, delegate.responds(to: didPushSel) {
            return delegate.navigationBar!(navigationBar, didPush: item)
        }
    }
}

以下是用法:


  • 从UINavigationBarDelegate导出视图控制器

  • 将故事板中导航控制器的类名从UINavigationController更改为NavigationController。

  • 将以下代码放入视图控制器中

  • Derive your view controller from UINavigationBarDelegate
  • Change the class name of the navigation controller in your storyboard from UINavigationController to NavigationController.
  • Put the following code into your view controller

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    (navigationController as? NavigationController)?.navigationBarDelegate = self
}

override func viewWillDisappear(_ animated: Bool) {
    (navigationController as? NavigationController)?.navigationBarDelegate = nil
    super.viewWillDisappear(animated)
}


  • 实现一个或多个UINavigationBarDelegate方法你的视图控制器(这只是一个例子):

  • implement one or more of UINavigationBarDelegate methods in your view controller (this is just an example):

    func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        let alert = UIAlertController(title: "Do you really want to leave the page?", message: "All changes will be lost", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Stay here", style: .default, handler: nil))
        alert.addAction(UIAlertAction(title: "Leave", style: .destructive, handler: { action in
            self.navigationController?.popViewController(animated: true)
        }))
    
        self.present(alert, animated: true)
    
        return false
    }
    


  • 这篇关于例外:无法在由控制器管理的UINavigationBar上手动设置委托的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆