9

I have an application created from the tabbed application template. (ARC, iOS 4)

  • There are several tabs and there is a button on the 2. tabs viewcontroller.view(ViewCont2).
  • This button loads another viewcontroller's(ModalViewCont) view by presentModalViewController method.
  • There is a close button on ModalViewCont which calls dismissModalViewControllerAnimated.
  • In viewDidDisappear of ViewCont2, i am setting self.view = nil and other outlets to nil to unload the view so it will be fresh loaded next time it appears on screen. I am doing this because it inherits from a base class(BaseViewCont) which initializes some general properties of the view controller and adds some buttons, labels etc. in viewDidLoad method. So, ViewControllers that inherit from this base class may configure those properties differently as they wish in their viewDidLoad method.

Problem

Now, when ModalViewCont on screen, pressing the Home button to put application in background and after getting the application back, closing the ModalViewCont does not bring back the ViewCont2's view but a black screen with the tabbar at the bottom. The same thing happens without putting the application background/foreground; if other tabs tapped before tapping the 2. tab.(EDIT : This happens only if self.view set to nil in viewWillDisappear instead of viewDidDisappear.)

I determined that ViewCont2 loads a new view (checked it's reference) but view's superview is nil so the new view is not displayed but a black screen.

Things that did not work

  • Using [self.view removeFromSuperview]; before setting self.view=nil,
  • In viewWillAppear adding view to the parent; [self.parentViewController.view addSubview:self.view]; This one did not work smoothly, view placed slightly up of the screen. This is because there are several other superviews in the hierarchy.

Solutions i considered;

  • 1- If superview is nil in viewDidLoad, it becomes available in viewWillAppear (assumption). So, viewWillAppear method of ViewCont2 could be used to get the superview loaded correctly by the following;

_

if (self.view.superview == nil)
{
    self.tabBarController.selectedViewController = nil;
    self.tabBarController.selectedViewController = self;
}
  • 2- viewWillAppear method of base class could be used instead for initialization so there is no need to unload the view. So, performance could be optimized, it will not be unloaded each time view disappears. Also, it would be better to perform initialization only once by checking a flag, instead of performing it every time it appears.

Questions

  • 1- Why does not the superview restored? What should i do for it? (This is the main problem i want to understand and solve instead of trying alternatives...)
  • 2- Am i doing something wrong by assigning nil to view for unloading it? If so, how should i unload the view properly in such case like this(tabbed application)?
  • 3- Is anything wrong with the 1. solution? Does it seem like a kludge? Is that assumption about superview and viewWillAppear correct?

EDIT : It seems that when viewDidLoad is called earlier than it should(i.e when view nilled in viewWillDisappear instead of viewDidDisappear), superview is not set.

4

6 回答 6

7

看起来很奇怪,但是您的建议(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 可以随时重新配置而无需重新加载视图。

于 2012-08-24T21:46:55.217 回答
1

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

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

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

您无法完全控制何时调用上述回调,这一事实暗示了它们应该包含哪些内容。

在您的情况下,如果我理解正确,每当您的 ViewCont2 的视图消失时,您都希望将其重置,以便当它重新出现时它将处于某种“干净”状态。我会以某种方法实现此状态重置,并从 和 调用viewDidLoadviewDidDisappear。或者,您可以在viewWillAppear.

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

于 2012-08-24T22:05:01.030 回答
1

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

你可以玩我的项目,看看你认为它是否适合你。

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

于 2012-08-25T02:26:13.167 回答
1

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

我找到了其他解决方案;

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

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

  • 第二个似乎更合理。

.

- (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];
}
于 2012-08-31T16:56:08.660 回答
-1

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

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

请告诉我我是否正确理解您的问题

于 2012-08-19T00:04:03.600 回答