强制 iOS 视图不旋转,同时仍允许子视图旋转
Force iOS view to not rotate, while still allowing child to rotate
我有一个带有子视图控制器的视图控制器。
tab bar controller
|
|
nav controller
|
|
UIPageViewController (should rotate)
|
|
A (Video Player) (shouldn't rotate)
|
|
B (Controls overlay) (should rotate)
A 必须始终保持纵向,但 B 可以自由旋转。
我知道 shouldAutorotate
适用于任何视图控制器及其子级,但是有什么办法可以解决这个问题吗?似乎我可以使用 shouldAutorotateToInterfaceOrientation
,但这在 iOS 8.
中被阻止
我想让视频播放器保持静止(因此无论设备方向如何,水平视频始终是水平的),同时允许控件层子视图覆盖自由旋转。
我正在使用 Swift。
我不确定,但我认为你可以为你的子视图创建一个自己的 class 并覆盖 shouldAutorotate
方法等。这样它应该覆盖 shouldAutorotate
来自parent-viewcontroller.
你可以试试这个-
Objective -C 代码,如果您在 swift 中有替代代码:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if ()//Place your condition here like if A is visible
{
return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskAll;
}
您可以订阅旋转变化通知,在您要旋转的子视图上手动设置旋转变换矩阵。
简短回答:不,所有可见的控制器和视图一起旋转(或不旋转)。
长答案:
首先,你必须在根控制器中实现自动旋转决策功能;这可能意味着创建一个导航控制器子类。
您可以通过让父视图自动旋转来改变您想要的行为——但让它手动旋转返回以显示未旋转。
或者,您不能自动旋转,但会监听物理设备旋转的通知,然后手动旋转您想要的任何视图,例如:Replicate camera app rotation to landscape IOS 6 iPhone
另见,仅供参考:
How to force a UIViewController to Portrait orientation in iOS 6
shouldAutoRotate Method Not Called in iOS6
iOS6: supportedInterfaceOrientations not working (is invoked but the interface still rotates)
How to implement UIViewController rotation in response to orientation changes?
我遇到了这个确切的问题,并且很快发现有很多关于自转的错误建议,特别是因为 iOS 8 处理它的方式与以前的版本不同。
首先,您不想手动应用反向旋转或订阅UIDevice
方向更改。进行反向旋转仍会导致动画效果不佳,并且 device 方向并不总是与 interface 方向相同。理想情况下,您希望相机预览保持真正的静止状态,并且您的应用 UI 在状态栏方向和大小发生变化时匹配它们,就像本机相机应用一样。
在 iOS 8 中的方向更改期间,window 本身旋转而不是它包含的视图。您可以将多个视图控制器的视图添加到单个 UIWindow
,但只有 rootViewController
有机会通过 shouldAutorotate()
进行响应。即使您在视图控制器级别做出旋转决定,实际上旋转的是父 window,因此旋转了它的所有子视图(包括来自其他视图控制器的子视图)。
解决方案是两个 UIWindow
堆叠在一起,每个旋转(或不旋转)都有自己的根视图控制器。大多数应用只有一个,但您没有理由不能拥有两个并像任何其他 UIView
子类一样覆盖它们。
这是一个有效的概念验证,which I've also put on GitHub here。你的特殊情况有点复杂,因为你有一堆包含视图控制器,但基本思想是一样的。我将在下面谈谈一些具体的要点。
@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
上设置负插图使其略大于屏幕边界,有效地隐藏了您原本会看到的黑色矩形遮罩。通常你不会注意到,因为遮罩随着 window 旋转,但由于相机 window 是固定的,遮罩在旋转期间在角落变得可见。
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
}
}
最后一个技巧是撤消我们应用于 window 的负插图,我们通过偏移 view
相同的量并将 contentView
视为主视图来实现。
对于您的应用,interfaceWindow.rootViewController
将是您的标签栏控制器,它又包含一个导航控制器等。当您的相机控制器出现时,所有这些视图都需要透明,以便相机 window 可以在它下面显示出来。出于性能原因,您可能会考虑让它们不透明,并且仅在实际使用相机时将所有内容设置为透明,并在不使用时将相机 window 设置为 hidden
(同时关闭捕获会话) .
对不起 post 一部小说;我还没有在其他任何地方看到这个问题,我花了一段时间才弄清楚,希望它能帮助你和任何其他试图获得相同行为的人。即使是 Apple 的 AVCam
示例应用程序也无法正确处理它。
The example repo I posted 还包括已安装相机的版本。祝你好运!
这个问题最简单、最直接的答案是查看 Apple 的 AVCam 示例代码。对我来说关键的部分是:
- 使用
layerClass
为 AVCaptureVideoPreviewLayer
的视图。
- 设置 AVCaptureVideoPreviewLayer 连接的
videoOrientation
以匹配呈现视图时应用程序的 statusBarOrientation
,本质上是 viewWillAppear(_:)
.
- 设置
videoOrientation
以匹配 viewWillTransitionToSize(_:withTransitionCoordinator:)
中的 UIDevice.currentDevice().orientation
。
- 启用自动旋转并支持所有界面方向。
我实现了 jstn 描述的后台 window 方法并且它工作得相当好,但实际情况是它比必要的复杂得多。 AVCam 效果很好,而且方法相对简单。
我有一个带有子视图控制器的视图控制器。
tab bar controller
|
|
nav controller
|
|
UIPageViewController (should rotate)
|
|
A (Video Player) (shouldn't rotate)
|
|
B (Controls overlay) (should rotate)
A 必须始终保持纵向,但 B 可以自由旋转。
我知道 shouldAutorotate
适用于任何视图控制器及其子级,但是有什么办法可以解决这个问题吗?似乎我可以使用 shouldAutorotateToInterfaceOrientation
,但这在 iOS 8.
我想让视频播放器保持静止(因此无论设备方向如何,水平视频始终是水平的),同时允许控件层子视图覆盖自由旋转。
我正在使用 Swift。
我不确定,但我认为你可以为你的子视图创建一个自己的 class 并覆盖 shouldAutorotate
方法等。这样它应该覆盖 shouldAutorotate
来自parent-viewcontroller.
你可以试试这个-
Objective -C 代码,如果您在 swift 中有替代代码:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if ()//Place your condition here like if A is visible
{
return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskAll;
}
您可以订阅旋转变化通知,在您要旋转的子视图上手动设置旋转变换矩阵。
简短回答:不,所有可见的控制器和视图一起旋转(或不旋转)。
长答案:
首先,你必须在根控制器中实现自动旋转决策功能;这可能意味着创建一个导航控制器子类。
您可以通过让父视图自动旋转来改变您想要的行为——但让它手动旋转返回以显示未旋转。
或者,您不能自动旋转,但会监听物理设备旋转的通知,然后手动旋转您想要的任何视图,例如:Replicate camera app rotation to landscape IOS 6 iPhone
另见,仅供参考:
How to force a UIViewController to Portrait orientation in iOS 6
shouldAutoRotate Method Not Called in iOS6
iOS6: supportedInterfaceOrientations not working (is invoked but the interface still rotates)
How to implement UIViewController rotation in response to orientation changes?
我遇到了这个确切的问题,并且很快发现有很多关于自转的错误建议,特别是因为 iOS 8 处理它的方式与以前的版本不同。
首先,您不想手动应用反向旋转或订阅UIDevice
方向更改。进行反向旋转仍会导致动画效果不佳,并且 device 方向并不总是与 interface 方向相同。理想情况下,您希望相机预览保持真正的静止状态,并且您的应用 UI 在状态栏方向和大小发生变化时匹配它们,就像本机相机应用一样。
在 iOS 8 中的方向更改期间,window 本身旋转而不是它包含的视图。您可以将多个视图控制器的视图添加到单个 UIWindow
,但只有 rootViewController
有机会通过 shouldAutorotate()
进行响应。即使您在视图控制器级别做出旋转决定,实际上旋转的是父 window,因此旋转了它的所有子视图(包括来自其他视图控制器的子视图)。
解决方案是两个 UIWindow
堆叠在一起,每个旋转(或不旋转)都有自己的根视图控制器。大多数应用只有一个,但您没有理由不能拥有两个并像任何其他 UIView
子类一样覆盖它们。
这是一个有效的概念验证,which I've also put on GitHub here。你的特殊情况有点复杂,因为你有一堆包含视图控制器,但基本思想是一样的。我将在下面谈谈一些具体的要点。
@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
上设置负插图使其略大于屏幕边界,有效地隐藏了您原本会看到的黑色矩形遮罩。通常你不会注意到,因为遮罩随着 window 旋转,但由于相机 window 是固定的,遮罩在旋转期间在角落变得可见。
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
}
}
最后一个技巧是撤消我们应用于 window 的负插图,我们通过偏移 view
相同的量并将 contentView
视为主视图来实现。
对于您的应用,interfaceWindow.rootViewController
将是您的标签栏控制器,它又包含一个导航控制器等。当您的相机控制器出现时,所有这些视图都需要透明,以便相机 window 可以在它下面显示出来。出于性能原因,您可能会考虑让它们不透明,并且仅在实际使用相机时将所有内容设置为透明,并在不使用时将相机 window 设置为 hidden
(同时关闭捕获会话) .
对不起 post 一部小说;我还没有在其他任何地方看到这个问题,我花了一段时间才弄清楚,希望它能帮助你和任何其他试图获得相同行为的人。即使是 Apple 的 AVCam
示例应用程序也无法正确处理它。
The example repo I posted 还包括已安装相机的版本。祝你好运!
这个问题最简单、最直接的答案是查看 Apple 的 AVCam 示例代码。对我来说关键的部分是:
- 使用
layerClass
为AVCaptureVideoPreviewLayer
的视图。 - 设置 AVCaptureVideoPreviewLayer 连接的
videoOrientation
以匹配呈现视图时应用程序的statusBarOrientation
,本质上是viewWillAppear(_:)
. - 设置
videoOrientation
以匹配viewWillTransitionToSize(_:withTransitionCoordinator:)
中的UIDevice.currentDevice().orientation
。 - 启用自动旋转并支持所有界面方向。
我实现了 jstn 描述的后台 window 方法并且它工作得相当好,但实际情况是它比必要的复杂得多。 AVCam 效果很好,而且方法相对简单。