iOS:iOS 4.3和5.0之间的不同addSubview行为

而在iOS 4.3编码之前,我发现一个视图控制器的视图添加到另一个视图与[superview addSubView:controller.view] ,控制器实例将不会收到-viewWillAppear/viewDidAppear消息,比我发现同样的问题,在某些线程堆栈溢出。 之后,我-viewWillAppear/-viewDidAppear根据需要手动调用-viewWillAppear/-viewDidAppear

但是,升级到iOS 5.0 ,一些活泼的UIView行为发生了。 最后我发现,在iOS 5中, [superview addSubView:controller.view]会自动发送一个-viewWillAppear/-viewDidAppear消息给控制器实例,再加上我的手动调用,每次控制器动作时会有两个重复的消息。

而且我也发现了一个类似的问题: iOS 5:-viewWillAppear在解除iPad中的模式之后不会被调用

现在,问题是,search苹果的文件后,我没有find任何明确的文件差异有关这些问题。 我甚至想知道这是否是iOS 5.0中的有保证的视图生命周期行为。

有没有人修复类似的问题或find一些关于这些差异的指导 因为我想在4.x & 5.x iOS运行我的应用程序。

在iOS 4中,添加或删除视图层次结构中的视图时,必须手动调用-viewWillAppear-viewWillDisappear等。 如果在窗口层次结构中添加或删除视图,则会在iOS 5中自动调用它们。 幸运的是,iOS 5在UIViewController中有一个方法,您可以重写以恢复到iOS 4的工作状态。只需将其添加到您的UIViewController

 -(BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers { return NO; } 

只要你同时支持iOS 4和iOS 5,这可能是最简单的解决scheme。一旦你放弃对iOS 4的支持,你可能会考虑修改你的代码,以便在交换视图时使用更新的方法。

编辑2012年2月5日

显然这个函数需要使用addChildViewController:方法将子视图控制器添加到主视图控制器中。 这个方法在iOS4中不存在,所以你需要做这样的事情:

  if ([self respondsToSelector:@selector(addChildViewController:)] ) { [self addChildViewController:childViewController]; } 

感谢所有纠正我的人。

这可能不是你想要的答案,但是我有同样的问题。

在我的情况下,当我添加一个视图控制器的视图作为子视图的另一个视图控制器的视图,子视图收到viewWillAppear只在iOS 5.0而不是iOS 4.X.

所以我添加了一个恶劣的条件。

 [self.view addSubview:self.viewController.view]; if ([[[UIDevice currentDevice] systemVersion] compare:@"5.0"] == NSOrderedAscending) { [self.viewController viewWillAppear:animated]; } 

从iOS 5.0开始, Apple提供了一种实现自定义容器视图控制器(如UINavigationController或UITabController)的方法 。 我认为这个变化影响viewWillAppear被调用时。

如果我们使用-[UIViewController addChildViewController:]这个问题是可以解决的。

上面的答案略有不完整。 假设您有2个视图控制器,ControllerA和ControllerB。

ControllerA.view已经添加到窗口(它是父窗口),并且您想要将ControllerB.view添加为ControllerA的子视图。

如果您没有首先将ControllerB作为ControllerA的子级添加,则自动ForwardAppearanceAndRotationMethodsToChildViewController将被忽略,并且您仍将被iOS5调用,这意味着您将调用您的视图控制器callback两次。

ControllerA中的示例:

 - (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers { return NO; } - (void)viewDidLoad { [super viewDidLoad]; self.controllerB = [[ControllerB alloc] initWithNibName:@"ControllerB" bundle:nil]; [self.view addSubview:self.controllerB.view]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.controllerB viewWillAppear:animated]; } 

在ControllerB NSLogging in viewWillAppear:

 - (void)viewWillAppear:(BOOL)animated { NSLog("@ControllerB will appear"); } 

这将导致iOS5只显示两次NSLog消息。 即你自动ForForAppearanceAndRotationMethodsToChildViewControllers已被忽略。

为了解决这个问题,你需要添加controllerB作为控制器a的孩子。

回到ControllerA的课堂上:

 - (void)viewDidLoad { [super viewDidLoad]; self.controllerB = [[ControllerB alloc] initWithNibName:@"ControllerB" bundle:nil]; if ([self respondsToSelector:@selector(addChildViewController:)]) [self addChildViewController:self.controllerB]; [self.view addSubview:self.controllerB.view]; } 

现在,iOS4和iOS5都可以按照预期的方式工作,而无需借助检查iOS版本string的可怕手段,而是检查我们之后的function是否可用。

希望这可以帮助。

这是iOS5的行为:
viewWillAppear,viewDidAppear,…会在addSubView:iOS5之后自动执行。
所以对于iOS5来说,不需要像iOS 5.0那样手动执行这些方法。

修复可能是:

 if ([[UIDevice currentDevice].systemVersion doubleValue] < 5.0) { ...execute viewWillAppear or other } 

通过这种方法,你知道哪个操作系统使用和把条件如果是小于5.0或其他

[[UIDevice currentDevice] systemVersion]

view{Will,Did}Appearview{Will,Did}Disappear视图控制器上的function而不是视图。 这些函数被SDK提供的视图控制器调用,这些视图控制器应该pipe理其他视图控制器,例如UITabBarControllerUINavigationBarController

如果您自己pipe理子视图控制器,则必须明确地调用这些控制器(并按照正确的顺序 – 尽pipe您应该有一个很好的理由来做到这一点)。 一种模式化的观点是,当解散一种模态观点时没有得到这些电话,只是因为没有人可以称之为模态观点。 将根视图控制器封装在一个UINavigationController (如果你喜欢,隐藏导航栏),然后打开一个模式视图控制器。 一旦被解雇或stream行, viewWillAppear将被调用。

审查所有的证据后,我认为最好的事情不是使用viewDidAppear等受这个ios 4 / ios 5错误影响的意见。 而是自定义类(如viewDidAppearCustom),并自己调用它。 这样你可以保证苹果不会再次改变sdk并把你搞乱。 这里有一个很好的博客来讨论这个问题:

http://gamesfromwithin.com/view-controller-notification-changes-on-ios5