UINavigationControllerDelegate方法被调用两次 [英] UINavigationControllerDelegate method called twice

查看:109
本文介绍了UINavigationControllerDelegate方法被调用两次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我建立了一个非常简单的项目,没有情节提要,一个窗口和一个UINavigationController,其中包含一个普通的旧UIViewController作为rootViewController.在AppDelegate中,我将UINavigationController的委托设置为self并实现了

I have set up a very simple project with no storyboard, one window and one UINavigationController containing a plain old UIViewController as rootViewController. In the AppDelegate I've set the UINavigationController's delegate to self and implemented

navigationController:didShowViewController:animated ,其中包含1行:

NSLog("didShow viewController")

启动我的应用程序时,UINavigationControllerDelegate方法 navigationController:didShowViewController:animated 被调用两次.

When I launch my app the UINavigationControllerDelegate method navigationController:didShowViewController:animated gets called twice.

AppDelegate:

AppDelegate:


import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UINavigationControllerDelegate {

    var window: UIWindow?
    var vc1: FirstViewController?
    var nav1: UINavigationController?

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        NSLog("didShow viewController")
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        vc1 = FirstViewController()
        nav1 = UINavigationController(rootViewController: vc1!)
        nav1?.delegate = self

        window = UIWindow(frame: UIScreen.main.bounds)
        if let window = window {
            window.backgroundColor = UIColor.white
            window.rootViewController = nav1
            window.makeKeyAndVisible()
        }

        return true
    }
}

FirstViewController:

FirstViewController:


import UIKit

class FirstViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.blue
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

我已经在更复杂的环境中进行了尝试-一个带有UITabBarController和2个UINavigationControllers作为UITabBarController的viewController的应用程序.奇怪的是,UINavigationControllerDelegate方法在第一次显示UINavigationController时会触发两次,但之后只会触发一次.

I have tried this in more complicated environments - An app with a UITabBarController and 2 UINavigationControllers as the viewControllers for the UITabBarController. Oddly the UINavigationControllerDelegate methods fire twice the first time a UINavigationController is shown, but will only fire once after that.

任何人都对如何解决此问题有任何见解?根据文档,我认为正确的行为是 navigationController:didShowViewController:animated 在此示例应用程序中应仅被调用一次.我还检查了点,确保委托方法中的 navigationController viewController 参数是同一对象.

Anyone have any insight as to how to correct this? What I believe to be correct behavior based on the documentation is that navigationController:didShowViewController:animated should be only called once in this sample app. I have also checke dot make sure the navigationController and viewController params in the delegate method are the same object.

提前谢谢!

这是每个调用的调用堆栈.全部包含在UIKit代码中,而不是我的!

Here's the callstack from each call. All in UIKit code, not mine!

第一次通话:


29 elements
  - 0 : "0   ???                                 0x0000000115145377 0x0 + 4648620919"
  - 1 : "1   ???                                 0x0000000115145462 0x0 + 4648621154"
  - 2 : "2   Test                                0x0000000105f22d00 main + 0"
  - 3 : "3   Test                                0x0000000105f21e11 _TToFC4Test11AppDelegate20navigationControllerfTCSo22UINavigationController7didShowCSo16UIViewController8animatedSb_T_ + 97"
  - 4 : "4   UIKit                               0x0000000106bab7a8 -[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] + 1660"
  - 5 : "5   UIKit                               0x0000000106e8839e -[UINavigationTransitionView _notifyDelegateTransitionDidStopWithContext:] + 421"
  - 6 : "6   UIKit                               0x0000000106e88677 -[UINavigationTransitionView _cleanupTransition] + 629"
  - 7 : "7   UIKit                               0x0000000106a58f07 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 222"
  - 8 : "8   UIKit                               0x0000000106a54bcb +[UIViewAnimationState popAnimationState] + 305"
  - 9 : "9   UIKit                               0x0000000106e8810b -[UINavigationTransitionView transition:fromView:toView:] + 2582"
  - 10 : "10  UIKit                               0x0000000106bb01d1 -[UINavigationController _startTransition:fromViewController:toViewController:] + 3301"
  - 11 : "11  UIKit                               0x0000000106bb06b3 -[UINavigationController _startDeferredTransitionIfNeeded:] + 843"
  - 12 : "12  UIKit                               0x0000000106bb17f1 -[UINavigationController __viewWillLayoutSubviews] + 58"
  - 13 : "13  UIKit                               0x0000000106da32bc -[UILayoutContainerView layoutSubviews] + 231"
  - 14 : "14  UIKit                               0x0000000106a9020b -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1268"
  - 15 : "15  QuartzCore                          0x000000010bfbf904 -[CALayer layoutSublayers] + 146"
  - 16 : "16  QuartzCore                          0x000000010bfb3526 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 370"
  - 17 : "17  QuartzCore                          0x000000010bfb33a0 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24"
  - 18 : "18  QuartzCore                          0x000000010bf42e92 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 294"
  - 19 : "19  QuartzCore                          0x000000010bf6f130 _ZN2CA11Transaction6commitEv + 468"
  - 20 : "20  QuartzCore                          0x000000010bf6fb37 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 115"
  - 21 : "21  CoreFoundation                      0x000000010910f717 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23"
  - 22 : "22  CoreFoundation                      0x000000010910f687 __CFRunLoopDoObservers + 391"
  - 23 : "23  CoreFoundation                      0x00000001090f4038 CFRunLoopRunSpecific + 440"
  - 24 : "24  UIKit                               0x00000001069c702f -[UIApplication _run] + 468"
  - 25 : "25  UIKit                               0x00000001069cd0d4 UIApplicationMain + 159"
  - 26 : "26  Test                                0x0000000105f22d37 main + 55"
  - 27 : "27  libdyld.dylib                       0x000000010a19a65d start + 1"
  - 28 : "28  ???                                 0x0000000000000001 0x0 + 1"
  

第二次通话:


20 elements
  - 0 : "0   ???                                 0x00000001151456e7 0x0 + 4648621799"
  - 1 : "1   ???                                 0x00000001151457d2 0x0 + 4648622034"
  - 2 : "2   Test                                0x0000000105f22d00 main + 0"
  - 3 : "3   Test                                0x0000000105f21e11 _TToFC4Test11AppDelegate20navigationControllerfTCSo22UINavigationController7didShowCSo16UIViewController8animatedSb_T_ + 97"
  - 4 : "4   UIKit                               0x0000000106ba949b -[UINavigationController viewDidAppear:] + 421"
  - 5 : "5   UIKit                               0x0000000106b7595e -[UIViewController _setViewAppearState:isAnimating:] + 704"
  - 6 : "6   UIKit                               0x0000000106b7863b __64-[UIViewController viewDidMoveToWindow:shouldAppearOrDisappear:]_block_invoke + 42"
  - 7 : "7   UIKit                               0x0000000106b76a7b -[UIViewController _executeAfterAppearanceBlock] + 86"
  - 8 : "8   UIKit                               0x00000001069d992f _runAfterCACommitDeferredBlocks + 634"
  - 9 : "9   UIKit                               0x00000001069c67bc _cleanUpAfterCAFlushAndRunDeferredBlocks + 532"
  - 10 : "10  UIKit                               0x00000001069e957d __84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke_2 + 155"
  - 11 : "11  CoreFoundation                      0x000000010910fb5c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12"
  - 12 : "12  CoreFoundation                      0x00000001090f4e54 __CFRunLoopDoBlocks + 356"
  - 13 : "13  CoreFoundation                      0x00000001090f45ee __CFRunLoopRun + 894"
  - 14 : "14  CoreFoundation                      0x00000001090f4016 CFRunLoopRunSpecific + 406"
  - 15 : "15  GraphicsServices                    0x000000010b100a24 GSEventRunModal + 62"
  - 16 : "16  UIKit                               0x00000001069cd0d4 UIApplicationMain + 159"
  - 17 : "17  Test                                0x0000000105f22d37 main + 55"
  - 18 : "18  libdyld.dylib                       0x000000010a19a65d start + 1"
  - 19 : "19  ???                                 0x0000000000000001 0x0 + 1"

推荐答案

经过试验,我发现iOS 13使UITabBarController中第一个UINavigationController的问题更加复杂.

After experimenting, I found that iOS 13 had made this issue for the first UINavigationController in UITabBarController more complicated.

不会为调用堆栈调用第一个UINavigationController [UINavigationController viewDidAppear:]

the first UINavigationController won't be called for the call stack [UINavigationController viewDidAppear:]

class ViewController: UIViewController {

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

}

extension ViewController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        print("willShow")
    }

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        print("didShow")
    }

}

输出将为

willShow
didShow -> this one is from call stack `[UINavigationController __viewWillLayoutSubviews]`

第一个标签.

这意味着,如果您使用的是iOS 13以下的旧解决方法,则第一次显示第一个VC,而不会调用第一个VC的 didShow .

Which means that if you are using the old workaround down below below iOS 13 then the first time show first VC and only first VC's didShow won't be called.

我能想到的新变通办法仍然是在 viewDidLoad 中设置委托,但尝试停止VC的第一个调用,而不是在TabBarVC中的第一个调用.

the new workaround I can think of will be still set the delegate in viewDidLoad but try to stop the first call of VC other than first in TabBarVC.

将按此顺序调用 didShow

LAUNCH APP

VC1 willShow 
VC1 didShow `[UINavigationController __viewWillLayoutSubviews]`

SELECT SECOND TAB

VC2 willShow 
VC2 didShow `[UINavigationController __viewWillLayoutSubviews]`
VC2 didShow `[UINavigationController viewDidAppear:]` -> this is the one I try to get rid off

SELECT BACK TO FIRST TAB

VC1 willShow 
VC1 didShow `[UINavigationController viewDidAppear:]`

SELECT BACK TO SECOND TAB

VC2 willShow 
VC2 didShow `[UINavigationController viewDidAppear:]`

因此设置一个标志,以使除第一次UINavigationControllers的 didShow 之外的其他对象不会被首次调用.

so make a flag to let other than first UINavigationControllers' didShow not be called only for the first time.

这是解决该问题的演示项目演示

here is the demo project to solve it Demo

低于iOS 13如果您只需要

below iOS 13 if you only need

func navigationController(_ navigationController:UINavigationController,didShow viewController:UIViewController,动画:Bool)

然后可以进行以下设置:

then there is work around to set like this:

class ViewController: UIViewController {

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

}

extension ViewController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        // do something
    }

}

但是有一个问题

func navigationController(_ navigationController:UINavigationController,willShow viewController:UIViewController,animation:Bool)

,因为您将委托设置得太晚,但是第二次相同的 ViewController 实例 willShow 方法会正常调用.

will not be called at the first time cause you set delegate too late, but the second time the same ViewController instance willShow method get called normally.

p.s.如果要在UINavigationController中实现 didShow 方法,则必须这样设置委托:

p.s. if you want to implement didShow method in UINavigationController, you have to set delegate like this:

class NavigationController: UINavigationController {

    override func viewDidAppear(_ animated: Bool) {
        delegate = self
        super.viewDidAppear(animated)
    }

}

extension NavigationController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        print("will")
    }

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        print("did")
    }

}

因为 didShow 方法在 super.viewDidAppear(动画)

这篇关于UINavigationControllerDelegate方法被调用两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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