我需要执行一个动作(清空一个数组),当UINavigationController的后退按钮被按下,而按钮仍然导致堆栈上的前一个ViewController出现。我如何使用swift来实现这一点?


当前回答

Swift 5+(带警报控制的后退按钮)

override func viewDidLoad() {
        super.viewDidLoad()
        
        self.navigationItem.hidesBackButton = true
        let newBackButton = UIBarButtonItem(title: "<Back", style: UIBarButtonItem.Style.plain, target: self, action: #selector(PGWebViewController.back(sender:)))
        self.navigationItem.leftBarButtonItem = newBackButton
}


@objc func back(sender: UIBarButtonItem) {
    
    let alert = UIAlertController(title: "Warning!", message: "Your payment process is not completed yet. Do you want to go back?", preferredStyle: .alert)
        
         let ok = UIAlertAction(title: "OK", style: .default, handler: { action in
             _ = self.navigationController?.popViewController(animated: true)
         })
         alert.addAction(ok)
         let cancel = UIAlertAction(title: "Cancel", style: .default, handler: { action in
         })
         alert.addAction(cancel)
         DispatchQueue.main.async(execute: {
            self.present(alert, animated: true)
    })}

其他回答

我通过调用/重写viewWillDisappear来实现这一点,然后像这样访问navigationController的堆栈:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)

    let stack = self.navigationController?.viewControllers.count

    if stack >= 2 {
        // for whatever reason, the last item on the stack is the TaskBuilderViewController (not self), so we only use -1 to access it
        if let lastitem = self.navigationController?.viewControllers[stack! - 1] as? theViewControllerYoureTryingToAccess {
            // hand over the data via public property or call a public method of theViewControllerYoureTryingToAccess, like
            lastitem.emptyArray()
            lastitem.value = 5
        }
    }
}

Swift 5 __ Xcode 11.5

在我的情况下,我想做一个动画,当它完成后,返回。 一种覆盖后退按钮默认动作的方法 调用你的自定义动作是这样的:

     override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        setBtnBack()
    }

    private func setBtnBack() {
        for vw in navigationController?.navigationBar.subviews ?? [] where "\(vw.classForCoder)" == "_UINavigationBarContentView" {
            print("\(vw.classForCoder)")
            for subVw in vw.subviews where "\(subVw.classForCoder)" == "_UIButtonBarButton" {
                let ctrl = subVw as! UIControl
                ctrl.removeTarget(ctrl.allTargets.first, action: nil, for: .allEvents)
                ctrl.addTarget(self, action: #selector(backBarBtnAction), for: .touchUpInside)
            }
        }
    }


    @objc func backBarBtnAction() {
        doSomethingBeforeBack { [weak self](isEndedOk) in
            if isEndedOk {
                self?.navigationController?.popViewController(animated: true)
            }
        }
    }


    private func doSomethingBeforeBack(completion: @escaping (_ isEndedOk:Bool)->Void ) {
        UIView.animate(withDuration: 0.25, animations: { [weak self] in
            self?.vwTxt.alpha = 0
        }) { (isEnded) in
            completion(isEnded)
        }
    }

或者你可以使用这个方法一次来探索NavigationBar视图层次结构,并获得访问_UIButtonBarButton视图的索引,转换为UIControl,删除目标-动作,并添加你的自定义目标-动作:

    private func debug_printSubviews(arrSubviews:[UIView]?, level:Int) {
        for (i,subVw) in (arrSubviews ?? []).enumerated() {
            var str = ""
            for _ in 0...level {
                str += "\t"
            }
            str += String(format: "%2d %@",i, "\(subVw.classForCoder)")
            print(str)
            debug_printSubviews(arrSubviews: subVw.subviews, level: level + 1)
        }
    }

    // Set directly the indexs
    private func setBtnBack_method2() {
        // Remove or comment the print lines
        debug_printSubviews(arrSubviews: navigationController?.navigationBar.subviews, level: 0)   
        let ctrl = navigationController?.navigationBar.subviews[1].subviews[0] as! UIControl
        print("ctrl.allTargets: \(ctrl.allTargets)")
        ctrl.removeTarget(ctrl.allTargets.first, action: nil, for: .allEvents)
        print("ctrl.allTargets: \(ctrl.allTargets)")
        ctrl.addTarget(self, action: #selector(backBarBtnAction), for: .touchUpInside)
        print("ctrl.allTargets: \(ctrl.allTargets)")
    }
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingToParent {

        //your code backView
    }
}

在离开电流控制器之前,我需要显示警报。所以我是这样做的:

添加扩展UINavigationController与UINavigationBarDelegate 添加选择器到你的控制器导航

它的工作)

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        if let items = navigationBar.items, viewControllers.count < items.count {
            return true
        }

        let clientInfoVC = topViewController as? ClientInfoVC
        if clientInfoVC?.responds(to: #selector(clientInfoVC?.navigationShouldPopOnBack)) ?? false {
            clientInfoVC?.navigationShouldPopOnBack(completion: { isAllowPop in
                if isAllowPop {
                    DispatchQueue.main.async {
                        self.popViewController(animated: true)
                    }
                }
            })
        }

        DispatchQueue.main.async {
            self.popViewController(animated: true)
        }

        return false
    }
}

@objc func navigationShouldPopOnBack(completion: @escaping (Bool) -> ()) {
        let ok = UIAlertAction(title: R.string.alert.actionOk(), style: .default) { _ in
            completion(true)
        }
        let cancel = UIAlertAction(title: R.string.alert.actionCancel(), style: .cancel) { _ in
            completion(false)
        }
        let alertController = UIAlertController(title: "", message: R.string.alert.contractMessage(), preferredStyle: .alert)
        alertController.addAction(ok)
        alertController.addAction(cancel)
        present(alertController, animated: true, completion: nil)
    }

我通过以下方法做到了这一点:

斯威夫特3

override func didMoveToParentViewController(parent: UIViewController?) {
   super.didMoveToParentViewController(parent)

   if parent == nil {
      println("Back Button pressed.")
      delegate?.goingBack()
   }           
}

斯威夫特4

override func didMove(toParent parent: UIViewController?) {
    super.didMove(toParent: parent)

    if parent == nil {
        debugPrint("Back Button pressed.")
    }
}

不需要自定义后退按钮。