我遇到了完全相同的问题,并很快发现有许多关于自动旋转的不良建议流传,特别是因为iOS 8处理它的方式与以前的版本不同。
首先,你不应该手动应用反向旋转或订阅UIDevice
方向更改。进行反向旋转仍会导致不美观的动画效果,并且设备方向并不总是与界面方向相同。理想情况下,你希望摄像头预览保持真正静止,而你的应用程序UI则与状态栏方向和尺寸匹配,就像本地相机应用程序一样。
在iOS 8中进行方向更改时,窗口本身会旋转而不是它包含的视图。你可以将多个视图控制器的视图添加到单个UIWindow
中,但只有rootViewController
将有机会通过shouldAutorotate()
做出响应。即使你在视图控制器级别上做出旋转决策,也是父窗口实际旋转,从而旋转其所有子视图(包括来自其他视图控制器的视图)。
解决方案是两个UIWindow
叠加在一起,每个窗口都有自己的根视图控制器进行旋转(或不旋转)。大多数应用程序只有一个窗口,但你无法像任何其他UIView
子类一样拥有两个窗口并重叠它们。
这里是一个工作的概念验证,我也将其放在了GitHub上。你的特殊情况稍微复杂一些,因为你有一堆包含视图控制器,但基本思路是相同的。我将在下面涉及一些具体要点。
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var cameraWindow: UIWindow!
var interfaceWindow: UIWindow!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let screenBounds = UIScreen.mainScreen().bounds
let inset: CGFloat = fabs(screenBounds.width - screenBounds.height)
cameraWindow = UIWindow(frame: screenBounds)
cameraWindow.rootViewController = CameraViewController()
cameraWindow.backgroundColor = UIColor.blackColor()
cameraWindow.hidden = false
interfaceWindow = UIWindow(frame: CGRectInset(screenBounds, -inset, -inset))
interfaceWindow.rootViewController = InterfaceViewController()
interfaceWindow.backgroundColor = UIColor.clearColor()
interfaceWindow.opaque = false
interfaceWindow.makeKeyAndVisible()
return true
}
}
在interfaceWindow
上设置负的内嵌值可以使其略微大于屏幕边界,从而有效地隐藏了您否则会看到的黑色矩形遮罩。通常您不会注意到,因为该遮罩会随窗口旋转,但由于相机窗口是固定的,所以在旋转期间角落处的遮罩变得可见。
class CameraViewController: UIViewController {
override func shouldAutorotate() -> Bool {
return false
}
}
这里正是你所期望的,只需添加自己的AVCapturePreviewLayer
设置即可。
class InterfaceViewController: UIViewController {
var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
contentView = UIView(frame: CGRectZero)
contentView.backgroundColor = UIColor.clearColor()
contentView.opaque = false
view.backgroundColor = UIColor.clearColor()
view.opaque = false
view.addSubview(contentView)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let screenBounds = UIScreen.mainScreen().bounds
let offset: CGFloat = fabs(screenBounds.width - screenBounds.height)
view.frame = CGRectOffset(view.bounds, offset, offset)
contentView.frame = view.bounds
}
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.All.rawValue)
}
override func shouldAutorotate() -> Bool {
return true
}
}
最后一个技巧是撤销我们对窗口应用的负插图效果,这可以通过将
view
偏移相同的量并将
contentView
视为主视图来实现。
对于您的应用程序,
interfaceWindow.rootViewController
将是您的选项卡栏控制器,其中又包含一个导航控制器等。所有这些视图在相机控制器出现时需要透明,以便相机窗口可以在其下显示。出于性能原因,您可以考虑保持它们不透明,并仅在相机实际使用时将所有内容设置为透明,并在相机未使用时设置相机窗口为
hidden
(同时关闭捕获会话)。
很抱歉发了一篇小说;我没有看到其他地方讨论过这个问题,我花了一段时间才弄清楚,希望可以帮助您和其他试图获得相同行为的人。即使是Apple的
AVCam
示例应用程序也没有处理得完全正确。
我发布的示例存储库还包括已经设置好相机的版本。祝你好运!
self.view.addSubview(viewController.view)
- switz