iOS 横屏方向处理背后的实现方式

6

请问有人可以详细解释一下iOS在处理横向方向时,为什么会按照特定的框架和变换方式来进行处理吗?

我指的是通过在不同的视图生命周期方法中记录视图描述所展示出的行为:

viewDidLoad: "UIView: 0x1edb5ba0; frame = (0 0; 568 320); autoresize = W+H; layer = <CALayer: 0x1edb5c50>"

viewWillAppear: "UIView: 0x1edb5ba0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x1edb5c50>"

viewDidAppear: "UIView: 0x1edb5ba0; frame = (0 0; 320 568); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x1edb5c50>"

这样做的后果是微妙的,但非常有趣。
例如,我使用“初始界面方向”和“支持的界面方向”均设置为“横屏(右侧home按钮)”启动我的应用程序,
然后像这样显示我的根视图控制器:
self.viewController = [[MyViewController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = self.viewController;

这个.xib文件的方向被设置为横向,其框架大小为0、0、568、320。它能正确显示,并且我可以触摸屏幕上的所有点。
然而,当我这样呈现一个子视图时,问题就出现了:
SomeView *someView = [[SomeView alloc] initWithFrame:self.view.frame];
[self.view addSubview:someView];

现在,self.view.frame的值为(0 0; 320 568),而self.view.transform的值为transform = [0, 1, -1, 0, 0, 0]。最好情况下,最终结果是我只能触摸到刚刚显示的视图的左侧320像素,最糟糕的情况是视图的布局被破坏。

通过各种stackoverflow上的问题,我学会了正确的方法:

SomeView *someView = [[SomeView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:someView]

我还没有学到的是为什么,我对此非常好奇。
更让我好奇的是为什么在实例化和显示期间视图会被操纵。
已经有一段时间我没有使用过只有景观的应用程序了,但出于某种原因,我认为当前的实现与早期版本的iOS不同,这是正确的吗?
2个回答

0

Prince的回答很好。我想引用一下docs关于UIViewframe属性的警告:

警告:如果transform属性不是标识变换,则此属性的值未定义,因此应将其忽略。

另一方面,bounds属性以视图自己的坐标系表示,因此它永远不会改变,无论您设置哪个transform

更新:关于您最后一段的问题,是什么让您认为这种行为奇怪或已更改?iOS旋转视图层次结构的唯一方法是通过更改视图变换。因此,如果您希望所有不同视图控制器中的视图都朝向相同的方向,则应使用某种导航视图控制器或以模态方式呈现您的控制器。这样,每个控制器都将根据相同的规则旋转其视图,您就不必手动管理视图间的变换。

在早期,您可以看到像这样的代码:

UIViewController *ctrl = ...;
[window addSubview.ctrl.view];

这是在window拥有rootViewController属性之前,人们倾向于滥用此属性来展示他们自己的视图,在设备旋转时会导致意想不到的结果。如果你说你不再看到这段代码了,那肯定是一个变化,而且是一个更好的变化,因为那段代码是有问题的。所以人们学会了编写能够正确处理方向变化的代码。

如果你有一个所有UI都由游戏引擎渲染的游戏,你只需要一个(根)视图控制器。在这种情况下,你根本不需要担心变换。只需查询glView的边界并相应地调整你的glViewport和游戏内元素即可。由于glView在不同设备上具有不同的边界,只要你不对实际屏幕大小和视图当前方向做任何假设,你的游戏就会在所有设备上优雅地缩放。


0
根据苹果公司的说法,一款应用程序默认支持纵向和横向两种方向。当iOS设备的方向发生变化时,系统会发送UIDeviceOrientationDidChangeNotification通知,以便让任何感兴趣的方知道发生了变化。默认情况下,UIKit框架会监听此通知并使用它来自动更新界面方向。这意味着除了少数例外情况,您不需要处理此通知。 为什么必须给出边界而不是框架呢?原因很简单。一旦应用程序处于横向模式,其宽度和高度与纵向视图相比交换。这会导致行为不当。但是如果给出边界,则会考虑视图的方向并相应地采取高度和宽度。

当您的视图控制器非常接近视图层次结构的顶部(或者确实在顶部),您可能会发现宽度和高度出现了“交换”效果。这种交换通常表现在框架上,但不表现在视图的边界上。这是因为边界实际上是应用于框架的一些变换 -- 有时这些变换包括90度旋转(由于设备处于横向模式)。 请注意,检查框架属性的确切时间也很重要。如果您在视图加载后但在其出现在屏幕上之前检查属性,则可能会得到“错误”的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接