我已经设置了一个非常简单的项目,没有故事板,一个窗口和一个UINavigationController包含一个普通的旧UIViewController作为rootViewController。在AppDelegate中,我将UINavigationController的委托设置为self并实现了
navigationController:didShowViewController:animated
包含1行:
NSLog("didShow viewController")
当我启动我的应用程序时,UINavigationControllerDelegate方法navigationController:didShowViewController:animated
被调用两次。
的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:
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的viewControllers。奇怪的是,UINavigationControllerDelegate方法在第一次显示UINavigationController时会触发两次,但在此之后只会触发一次。
任何人都有任何关于如何纠正这一点的见解?我认为基于文档的正确行为是navigationController:didShowViewController:animated
应该只在此示例应用程序中调用一次。我还有checke dot确保委托方法中的navigationController
和viewController
参数是同一个对象。
提前致谢!
编辑:这是每个电话的callstack。所有在UIKit代码,而不是我的!
第一个电话:
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"
答案 0 :(得分:1)
经过试验,我发现iOS 13使UITabBarController中的第一个UINavigationController的问题更加复杂。
不会为调用堆栈[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
。
我能想到的新解决方法仍然是在viewDidLoad
中设置委托,但尝试停止VC的第一个调用,而不是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
之外的其他对象不会被首次调用。
这是解决该问题的演示项目Demo
低于iOS 13 如果只需要
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool)
然后可以进行以下设置:
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, animated: Bool)
由于您设置的委托时间太晚,不会在第一次调用,而是第二次正常调用相同的ViewController
实例willShow
方法。
p.s。如果要在UINavigationController中实现didShow
方法,则必须这样设置委托:
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(animated)
方法被调用