如果 你 曾经 用 用户界面套件 开发过 桌面窗口应用程序(比如:Windows,Mac OS X,Linux,Qt 或者 GTK+),你 就应该了解到 在窗口的尺寸改变时 我们设计的用户界面 也要能 正确地 改变 自己的尺寸。在这个过程中 我们 要确定 窗口中 哪些肉眼可见的部件 要移动位置,哪些 要根据窗口的变化 改变 自己的尺寸。
有 一件事情 很明显:在iphone 和 ipad这种运行iOS的设备上 窗口 并不能 像桌面应用程序一样 允许 用户 改变。然而 这样的设备 可以感知 物理上的转动,从而 允许 应用程序 以竖屏模式 或者 横屏模式 运行。既然 iOS设备 有 这样的能力,那么 我们 很有 必要 使 我们的应用程序用户界面布局 根据 设备的旋转 而 改变。
接下来 我们 要了解 设备转动 和 用户界面重新布局的机制。
首先 启动 Xcode。然后 创建 一个单一视图应用程序(Single View Application)。最后 将 产品名称 和 物件类型名称前缀 修改为 layout。
接着 点选 layoutViewController.xib这个文件,将 一个按钮 拖、放 到视图上。
Xcode 提供的应用程序模板 默认 支持 旋转特性。然而 并不是 所有的应用程序 都适合 支持 旋转特性。如果 同时 支持 横屏模式 和 竖屏模式 在用户体验上 不能获得 任何提升,那么 我们 就应该禁用 旋转特性。如果 要禁用 旋转特性,我们 需要 在模板代码中 做出 小小的修改。
配备网膜屏幕的iphone型号 在竖屏模式下的尺寸 是 960像素高、640像素宽,在横屏模式下的尺寸 是 640像素高、960像素宽。老式的iphone型号 在竖屏模式下的尺寸 是 480像素高、320像素宽,在横屏模式下的尺寸 是 320像素高、480像素宽。如果 你的应用程序 要显示 标准状态栏,那么 实际可用的屏幕尺寸 就会减少。
当设备 监测到 自己 被旋转 到新的方向时,就会对 当前的视图控制器 采取 shouldAutorotateToInterfaceOrientation这项措施。为了改变 应用程序 对旋转特性的支持,我们 需要 重新 编写 shouldAutorotateToInterfaceOrientation这项措施的内容。
幸运的 是 在layoutViewController.m这个文件中,Xcode 已经 为我们 创建了 shouldAutorotateToInterfaceOrientation这项措施的模板:
这项措施 仅仅 附带 一个参数 那就是 UIInterfaceOrientation类型interfaceOrientation。参数interfaceOrientation的值 可以 是 以下的四个常量:
这四个常量 分别 代表
shouldAutorotateToInterfaceOrientation:这项措施中的
就相当于
其中的变量interfaceOrientation 代表 当前设备的方向。这里的if()语句 先判断 当前的设备方向 是否是 竖屏上下颠倒。如果 当前的设备方向 不是 竖屏上下颠倒,那么 就 将 常量 YES 传递回去;如果 当前的设备方向 是 竖屏上下颠倒,则 将 常量 NO 传递回去。
如果 shouldAutorotateToInterfaceOrientation这项措施 产生 YES这个结果,那么 当前的视图 就会旋转 到 跟设备方向一致的方向;如果 这项措施 产生 NO这个结果,那么 当前视图的方向 就会 纹丝不动。
为了 使 我们的视图 能够 支持 任何方向的旋转,我们 只需要 将 shouldAutorotateToInterfaceOrientation:这项措施 改写成 这样:
这样的话 无论 变量interfaceOrientation的值 是多少,这项措施产生的结果 都是 常量YES。这 就意味着 设备的当前方向 无论 朝 哪个方向,视图 都会转向 当前设备的方向。接着 编译 并且 运行 程序。
如果 你 在真机上 测试 这个程序,你 只要将 你的iphone或者ipod touch 旋转到 某个方向,这个程序的视图 就会旋转 到相应的方向。如果 你 在iOS模拟器上 测试 这个程序,你 只需要 在按住 Command键的同时,按 方向键左键 或者 右键,就可以将 iOS模拟器 向左旋转 或者 向右旋转。
为了 使 对旋转的控制 更加容易,所有 UIView类型的物件,也就是 肉眼可见的物件,都有 一个特性 叫做 “自动改变尺寸”。自动改变尺寸这项特性 允许 我们 定义 在主视图尺寸 改变的情况下,主视图上的子视图 如何改变 尺寸 和 位置。
我们 先点选 layoutViewController.xib这个文件,将 其 载入 界面创建器。再在视图上 添加 一个按钮,就像 这样:
接着 我们 编译 并且 运行 这个程序,我们 可以看到 这样的结果:
然后 将 iOS模拟器 旋转 90度,我们 可以看到 这样的结果:
这 就是 典型的启用了旋转支持 而 用户界面 却没有 重新布局的例子。旋转 设备后,只有 上方的按钮 可见,而 这个按钮的位置 却没有改变,本来 这个按钮 应该 保持 在中间的位置的。下方的按钮 也没有改变 位置,因此 被移出到了 屏幕可见区域以外。
为了 保证 用户界面的正确布局,我们 需要对 两个按钮的“自动改变尺寸”设定 进行修改。我们 先点击 上方的按钮,然后 点击 Xcode窗口右方面板中的尺寸查看器:
在这个窗口中 最让人感兴趣的部分 就是 Autosizing这个标签上方的白色方框。外层的矩形 代表 主视图,里层的矩形 代表 子视图。子视图改变尺寸 和 位置的任何设置 都是 相对主视图而言的。在代表子视图的矩形里,有 两条互相交叉且两端带有箭头的线段。垂直的线段 和 水平的线段 分别 代表 你所选取的视图的宽度 和 长度。如果 这两条线段 是 虚线段,就表明 你所选取的子视图的尺寸 不会 随着主视图尺寸的改变 而 改变;如果 这两条线段 是 实线段,则表明 你所选取的子视图的尺寸 会 随着主视图尺寸的改变 而 改变。Example这个标签上方的图像 则展示了 当前设置的效果。其中 红色的方框 代表 子视图,而 白色的方框 代表 主视图。
通过设置 一个视图物件所包含的UIViewAutoResizingFlexibleHeight 和 UIViewAutoResizingFlexibleWidth这两个变量的值 也可以决定 这个视图的尺寸 是否 跟着其主视图尺寸的改变 而 改变。
在Autosizing这个标签上方的区域中 外层矩形的各条边 与 里层矩形的各条边之间的线段 代表 主视图各条边 与 子视图各条边之间的距离。如果 这些线段 是 实线段,那么 主视图 改变 尺寸时,子视图各条边 与 主视图各条边之间的距离 不变;如果 这些线段 是 虚线段,那么 主视图 改变 尺寸时,子视图各条边 与 主视图各条边之间的距离 就会 按照比例 改变。
要决定 子视图各条边 与 主视图各条变之间的距离 是否 随着主视图尺寸的改变 而 改变,还可以 通过设置 下面四个变量的值 来办到:
在这里这个例子当中 我们 需要 两个按钮 相对于主视图的宽度来说 始终 保持 在正中央。于是 在Autosizing这个标签上方的区域中 我们 需要 点击 外层方框各条边 和 里层方框各条之间的实心线段,使 其 变成 虚线段,就像 这样:
最后 我们 需要 使 两个按钮的宽度 随着主视图宽度的变化 按比例 改变。于是 按住 shift键 选取 两个按钮,再在Autosizing这个标签上方的区域中 我们 点击 里层方框内的水平线段,使 其 变成 实线段,而 垂直线段 使 其 保持 虚线段 不变,就像 这样:
接着 我们 编译 并且 运行 这个程序。我们 将 iOS模拟器 向左 或者 向右 旋转 90度后,可以看到 这样的结果:
虽然 在不少的情况之下 “自动改变尺寸”这项特性 都能 很好的 满足 你的要求,但是 这项特性 也有 一些限制。比如 在某些情况之下,你 可能需要 根据设备 是 横屏 或者 竖屏 从而 整体 改变 用户界面的布局。在这样的情况下 你 不但需要改变 视图元素的尺寸,还需要将 其 移动 到特定的位置。很明显 “自动改变尺寸”这个特性 就办不到 这点。有 一项解决办法 就是 在设备 旋转的时候 打断 视图的旋转,在视图 重新 绘制之前 改变 用户界面元素的尺寸 和 位置。在这里这个例子中 我们 需要编写 willAnimateRotationToInterfaceOrientation这项措施 并且 使 其 适用于 layoutViewController这类物件。willAnimateRotationToInterfaceOrientation这项措施 在用户界面 开始旋转之前 会得以实施。首先 我们 在源代码中 为layoutViewController类型的物件 添加 两个按钮。在layoutViewController.h这个文件中的@interface命令 和 @end命令之间 添加 下面两行语句:
接着 在layoutViewController.m这个文件中@implementation命令后面 添加 下面两行语句:
最后 在界面创建器中 将 用户界面中的两个按钮 分别 与 layoutViewController类型物件所包含的变量firstButton 和 secondButton 连接起来,具体的连接方法 在之前的文章中 提到过。这样 你 就可以在源代码中 控制 用户界面上的两个按钮了。
然后 我们 需要 在layoutViewController.m这个文件当中 添加 willAnimateRotationToInterfaceOrientation这项措施:
这项措施的第一个参数 toInterfaceOrientation 代表 当前设备转到的方向 或者 视图即将要转到的方向。if()语句 会判断 视图即将要转到的方向 是否是 横屏底部朝左 或者 横屏底部朝右。如果 视图即将要转到的方向 是 横屏底部朝左 或者 横屏底部朝右,则执行
如果 视图即将转到的方向 不是 横屏底部朝左 或者 横屏底部朝右,换句话说 就是 竖屏 或者 竖屏上下颠倒,那么 就执行:
在上面四行语句中 我们 调用了 CGRectMake()这个函数。调用 CGRectMake()这个函数时,需要向 其 提供 四个参数,它们 分别依次 是 横坐标、纵坐标、宽度、高度。CGRectMake()这个函数 执行结束后 会产生 一个你指定位置 和 尺寸的矩形。每个视图物件 都包含 一个变量frame,代表 这个视图物件的框架。我们 只要将 一个视图物件的框架frame 设定为 CGRectMake()函数生成的矩形,这个视图物件的位置 和 尺寸 就会变为 CGRectMake()这个函数所生成的矩形的位置 和 尺寸。
最后 别忘了 将 viewDidUnload这项措施 修改成 这样:
在编译、运行 这个程序之前,我们 要打开 layoutViewController.xib这个文件,将 视图 修改为 下面的样子:
编译 并且 运行 这个程序后,可以看到 这样的效果:
我们 将 iOS模拟器 旋转 到横屏模式 就可以看到 这样的效果: