转载:http://blogread.cn/it/article/7765?f=wb#original

代码示例:https://github.com/johnlui/Swift-On-iOS/tree/master/ControlOrientation/ControlOrientation

环境要求:Xcode 7 / Swift2.0

前两天遇到了一个 “使用指定的不同屏幕方向打开新页面” 的需求,需求很简单:APP 一直保持竖屏,要求新打开的页面能够指定为横屏或竖屏,并且不允许自动切换,新页面退出后要恢复竖屏。

准备工作

新建一个单页面项目,命名为 ControlOrientation。接下来取消 Landscape Right 的勾选,我们的 APP 将只支持 竖屏(Portrait)和 Landscape Left:

以横屏打开新页面

给 Main.storyboard 拖入一个 View Controller,新建一个继承自 UIViewController 的类,名为 SecondViewController,然后将两者绑定。之后给新建的这个 View Controller 赋予 StoryBoard ID 值 “secondVC”:

简单 Google 便可以得到代码方式指定横屏打来新页面的方式:

在新页面的 viewDidLoad 中执行:

UIDevice.currentDevice().setValue(UIInterfaceOrientation.LandscapeLeft.rawValue, forKey: "orientation")

在启动页中间添加一个居中的按钮,拖动绑定点击事件,加入载入新页面的代码:

@IBAction func openNewVC(sender: AnyObject) {
if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("secondVC") as? SecondViewController {
self.presentViewController(vc, animated: true, completion: nil)
}
}

运行!查看效果:

代码控制横屏成功!

还有一个收尾工作:给 SecondViewController 增加一个退出按钮,这一步就不再描述啦。然后再次运行项目,查看效果:

出来之后也变横屏了哎,这不是我们想要的。

屏幕方向锁定

重读文章开头的需求,我们就会发现需求要求我们在任何一个界面都不能因为手机物理方向的变化而自动改变屏幕方向,稍微 Google 一下,我们就会得到锁死屏幕方向的代码:

给 ViewController 和 SecondViewController 都增加一个函数:

override func shouldAutorotate() -> Bool {
return false
}

为了直观的给大家展示,我们将通过手动改变模拟器的方向来模拟真机屏幕方向的变化(改变模拟器屏幕方向的快捷键是 Command 加左或者右),检验效果:

简直完美呀!是不是觉得有点太简单了?这么容易就实现了?当然不是,这么容易我还写个毛的文章呀。

神级 BUG

既然新打开横屏已经完美解决了,那么新打开竖屏怎么样呢?让我们把那一行切换屏幕方向的代码注释掉,运行查看结果:

BUG 了!!

解决 BUG

这个 bug 我搞了两天,虽然不是一直在搞它,但是总时长也至少有八个小时。其实严格意义上来讲,这并不是 bug,这只是我们不知道怎么才能搞定而已。最后我终于搞明白了这个 BUG 背后的运行原理,先说解决方案:

竖屏解决方案

在第一个页面 ViewController 中增加以下两个函数:

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.Portrait
}

在第二个页面 SecondViewController 中增加两个函数:

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.Portrait
}

查看效果:

竖屏搞定!

横屏解决方案

将 SecondViewController 中的函数改为:

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.All
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.LandscapeLeft
}

别忘了打开 viewDidLoad 函数中的横屏控制代码哦。查看效果:

横屏搞定!

全搞定了吗?并没有

我们还是 Too Young Too Simple,在我们简陋的例子中,似乎确实已经全搞定了,但是实际工程中大多数项目都不是普通 View Controller 作为根控制器哦~

我们测试一下 QQ、微信、微博 等 APP 采用的通用方案:根控制器为 TabBarController,之后嵌套 NavigationController,然后放入 ViewController 页面进行展示,然后 present 出 SecondViewController。

搭 VIE TNV(TabbarController->NavigationController->ViewController) 架构

我们可不是跟风在国外上市的中国互联网公司拆 VIE 架构哦~

  • 选中 ViewController,点击菜单中的 Editor -> Embed In -> Navigation Controller,第一层嵌套完成。

  • 选中 NavigationController,点击菜单中的 Editor -> Embed In -> Tab Bar Controller,第二层嵌套完成。

如图:

查看效果:

这他妈什么玩意儿 o(╯□╰)o

终极解决方案

我埋坑的过程就不细说了,下面直接给出特性分析及解决方案:

特性分析

跟 shouldAutorotate() 不同,判断是否应该改变 APP 屏幕方向并不会检测当前显示的 View Controller 的属性,而是去检测根 View Controller 的属性,所以我们要从 TabBarController 一路获取到当前 View Controller。

解决方案

新建一个继承于 UITabBarController 的类,名为 TabBarController,将其和 StoryBoard 中的 Tab Bar Controller 页面绑定。该类完整代码如下:

import UIKit

class TabBarController: UITabBarController, UITabBarControllerDelegate {

    override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
} override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
} override func shouldAutorotate() -> Bool {
return false
}
func tabBarControllerSupportedInterfaceOrientations(tabBarController: UITabBarController) -> UIInterfaceOrientationMask {
return self.selectedViewController!.supportedInterfaceOrientations()
}
func tabBarControllerPreferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return self.selectedViewController!.preferredInterfaceOrientationForPresentation()
}
}

同样,新建一个继承于 UINavigationController 的类,名为 NavigationController,将其和 StoryBoard 中的 Navigation Controller 页面绑定。向该类添加以下代码:

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return self.visibleViewController!.supportedInterfaceOrientations()
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return self.visibleViewController!.preferredInterfaceOrientationForPresentation()
}

检查结果

写在最后

通过本文我们可以总结出以下两点:

  • 是否自动转换屏幕方向由当前显示的 View Controller 决定。

  • 是否支持横屏和是否优先选择横屏由 rootViewController 决定,若有多层结构嵌套,则需要层层专递,将控制权交给当前显示的页面。类似于 代理传值 和 延迟静态绑定。

另外,我利用 Swift 的 enum 给 SecondViewController 类加上了一个优雅的 横屏/竖屏 控制开关,具体代码可以在 Github 上查看。

iOS如何用代码控制以不同屏幕方向打开新页面?的更多相关文章

  1. HBuilder mui 手机app开发 Android手机app开发 ios手机app开发 打开新页面 预加载页面 关闭页面

    创建子页面 在mobile app开发过程中,经常遇到卡头卡尾的页面,此时若使用局部滚动,在android手机上会出现滚动不流畅的问题: mui的解决思路是:将需要滚动的区域通过单独的webview实 ...

  2. 【iOS 开发】iOS 开发 简介 (IOS项目文件 | MVC 模式 | 事件响应机制 | Storyboard 控制界面 | 代码控制界面 | Retina 屏幕图片适配)

    一. iOS 项目简介 1. iOS 文件简介 创建一个 HelloWorld 项目, 在这个 IOS 项目中有四个目录 : 如下图; -- HelloWorldTests 目录 : 单元测试相关的类 ...

  3. 控制使用jquery load()方法载入新页面中的元素

    最近在项目中用到jquery的load()方法来加载页面,首先简单说一下load()方法. load(url,data,callback);该方法接收三个参数,第一个是载入的页面地址,第二个是要传到服 ...

  4. java:如何用代码控制H2 Database启动

    1.纯手动start/stop package com.cnblogs.yjmyzz.h2; import java.sql.Connection; import java.sql.DriverMan ...

  5. iOS 屏幕方向

    参考文章:http://www.tuicool.com/articles/e2q6zi 一般的应用,只会支持竖屏正方向一个方向,支持多个屏幕方向的应用还是比较少的. 当时也没搞明白,所以直接就设置了正 ...

  6. 游戏开发之在UE4中编写C++代码控制角色

    当你运行我们上次做完的项目,你可能会意识到我们移动的摄像机还是默认的那个摄像机,这个默认的摄像机可以自由飞翔.这一节,我们要使得开始的角色是我们的一个Avatar类的实例对象,并且使用键盘控制我们的角 ...

  7. iOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry)

    iOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry) 随着iPhone6/6+设备的上市,如何让手头上的APP适配多种机型多种屏幕尺寸变得尤为迫 ...

  8. 谈谈iOS中的屏幕方向

    众所周知,iOS中提供了[UIDevice currentDevice].orientation与[UIApplication sharedApplication].statusBarOrientat ...

  9. 可控制导航下拉方向的jQuery下拉菜单代码

    效果:http://hovertree.com/texiao/nav/1/ 代码如下: <!DOCTYPE html> <html> <head> <meta ...

随机推荐

  1. thinkPHP入门之二

    thinkphp提供了很多便利的方法,因为php要记的东西太多了,而thinkphp极大简化了这些,让编写过程更加快速. 为了预防那些了解它的人胡乱玩耍,thinkphp提供一个函数_empty 它是 ...

  2. div非弹出框半透明遮罩实现全屏幕遮盖css实现

    IE浏览器下设置元素css背景为透明: background-color: rgb(0, 0, 0); filter: alpha(opacity=20); 非IE浏览器下设置元素css背景为透明: ...

  3. SQL Server 2014新特性探秘(1)-内存数据库

    简介    SQL Server 2014提供了众多激动人心的新功能,但其中我想最让人期待的特性之一就要算内存数据库了.去年我再西雅图参加SQL PASS Summit 2012的开幕式时,微软就宣布 ...

  4. win8下IE10的鼠标mouse事件响应错误BUG

    具体症状就是有时候鼠标左键响应,有时候右键才能响应 问题的原因就是事件对象的detail没有复位 https://github.com/clientside/amplesdk/issues/187

  5. Windows下使用Dev-C++开发基于pthread.h的多线程程序

    一.下载Windows版本的pthread 目前最新版本是:pthreads-w32-2-9-1-release.zip. 二.解压pthread到指定目录      我选择的目录是:E:\DEV-C ...

  6. Android随笔之——静默安装、卸载

    随笔之所以叫随笔,就是太随意了,说起来,之前的闹钟系列随笔还没写完,争取在十月结束之前找时间把它给写了吧.今天要讲的Android APK的静默安装.卸载.网上关于静默卸载的教程有很多,更有说要调用隐 ...

  7. EntityFramework 7 Join Count LongCount 奇怪问题

    先吐槽一下,EF7 目前来说,真对的起现在的版本命名:"EntityFramework": "7.0.0-beta1". 这篇博文纪录一下:当 Linq 查询中 ...

  8. 设计数据库字段或者java中使用boolean型时需谨慎

    boolean型变量只有两个值 false和true,我们在设计数据库字段时或者定义java变量时会使用boolean,通常情况下开关类的变量使用无可非议,但请一定要考虑到扩展性. 使用前请仔细考虑一 ...

  9. 多种方法实现Loading(加载)动画效果

    当我们ajax提交一个按钮的时候,给那个按钮来个Loading效果会高端很多,体验也会上升个层次. 既能让用户知道正在提交中,也能防止二次提交,好处多多呢.

  10. TCP三次握手和四次挥手状态变迁解析

    TCP是TCP/IP的传输层控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接: 首先需要了解几个名词:tcp标志位,有6种分别为:SYN(synchronous建立联机) .ACK(ackn ...