多个UIViewController使用addSubView,第二个 UIViewController 不响应旋转[转]
-----------------------------------
?
我张贴这因为后争夺这一问题的更好的部分的一天 (和很多、 很多的谷歌和计算器搜索),我终于找到了这个问题,但是我没有看到任何地方此解决方案。
我有 UINavigationController 作为根控制器的应用软件和两个 UIViewControllers 所拥有的根控制器。所有编程方式创建,我根本不使用界面生成器。这两个 UIViewControllers 成立,处理所有方向的旋转 (据称),但只根 UIViewController 旋转正确。对于第二个 UIViewController,?shouldAutoRotateToInterfaceOrientation
?触发一次,但永远不会触发再次,和 willRotateToInterfaceOrientation 永远不会触发的根本。
有问题的视图控制器已只标准控件的子视图: UILabel、 UITextView 和 UISwitch。作为子视图,它不包括 tabBar 或任何其他视图控制器。视图和 VC 都是在范围中,似乎另有健康。与该视图的所有交互除了旋转的都正常运行。
我特别呼吁:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications
和实施:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:@"UIDeviceOrientationDidChangeNotification" object:nil];
随着?didRotate
?方法不做差异。
在 iOS 5,如果您要手动添加意见它很有可能可能,你必须应用的方法addChildViewController:
------------------------------------
什么对象是dipatching的UIViewController旋转的方法调用,即负责:
- shouldAutorotateToInterfaceOrientation:?
- willRotateToInterfaceOrientation:时间:?
- willAnimateFirstHalfOfRotationToInterfaceOrientation:时间:?
- willAnimateSecondHalfOfRotationFromInterfaceOrientation:时间:?
- didRotateFromInterfaceOrientation:
我猜想,UIApplication(但也许是AppDelegate或UIWindow)。
?
看来,UIApplication是派遣一个信息,活动视图控制器。
但如何做你的视图控制器实例得到这些信息?
该邮件被转发到的第一个视图控制器的观点已被添加到UIWindow实例。
这可以归结为3个基本情况:
?
他们的观点是的ViewController?
直接添加到UIWindow?
(例如单一视图的应用程序)
?
在导航控制器?
导航的应用程序,则?
导航控制器转发?
信息的积极的意见看法?
控制器。
?
在一个标签栏标签栏控制器?
基于应用程序,那么标签栏?
控制器转发邮件?
活动的意见看法控制器(或?
积极导航控制器)。
?
您将有问题,如果你是建立一个与多个视图的应用程序,但不使用导航控制器或一个标签栏控制器。 如果您在交换意见,并出UIWindow实例手动,您将不会收到这些消息可靠。
?
?
?
首先重写UIViewController方法:
Java代码 ?
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { ?
?
?? ?return YES; ?
} ?
?你也可以根据toInterfaceOrientation的不同值来判断是否允许旋转。这个传入参数有四种取值:
Java代码 ?
UIInterfaceOrientationLandscapeLeft 横向Home键在左 ?
UIInterfaceOrientationLandscapeRight 横向Home键在右 ?
UIInterfaceOrientationPortrait 正常 ?
UIInterfaceOrientationPortraitUpsideDown 反向Home键在上 ?
?
可以在下面的方法中处理旋转后要重画的组件,或者重载另一个NIB文件。?
Java代码 ?
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { ?
// 重新加载一个Nib文件 ?
if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft || self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) { ?
?? ? ? ?[[NSBundle mainBundle] loadNibNamed:@"LoginViewLandscape" owner:self options:nil]; ?
?? ?}else { ?
?? ? ? ?[[NSBundle mainBundle] loadNibNamed:@"LoginView" owner:self options:nil]; ?
?? ?} ?
// 重写Toolbar ?
// Set Toolbar ?
?? ?UIBarButtonItem *newChat = [[UIBarButtonItem alloc] initWithTitle:@"新增" style:UIBarButtonItemStylePlain target:self action:@selector(createChat:)]; ?
?? ?UIBarButtonItem *refresh = [[UIBarButtonItem alloc] initWithTitle:@"刷新" style:UIBarButtonItemStylePlain target:self action:@selector(refresh:)]; ?
?? ?UIBarButtonItem *deleteChat = [[UIBarButtonItem alloc] initWithTitle:@"删除" style:UIBarButtonItemStylePlain target:self action:@selector(deleteChat:)]; ?
?
?? ?self.deleteItem = deleteChat; ?
?? ?self.deleteItem.enabled = NO; ?
?? ?UIBarButtonItem *fixedItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:NULL]; ?
?? ?if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { ?
?? ? ? ?fixedItem.width = 155; ?
?? ?}else { ?
?? ? ? ?fixedItem.width = 75; ?
?? ?} ?
?
?? ?NSArray *toolBarItems = [[NSArray alloc] initWithObjects:newChat, fixedItem, refresh, fixedItem, self.deleteItem, nil]; ?
?
?? ?[self setToolbarItems:toolBarItems]; ?
?? ?[toolBarItems release]; ?
?? ?[newChat release]; ?
?? ?[deleteChat release]; ?
?? ?[fixedItem release]; ?
?? ?[refresh release]; ?
} ?
----------------------------------------
?
做iOS开发的经常会和UIViewController打交道,从类名可知UIViewController属于MVC模型中的C(Controller),说的更具体点它是一个视图控制器,管理着一个视图(view)。
UIViewController的view是lazy loading的,当你访问其view属性的时候,view会从xib文件载入或者通过代码创建(覆盖loadView方法,自定义其view hierarchy),并返回,如果要判断一个View Controller的view是否已经被加载需要通过其提供的isViewLoaded方法来判断。?
view加载后viewDidLoad会被调用,这里可以进行一些数据的请求或加载,用来更新你的界面。?
当view将被加入view hierarchy中的时候viewWillAppear会被调用,view完成加入的时候viewDidAppear会被调用,同样当view将要从view hierarchy中移除的时候viewWillDisappear会被调用,完成移除的时候viewDidDisappear会被调用。?
当内存紧张的时候,所有的UIViewController对象的didReceiveMemoryWarning会被调用,其默认实现是 如果当前viewController的view的superview是nil的话,则将view释放且viewDidUnload会被调用,viewDidUnload中你可以进行后继的内存清理工作(主要是界面元素的释放,当再次加载的时候需要重建)。
如果想要展示一个View Controller,一般有如下一种途径
直接使用4种方法是比较危险的,上一级 View Controller并不能对当前View Controller的 生命周期相关的函数进行调用,以及旋转事件的传递等。
我们知道一个View可以将另一个View添加为子View(subview),构成一个View Hierarchy.当某一个View添加到window的View Hierarchy中时,将被“显示”。每一个View Controller管理着的其实就是一个View Hierarchy.而View Controller本身可以有Child View Controller,所以也存在一个 View Controller Hierarchy的概念,当View Controller收到上层传来的诸如旋转,显示事件的时候,需要传递给它的Child View Controller. 一般情况下,View Hierarchy 和 View Controller Hierarchy需要保持一致性,比如一个View Controller的view的superView是由其parent view controller管理着
一个iOS的app很少只由一个ViewController组成,除非这个app极其简单。 当有多个View Controller的时候,我们就需要对这些View Controller进行管理。 那些负责一个或者多个View Controller的展示并对其视图生命周期进行管理的对象,称之为容器,大部分容器本身也是一个View Controller,这样的容器可以称之为Container View Controller,也有极少数容器不是View Controller,比如UIPopoverController,其继承于NSObject。
我们常用的容器有 UINavigationController,UITabbarController等,一般容器有一些共同的特征:
从上面可以看出来,实现一个Container View Controller并不是一个简单的事情,好在iPhone的界面大小有限,一般情况下一个View Controller的view都是充满界面或者系统自带容器的,我们无需自己创建额外的容器,但是在iPad中情况就不同了。
在iOS 5之前框架并不支持自定义 Container View Controller, iOS 5开始开放了一些新的接口来支持支持自定义容器
addChildViewController:removeFromParentViewControllertransitionFromViewController:toViewController:duration:options:animations:completion:willMoveToParentViewController:didMoveToParentViewController:
其中前两个接口比较重要,可以直接改变View Controller 的 Hierarchy。
有点意外的是,在不做任何额外设置的情况下进行如下操作
[viewController.view addSubview:otherViewController.view]
iOS 5中otherViewController是可以立刻收到viewWillAppear和viewDidAppear的调用。
至于旋转事件的传递以及其他时机viewWillAppear viewDidAppear的调用是需要建立在 [viewController addChildViewController:otherViewController]基础上的。
当我们需要在iOS 4上实现自定义容器,或者有时候我们不想让viewWillAppear这类方法被自动调用,而是想自己来控制,这个时候我们就得需要手动来调用这些方法,而不是由框架去自动调用。 iOS 5中可以很方便的禁用掉自动调用的特性,覆盖automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers返回NO
但是单单覆盖这个方法在iOS5下还是有问题的,当执行下面的语句的时候
[viewController.view addSubview:otherViewController.view]
otherViewController还是是可以立刻收到viewWillAppear和viewDidAppear的调用。?
解决这一问题的方法就是在iOS5的时候调用[viewController.view addSubview:otherViewController.view]之前 进行如下操作
[viewController addChildViewController:otherViewController]
总的来说实现兼容iOS 4和iOS 5的容器有不少问题和注意点的
苹果对UIViewController以及其使用有着非常详细的文档?UIViewController Reference?,?ViewController Programming Guide。
------------------------------------------
?
对于iPhone app,UIViewController类提供了基本的视图管理模式。当设备改变方向的时候view controller的视图会自动随之旋转的。如果视图和子视图的autoresizing属性设置是对的,这时候视图又没有随着设备一起旋转,可能是以下的原因:
1.view controller没有完成代理方法
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
也要实现了shouldAutorotateToInterfaceOrientation方法,同时shouldAutorotateToInterfaceOrientation方法要返回YES来支持所有的旋转方向
?
2.view controller的UIView属性嵌入在UIWindow中,并非是一个附加的view controller
你可能会发现shouldAutorotateToInterfaceOrientation方法只会在view controller启动的时候被调用,不会因为设置的旋转而在被调用。这是因为view controller束缚着他们所管理的视图,view controller是用来处理时间的响应链的一部分。view controller是从UIResponder继承而来,同时他被插入到他所管理的视图和他的superview之间。因此,通常的做法是在你的app中有一个主view controller来作为响应链的一部分。通常来说会添加一个主view controller,例如UINavigationController,?UITabBarController或者UIViewController到UIWindow上。
例如
[myWindow addSubview:primaryViewController.view];?
如果你添加了另外一个view controller的UIView属性到UIWindow(anotherController和主view controller在同一个等级上)
[myWindow addSubview:anotherController.view];
anotherController将不会接受旋转事件,只有第一个view controller(primaryViewController)会接受旋转事件。
?
3.你添加了view controller的UIView属性到UIWindow作为subview,但是过早的release它。
UIWindow会retain视图,而不是view controller。你不能过早的release他。在UIApplicationDelegate子类中定义他为retain属性。
?
4.在UITabBarController或者UINavigationController中的子view controller没有对同一方向的支持。
为了确保所有的子view controller旋转正确,你的每一个view controller,每一个tab或者额navigation都要完成shouldAutorotateToInterfaceOrientation,而且必须支持对于同一方向的旋转,也就是说对于同一方向的位置要返回为YES。
?
5.重写-(id)init:或者?-(id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle?方法的时候没有调用super。
对于对象的初始化,在你的view controller的init或者initWithNibName方法中必须要调用super。