iPhone - 使用方向和多个视图
为了弄清楚这一点,我进行了长期而艰苦的搜索。我花了大约4-5个小时才终于找到解决方案。我将回答我自己的问题,努力为遇到同样问题的人提供支持。
因此,我创建了应用程序 Critical Mass Typer。这是一款以键盘为主要控制方式的打字游戏。单词向屏幕中心移动,那里有一个核心。你输入这个词,然后在它到达你的核心之前把它吹走。如果 4 个单词到达你的核心,你就会达到临界质量并爆炸(游戏结束)。
现在,我希望能够在游戏中支持横向和纵向模式。我提前考虑了这个问题,并设计了我的类,使其易于实现。设置游戏来做到这一点并不是问题。让视图正确旋转是。
很快出现的一件事是:任何为视图控制器的 shouldAutorotateToInterfaceOrientation 方法返回 NO 的视图都将取消其子视图的返回。
因此,我的主视图控制器 (CriticalMassViewController) 控制游戏的启动/菜单,并添加运行游戏模式的视图控制器 (CMGameViewController) 作为子视图。我只希望我的主视图控制器处于横向模式,因为如果我旋转它,动画/按钮都会从侧面运行。如果我希望将其设置为纵向模式,我必须创建另一个视图或更改屏幕上所有内容的位置。但是,我的游戏视图需要能够切换。为了实现这一点,您可以将返回值设置为您希望主视图所处的特定模式。然后告诉你的游戏视图不返回任何模式。之后,注册游戏视图以获取通知,然后自行处理旋转。
例如: CriticalMassViewController.m:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
CMGameViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
/*GUI setup code was here*/
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
-(void)didRotate:(NSNotification *)nsn_notification {
UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
/*Rotation Code
if(interfaceOrientation == UIInterfaceOrientationPortrait) {
//Portrait setup
} else if(interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
//Landscape setup
}
*/
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return NO;
}
好的,那么这里发生了什么?我的主视图控制器只想处于 LandscapeRight 模式。所以它保持这样,因为它从不注册任何其他方向来自动旋转。我的游戏视图控制器需要能够旋转。如果我对 shouldAutorotateToInterfaceOrientation 使用以下代码,
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
当 IPHONE 改变方向时,它不会旋转。这是因为它的超级视图表示它不想旋转。一旦超级视图返回 no,子视图甚至不会被询问。因此,我们游戏视图控制器只是说“不,我不想自动旋转到任何视图”。然后我们注册方向变化的通知,并调用我们自己的旋转方法。
最后几位代码是您自己处理旋转的方式。从 didRotate 内部调用rotateView。请注意,我只希望用户能够在选择难度后进行旋转,因此我的 didRotate 方法中有逻辑来确定它是否应该旋转。代码中的注释将解释为什么会发生某些事情。
-(void) rotateView:(UIInterfaceOrientation)uiio_orientation {
/*This line is very important if you are using the keyboard. This was actually the
largest problem I had during this entire ordeal. If you switch orientations while
the keyboard is already being displayed, it will stay in landscape orientation. To
get it to switch to portrait, we have to tell the status bar that its orientation
has changed. Once you do this, the keyboard switches to portrait*/
[UIApplication sharedApplication].statusBarOrientation = uiio_orientation;
//Get the view you need to rotate
UIView *portraitImageView = self.view;
/*Check to make sure that you are in an orientation that you want to adjust to. If
you don't check, you can make your view accidently rotate when the user orientates
their phone to a position you aren't expecting*/
if(uiio_orientation == UIInterfaceOrientationPortrait || uiio_orientation == UIInterfaceOrientationLandscapeRight) {
if(uiio_orientation == UIInterfaceOrientationPortrait) {
//This transform rotates the view 90 degrees counter clock wise.
portraitImageView.transform = CGAffineTransformMakeRotation(-M_PI/2);
} else if(uiio_orientation == UIInterfaceOrientationLandscapeRight) {
//This transform rotates the view back to its original position
portraitImageView.transform = CGAffineTransformMakeRotation(0);
}
CGRect frame = portraitImageView.frame;
frame.origin.y = 0;
frame.origin.x = 0;
frame.size.width = portraitImageView.frame.size.height;
frame.size.height = portraitImageView.frame.size.width;
portraitImageView.frame = frame;
}
}
我希望这对某人有帮助,我知道我度过了一段非常艰难的时光,尤其是从横向到纵向的键盘。
I searched long and hard in an effort to figure this out. It took about 4-5 hours before I finally managed to reach a solution. I will be answering my own question in an effort to provide support to anyone who comes to the same problem.
So, I created the application Critical Mass Typer. It is a typing game that utilizes the keyboard as the main method of control. Words move towards the center of the screen where you have a nucleus. You type the word, and blow the word away before it reaches your nucleus. If 4 words make it to your nucleus, you reach critical mass, and explode (game over).
Now, I wanted to be able to support both landscape and portrait mode in the game. I thought about it ahead of time, and designed my classes such that it would be easy to implement. Setting up the game to do so was not a problem. Getting the views to rotate correctly was.
One thing that came up rather quickly was: any view that returns NO for the shouldAutorotateToInterfaceOrientation method of a view controller will cancel out the returns of its subviews.
So, my main view controller (CriticalMassViewController) controls the launch/menu of the game, and adds, as a subview, a view controller that runs a game mode (CMGameViewController). I only want my main view controller to be in landscape mode because if I rotate it, the animation/buttons all run off the side. I would have to create another view or alter the position of everything on the screen if I wanted it to be set up for portrait mode. However, my game view needs to be able to switch. To accomplish this, you set the return value to the specific mode you want your main view to be. Then tell your game view to not return any mode. After that, register the game view for notifications, then handle the rotation on your own.
For Ex:
CriticalMassViewController.m:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
CMGameViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
/*GUI setup code was here*/
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
-(void)didRotate:(NSNotification *)nsn_notification {
UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
/*Rotation Code
if(interfaceOrientation == UIInterfaceOrientationPortrait) {
//Portrait setup
} else if(interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
//Landscape setup
}
*/
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return NO;
}
Ok, so what's happening here? My main view controller only wants to be in LandscapeRight mode. So it stays like that since it never registers for any other orientation to auto rotate to. My game view controller needs to be able to rotate. If I use the following code for shouldAutorotateToInterfaceOrientation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
IT WILL NOT ROTATE WHEN THE IPHONE CHANGES ORIENTATION. This is because its super view says it doesn't want to rotate. Once the super view returns no, the subview doesn't even get asked. So instead, we the game view controller just says No, i dont want to autorotate to any view. Then we register for the notification of an orientation change, and call our own rotate method.
These last bits of code are how you handle the rotation yourself. Call rotateView from inside didRotate. Note, I only want the user to be able to rotate after a difficulty is chosen, so I have logic inside my didRotate method to determine if it should even rotate. The comments in the code will explain why the certain things are happening.
-(void) rotateView:(UIInterfaceOrientation)uiio_orientation {
/*This line is very important if you are using the keyboard. This was actually the
largest problem I had during this entire ordeal. If you switch orientations while
the keyboard is already being displayed, it will stay in landscape orientation. To
get it to switch to portrait, we have to tell the status bar that its orientation
has changed. Once you do this, the keyboard switches to portrait*/
[UIApplication sharedApplication].statusBarOrientation = uiio_orientation;
//Get the view you need to rotate
UIView *portraitImageView = self.view;
/*Check to make sure that you are in an orientation that you want to adjust to. If
you don't check, you can make your view accidently rotate when the user orientates
their phone to a position you aren't expecting*/
if(uiio_orientation == UIInterfaceOrientationPortrait || uiio_orientation == UIInterfaceOrientationLandscapeRight) {
if(uiio_orientation == UIInterfaceOrientationPortrait) {
//This transform rotates the view 90 degrees counter clock wise.
portraitImageView.transform = CGAffineTransformMakeRotation(-M_PI/2);
} else if(uiio_orientation == UIInterfaceOrientationLandscapeRight) {
//This transform rotates the view back to its original position
portraitImageView.transform = CGAffineTransformMakeRotation(0);
}
CGRect frame = portraitImageView.frame;
frame.origin.y = 0;
frame.origin.x = 0;
frame.size.width = portraitImageView.frame.size.height;
frame.size.height = portraitImageView.frame.size.width;
portraitImageView.frame = frame;
}
}
I hope this helps someone, I know I had a very tough time with it, especially the keyboard going from landscape to portrait.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
用户编写的 UIViewController 并不意味着嵌套在单个屏幕上。我相信苹果的路线是“每个屏幕一个 UIViewController”。像这样的方向改变问题是重要原因之一。这是一个可以理解的“错误”,在某些情况下我很想犯一个错误:在这种情况下,诱惑是使用 UIViewController 作为 UIView 加载器。根本没有真正的理由使用 UIViewController 作为屏幕中的嵌套或可重用的“小部件”组件,只是为了访问它的视图,您并没有真正获得任何好处,因为所有这些有用的方便功能(例如方向更改) 、 viewWillAppear 、 viewWillDisappear 不会被除您自己的 UIViewController 之外的任何其他 UIViewController 自动调用!这些调用不是“广播”,而是从 UIViewController 父级“转发”到 UIViewController 子级。如果您编写自己的 UIViewController 子类,则您负责传递任何这些继承的方法调用。
您的内部 UIViewController 不是“未调用”,因为外部 UIViewController 回答“否”,它没有被调用,因为您自己没有将调用转发给嵌套的 UIViewController 。方向更改方法
shouldAutorotateToInterfaceOrientation
与其他 viewWill* 方法一样,不会广播到所有 UIViewController,它们会从顶层窗口沿层次结构从一个类传递到另一个类。内置 UIViewControllers(如 UINavigationControllers 和 UITabBarControllers)显式地将这些调用传递给它们的子级,这就是为什么它们到达时看起来像是广播,但是当您将自己的 UINavigationController 子类插入到 VC 层次结构时,您必须自己将这些调用传递给任何子级UINavigationControllers。如果您想编写自己的“全局控制器 VC”(例如 UITabBarController 替代品),那么您必须这样做。User written UIViewControllers are not meant to be nested on a single screen. I believe the Apple line is "one UIViewController per screen." Orientation change issues like this are one of the big reasons. This is an understandable "mistake", one I'm tempted to make myself in some cases: the temptation is to use a UIViewController as a UIView loader in this case. There's no real reason to use a UIViewController at all as a nested or reusable "widget" component in a screen, just to get to its view, you don't really get any benefit, because all of those useful handy functions like the orientation change, viewWillAppear, viewWillDisappear, will not be called automatically for you by any other UIViewController other than your own! These calls are not "broadcast" but "forwarded" from UIViewController parent to UIViewController child. And if you write your own UIViewController subclass, you're responsible for passing down any of these inherited method calls.
Your inner UIViewController was not "not called" because the outer UIViewController answered NO, it was not called because you did not forward the call to your nested UIViewController yourself. The orientation change method
shouldAutorotateToInterfaceOrientation
, just like the other viewWill* methods, are not broadcast to all UIViewControllers, they are passed down the hierarchy from the top level window, from class to class. Built-in UIViewControllers like UINavigationControllers and UITabBarControllers explicitly pass down these calls to their children, that's why it seems like a broadcast when they arrive but when you insert your own UINavigationController subclass to the VC hierarchy, you must yourself pass down these calls to any children UINavigationControllers. This is what you must do if you want to write your own "global controller VC" like a UITabBarController replacement.