黑屏 - 在viewWillAppear中,superview是零

时间:2012-08-16 13:52:48

标签: ios objective-c view superview

我有一个从选项卡式应用程序模板创建的应用程序。 (ARC,iOS 4)

  • 有几个标签,并且2.标签viewcontroller.view(ViewCont2)上有一个按钮。
  • 此按钮通过presentModalViewController方法加载另一个viewcontroller(ModalViewCont)视图。
  • ModalViewCont上有一个关闭按钮,调用dismissModalViewControllerAnimated。
  • 在ViewCont2的viewDidDisappear中,我将self.view = nil和其他出口设置为nil以卸载视图,以便下次显示在屏幕上时它将被新加载。我这样做是因为它继承自基类(BaseViewCont),它初始化视图控制器的一些常规属性,并在viewDidLoad方法中添加一些按钮,标签等。因此,从这个基类继承的ViewControllers可以在viewDidLoad方法中以不同的方式配置这些属性。

问题

现在,当ModalViewCont在屏幕上,按Home键将应用程序置于后台并在获得应用程序后,关闭ModalViewCont不会带回ViewCont2的视图,而是带有底部标签栏的黑屏。没有放置应用程序背景/前景就会发生同样的事情;如果在点击2.标签之前点击了其他标签。(编辑:只有在viewWillDisappear中将self.view设置为nil而不是viewDidDisappear时才会发生这种情况。)

我确定ViewCont2加载了一个新视图(检查了它的引用)但是视图的superview是nil所以新视图不会显示而是显示黑屏。

无效的事情

  • 使用[self.view removeFromSuperview];在设置self.view = nil之前,
  • 在viewWillAppear中向父级添加视图; [self.parentViewController.view addSubview:self.view];这个没有顺利工作,视图略微放在屏幕上。这是因为层次结构中还有其他几个超级视图。

我考虑过的解决方案;

  • 1-如果superview在viewDidLoad中为nil,则在viewWillAppear (假设)中可用。因此,ViewCont2的viewWillAppear方法可用于通过以下方式正确加载superview;

_

if (self.view.superview == nil)
{
    self.tabBarController.selectedViewController = nil;
    self.tabBarController.selectedViewController = self;
}
  • 2- viewWillAppear基类的方法可以用来代替初始化,因此不需要卸载视图。因此,可以优化性能,每次视图消失时都不会卸载。此外,最好只通过检查一个标志执行一次初始化,而不是每次出现时执行它。

问题

  • 1-为什么superview没有恢复?我该怎么办呢? (这是我想要理解和解决的主要问题,而不是尝试替代方案......)
  • 2-我是通过分配nil来查看卸载它而做错了吗?如果是这样,我应该如何在这种情况下正确卸载视图(选项卡式应用程序)?
  • 3- 1是否有问题。溶液?它看起来像一个kludge?关于superview和viewWillAppear 的假设是正确的吗?

编辑:似乎早于调用viewDidLoad时(即在viewWillDisappear而不是viewDidDisappear中调用视图时),不会设置superview。

6 个答案:

答案 0 :(得分:6)

看起来很奇怪,但你的建议(1)确实是解决这个问题的正确方法:

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    if (!self.view.superview) { // check if view has been added to view hierarchy
        self.tabBarController.selectedViewController = nil;
        self.tabBarController.selectedViewController = self;
    }
}

你的第二个建议对性能有好处(因为视图加载是一项昂贵的操作) - 但它无法解决问题。在以下情况下,您也可以使用黑屏而不将视图设置为nil(在iOS模拟器中测试):

  1. 打开模态视图
  2. 模拟内存警告 - >这将卸载tabbarcontroller中的视图
  3. 按主页按钮再次打开应用程序
  4. 关闭模态视图 - >黑屏
  5. 通常,您可以假设在viewDidLoad中设置了view属性,并且在viewWillAppear + viewDidAppear中,视图已添加到视图层次结构中;所以superview应该在那时(这里superview是类UIViewControllerWrapperView的tabbarcontroller的私有视图)。但是在我们的情况下,虽然重新加载视图(在应用程序恢复时),但它不会添加到视图层次结构中,从而导致黑屏。这似乎是UITabBarController中的一个错误。

    该解决方法强制再次执行外观选择器。因此,将再次调用viewWillAppear,这次使用superview。 viewDidAppear也会被调用两次!

    将self.view设置为nil是可以的,但在大多数情况下不应该是必需的。让系统决定何时卸载视图(iOS可以在内存变低时卸载视图)。视图控制器代码的设计应该能够在不重新加载视图的情况下随时重新配置UI。

答案 1 :(得分:1)

您无法完全控制何时加载和卸载视图,并且您不应自己手动加载/卸载视图。

相反,您应该将视图加载/卸载视为完全取决于UIViewController的内容,而您只负责:

  • 通过将UIViewController子类与nib文件相关联或通过手动实现loadView来实现实际加载。
  • 可选择实施viewDidLoadviewWillUnloadviewDidUnload回调,当 决定加载/卸载其视图时,视图控制器会调用这些回调。< / LI>

当上述回调将被调用时,你无法完全控制这一事实会对应该进入的内容产生影响。

在您的情况下,如果我理解正确,每当ViewCont2的视图消失时,您想要重置它,以便当它再次出现时它将处于某种“干净”状态。我会在某种方法中实现此状态重置,并从viewDidLoadviewDidDisappear调用它。或者,您可以在viewWillAppear中使用“干净”逻辑。

或者您可能只想在点击当前按钮时清理ViewCont2的视图?在这种情况下,请在viewDidLoad中以及点击按钮时清除视图。

答案 2 :(得分:1)

我提供的是当模态视图控制器处于活动状态并且您关闭视图时,您将新视图添加到导航视图控制器viewControllers,然后告知该视图删除其前一个。

您可以使用my project来查看您认为它是否适合您。

编辑:我对所选答案的评论是,这项技术现在显然有效,但我自己很难跟进它。我的项目中的代码以简单直接的方式使用系统 - 当模态视图被告知自己解散时,它调用一个方法(可以在任何类中),为导航控制器的数组添加一个新视图,然后解散自己。有一段时间,同时有两个视图控制器,新的视图控制器堆叠在旧视图控制器上。当新的视图控制器出现时,基于它静默地看到一个标志,并在幕后从nab条的堆栈中删除不需要的viewController,而poof,它就会消失。

答案 3 :(得分:1)

我找到了UITabBarController错误的实际解决方案(内存警告,应用程序返回/前台,关闭模式)。使用UITabBarController作为根视图控制器是bug的原因。因此,我们可以使用另一个视图控制器作为根视图控制器并从中显示标签栏。我在iOS 5.1模拟器上测试了它。

当然,额外的UIViewController的开销受到争议。此外,它反对Apple文档;

  

与其他视图控制器不同,标签栏界面不应该作为另一个视图控制器的子项安装。UITabBarController Class Reference

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // A root view controller other than the actual UITabBarController is required.
    self.window.rootViewController = [[UIViewController alloc] init];
    [self.window makeKeyAndVisible];    
    self.tabBarController = [[UITabBarController alloc] init];
    self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, ..., nil];

    [self.window.rootViewController 
        presentModalViewController:self.tabBarController animated:NO];
}

答案 4 :(得分:0)

我找到了其他解决方案;

  • 第一个引发警告:“虽然有根视图控制器,但应用程序窗口应该在应用程序启动结束时具有根视图控制器”。

  • 虽然看起来像kludgy,但临时视图控制器将与第一个一起发布。

  • 第二个似乎更合理。

- (void) tabBarBlankScreenFix1
{
    self.window.rootViewController = [[UIViewController alloc] init];
    [self.window makeKeyAndVisible];
    [self.window addSubview:self.tabBarController.view];            
    self.window.rootViewController = self.tabBarController;

}

- (void) tabBarBlankScreenFix2
{
    self.window.rootViewController = [[UIViewController alloc] init];
    [self.window makeKeyAndVisible];
    [self.window addSubview:self.tabBarController.view];
}

答案 5 :(得分:-1)

我认为你不应该将视图分配给nil。 如果我理解正确,您希望每次出现视图时刷新/重新加载内容。 因此,不应将视图设置为nil,而应尝试刷新它。您可以通过添加:

来实现
    - (void)viewWillAppear{
    [self.view setNeedsDisplay];}

如果我理解你的问题,请告诉我