【问题标题】:ios appDelegate window setRootViewController crashingios appDelegate 窗口 setRootViewController 崩溃
【发布时间】:2014-06-22 09:29:05
【问题描述】:

我正在开发一个通用的应用程序。在 iphone 版本中,我使用标准 UINavigationController 堆栈进行导航,一切都很好。但是,在 ipad 版本中,我对所有内容都使用 UISplitViewContoller 导航,但登录屏幕只是标准的 UIViewControllers。直到最近我不得不在细节方面稍微改变导航时,这一切都很好。基本上现在取决于主控中加载的内容,我必须在详细导航控制器中擦除堆栈并替换它的根。从那时起,有时当您点击注销并且每次会话超时并且用户被发送到登录屏幕时,应用程序崩溃:

* thread #1: tid = 0x140896, 0x0000000102143fcb libobjc.A.dylib`objc_msgSend + 11, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x0000000102143fcb libobjc.A.dylib`objc_msgSend + 11
frame #1: 0x000000010109ffb2 UIKit`-[UISplitViewController _calculateDelegateHiddenMasterOrientations] + 48
frame #2: 0x00000001010a284b UIKit`-[UISplitViewController hidesMasterViewInLandscape] + 42
frame #3: 0x000000010109fec7 UIKit`-[UISplitViewController _isMasterViewShownByDefault] + 75
frame #4: 0x000000010109fee7 UIKit`-[UISplitViewController _isMasterViewShown] + 23
frame #5: 0x00000001010a2e18 UIKit`-[UISplitViewController viewWillDisappear:] + 70
frame #6: 0x0000000100dd6e42 UIKit`-[UIViewController _setViewAppearState:isAnimating:] + 563
frame #7: 0x0000000100dd7ef8 UIKit`-[UIViewController viewWillMoveToWindow:] + 316
frame #8: 0x0000000100d30e00 UIKit`-[UIView(Hierarchy) _willMoveToWindow:] + 430
frame #9: 0x0000000100d2fd2a UIKit`__UIViewWillBeRemovedFromSuperview + 346
frame #10: 0x0000000100d2fb07 UIKit`-[UIView(Hierarchy) removeFromSuperview] + 67
frame #11: 0x0000000100d13f95 UIKit`-[UIWindow setRootViewController:] + 262
* frame #12: 0x0000000100047471 Callidus Enablement`__35+[CEInterfaceFunctions OpenLoginVC]_block_invoke(.block_descriptor=<unavailable>) + 609 at CEInterfaceFunctions.m:186
frame #13: 0x000000010336f851 libdispatch.dylib`_dispatch_call_block_and_release + 12
frame #14: 0x000000010338272d libdispatch.dylib`_dispatch_client_callout + 8
frame #15: 0x00000001033723fc libdispatch.dylib`_dispatch_main_queue_callback_4CF + 354
frame #16: 0x00000001024b6289 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
frame #17: 0x0000000102403854 CoreFoundation`__CFRunLoopRun + 1764
frame #18: 0x0000000102402d83 CoreFoundation`CFRunLoopRunSpecific + 467
frame #19: 0x00000001030fcf04 GraphicsServices`GSEventRunModal + 161
frame #20: 0x0000000100cdde33 UIKit`UIApplicationMain + 1010
frame #21: 0x000000010005f523 Callidus Enablement`main(argc=1, argv=0x00007fff5fbfec50) + 115 at main.m:16

我如何切换详细信息堆栈的示例:

if (![appDelegate.window.rootViewController isKindOfClass:[UISplitViewController class]]) {
    appDelegate.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main_iPad" bundle:nil] instantiateViewControllerWithIdentifier:@"split"];
}
UISplitViewController *splitViewController = (UISplitViewController *)appDelegate.window.rootViewController;
NSArray* VCs = splitViewController.viewControllers;
UINavigationController* masterNav = (UINavigationController*)VCs[0];
UINavigationController* detailNav = (UINavigationController*)VCs[1];

[masterNav popToRootViewControllerAnimated:NO];

[detailNav setViewControllers:@[[masterNav.storyboard instantiateViewControllerWithIdentifier:@"recent"]] animated:NO];

登录界面加载代码示例:

dispatch_async(dispatch_get_main_queue(), ^{
    AppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
    UIStoryboard* sb;
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
        sb = [UIStoryboard storyboardWithName:@"Main_iPad" bundle:nil];
    } else {
        sb = [UIStoryboard storyboardWithName:@"Main_iPhone" bundle:nil] ;
    }
    NSLog(@"sb:%@ appd:%@ win:%@ root:%@",sb,appDelegate,appDelegate.window,appDelegate.window.rootViewController);

    UIViewController* vc =[sb instantiateViewControllerWithIdentifier:@"login"];
    NSLog(@"vc:%@",vc);
    appDelegate.window.rootViewController = vc;//Crash happens HERE
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
    }
    [appDelegate.window makeKeyAndVisible];
});

任何帮助都会很棒!

【问题讨论】:

  • 在调用appDelegate.window.rootViewController = vc; 之前,您是否尝试将委托设置为零splitViewController.delegate = nil
  • @Visput 你拯救了我的一天!只需设置 nil 就不会崩溃。

标签: ios objective-c ipad uinavigationcontroller uisplitviewcontroller


【解决方案1】:

这可能与拆分控制器上尚未完成的动画或过渡有关。也许是警报视图?

无论如何,我建议在您未更改的rootController 的“顶部”以模态方式呈现登录控制器,这样您就不必玩弄控制器,并且可以动画输入/输出登录控制器。


例如:presentViewController:animated:completion:

【讨论】:

  • 似乎我没有考虑过我需要延迟的每种情况......我在所有注销调用中都设置了半秒延迟,现在它似乎工作得很好^^;谢谢!出于好奇,因为根是 UISplitVC,您如何建议将登录控制器“推到顶部”?
  • nvm,说话和点击太快了 >.
  • “模态”呈现。
  • 你的意思是使用 presentModelViewController:animated:?
  • 请问:presentViewController:animated:completion: ?
【解决方案2】:

您的代码存在各种问题。首先,您应该在应用委托的方法中设置窗口 rootController

application:didFinishLaunchingWithOptions:

您应该避免在应用启动的这个特殊时刻之外使用它。其次,考虑到您的第一段代码,您实际上在更改 rootController 什么?是不是你的台词:

  if (![appDelegate.window.rootViewController isKindOfClass:[UISplitViewController class]]) {

说你已经有了?为什么要做这个测试?

那么,这条线就没用了:

 [masterNav popToRootViewControllerAnimated:NO];

假设您没有在 masterNav 类的实例的 initWithCoder: 方法中做一些特殊的事情(如果我们相信放在实例前面的演员表,它是标准的 UINavigationController,但这可能只是隐藏故事板中的真相)。

现在,要真正回答您的问题,您需要指明“登录屏幕加载代码”的确切位置。在什么班,叫什么?为什么要在 dispatch_async(dispatch_get_main_queue(), ^{... 块内调用它?

通常,您应该在 application:didFinishLaunchingWithOptions: 方法中,无论如何都会在主线程(== 主队列)上调用该方法。

最后一个问题:为什么要在两个不同的地方设置窗口根控制器???对于您尝试解决的问题,您的代码被人为地复杂化了(否则这完全相关)。

【讨论】:

  • 在 application:didFinishLaunchingWithOptions: 我将 rootViewController 设置为登录控制器 (UIViewController) 或拆分控制器(如果他们的会话仍然良好)。现在,如果他们的会话超时或者他们想注销,我会返回主线程,根据他们的前进方向切换 rootViewController ...我已经做了一段时间的 ios 代码,有没有更好的方法来像这样换掉?
  • 或者你建议使用 presentModalViewController:animated:?
  • 对不起(我刚刚醒来):presentViewController:animated:completion: 是我的意思。
猜你喜欢
  • 1970-01-01
  • 2017-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-14
  • 1970-01-01
  • 2018-06-14
相关资源
最近更新 更多