首页 文章

黑屏 - superview在视图中是零

提问于
浏览
8

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

  • 有两个选项卡,并且2. tabs选项卡viewcontroller.view(ViewCont2)上有一个按钮 .

  • 此按钮通过presentModalViewController方法加载另一个viewcontroller(ModalViewCont)视图 .

  • ModalViewCont上有一个关闭按钮,它调用dismissModalViewControllerAnimated .

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

Problem

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

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

Things that did not work

  • 使用[self.view removeFromSuperview];在设置self.view = nil之前,

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

Solutions i considered;

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

_

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

Questions

  • 1-为什么superview没有恢复?我该怎么办? (这是我想要理解和解决的主要问题,而不是尝试替代方案......)

  • 2-我是否通过将nil分配给视图进行卸载而做错了什么?如果是这样,我应该如何在这种情况下正确卸载视图(选项卡式应用程序)?

  • 3- 1. solution 有什么问题吗?它看起来像一个kludge?是 assumption about superview and viewWillAppear 对吗?

EDIT : 似乎当viewDidLoad早于它应该被调用时(即当viewWillDisappear中的视图被填充而不是viewDidDisappear)时,不会设置superview .

6 回答

  • 1

    这看起来很奇怪,但你的建议(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模拟器中测试):

    • 打开模态视图

    • 模拟内存警告 - >这将卸载tabbarcontroller中的视图

    • 按主页按钮再次打开应用程序

    • 关闭模态视图 - >黑屏

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

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

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

  • -1

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

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

    • 实施实际加载,通过将UIViewController子类与nib文件相关联或手动实现 loadView .

    • (可选)实现 viewDidLoadviewWillUnloadviewDidUnload 回调,视图控制器在决定加载/卸载其视图时调用这些回调 .

    事实上,你无法完全控制何时调用上述回调,这会影响应该进入的内容 .

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

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

  • 1

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

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

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

  • 1

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

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

    与其他视图控制器不同,标签栏界面永远不应该作为另一个视图控制器的子项安装.UITabBarController类参考

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

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

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

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

相关问题