【问题标题】:black screen - superview is nil in viewWillAppear黑屏 - viewWillAppear 中的 superview 为零
【发布时间】:2012-08-12 21:27:14
【问题描述】:

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

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

问题

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

我确定 ViewCont2 加载了一个新视图(检查了它的引用),但视图的超级视图为零,因此新视图不显示,而是黑屏。

没用的东西

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

我考虑过的解决方案;

  • 1- 如果 viewDidLoad 中的 superview 为 nil,则它在 viewWillAppear 中变为可用(假设)。因此,可以使用 ViewCont2 的 viewWillAppear 方法来正确加载父视图;

_

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 没有设置。

【问题讨论】:

  • 因为我没有你的代码或基类,我想把问题最小化一点,所以我可以编码和呈现。有 tabViewController,3 个选项卡,第二个选项卡有一个按钮,你点击按钮,一个新的视图以模态方式呈现。 modalView 有一个导致它被关闭的按钮,而您想要发生的是 viewController2 的新实例进入视图,一切顺利且用户不明智。请指教。
  • 您准确定义了它。我在子类中没有什么特别之处。只需使用选项卡式应用模板并从它的 nib 创建模态视图并使用 presentmodalview 方法。
  • 我在呈现视图而不是在呈现视图上调用解除方法。可能是导致问题的原因。我还没有时间测试它。
  • 你应该尽快测试一下,因为赏金在 3 天后到期,如果你有一个可能的修复待定,没有人会尝试调试这个。它只会过期,对任何人都没有任何好处。
  • 我曾尝试直接调用 parentViewControllerdismissModal... 并通过 parent 的委托调用它,但它们都没有帮助。

标签: ios objective-c view superview


【解决方案1】:

我找到了其他解决方案;

  • 第一个导致警告:“应用程序窗口在应用程序启动结束时应该有一个根视图控制器”,尽管有根视图控制器。

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

  • 第二个似乎更合理。

.

- (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];
}

【讨论】:

    【解决方案2】:

    我找到了 UITabBarController 错误的实际解决方案(内存警告,应用程序进入后台/前台,关闭模式)。使用 UITabBarController 作为根视图控制器是该错误的原因。因此,我们可以使用另一个视图控制器作为根视图控制器并从中显示标签栏。我已经在 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];
    }
    

    【讨论】:

      【解决方案3】:

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

      你可以和my project一起玩,看看你认为它是否适合你。

      编辑:我对所选答案的评论是,这种技术现在显然有效,但我自己很难遵循它。我的项目中的代码以简单直接的方式使用系统 - 当模态视图被告知关闭自身时,它会调用一个方法(可以在任何类中)将新视图添加到导航控制器的数组中,然后自行关闭.有一段时间,同时有两个视图控制器,新的堆叠在旧的之上。当新的视图控制器出现时,基于看到一个标志,它会在后台默默地从导航栏的堆栈中删除不需要的视图控制器,然后噗,它就消失了。

      【讨论】:

        【解决方案4】:

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

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

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

        您无法完全控制何时上述回调将被调用,这一事实暗示了应该进入它们的内容。

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

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

        【讨论】:

          【解决方案5】:

          看起来很奇怪,但是您的建议 (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. 关闭模态视图 -> 黑屏

          通常您可以假设在 viewDidLoad 中设置了视图属性,而在 viewWillAppear + viewDidAppear 中,视图已添加到视图层次结构中;所以那时superview应该在那里(这里superview是类UIViewControllerWrapperView的tabbarcontroller的私有视图)。然而,在我们的例子中,尽管视图被重新加载(在应用程序恢复时),但它并没有添加到视图层次结构中,从而导致黑屏。这似乎是 UITabBarController 中的一个错误。

          解决方法强制再次执行外观选择器。所以 viewWillAppear 将被再次调用,这一次有一个超级视图。 viewDidAppear 也会被调用两次!

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

          【讨论】:

            【解决方案6】:

            我认为您不应该将视图分配为零。 如果我理解你的话,你想在每次视图出现时刷新/重新加载内容。 因此,与其将视图设置为 nil,不如尝试刷新它。您可以通过添加:

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

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

            【讨论】:

            • 刷新并没有帮助我想让它重新加载到它的超级视图中。事实上,它的超级视图通常(并非总是)恢复,但在应用程序确实发送到后台并进入前台后它并没有恢复。
            猜你喜欢
            • 2015-05-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-06-15
            相关资源
            最近更新 更多