在 iOS 9 中强制视图控制器方向
Posted
技术标签:
【中文标题】在 iOS 9 中强制视图控制器方向【英文标题】:Force View Controller Orientation in iOS 9 【发布时间】:2016-06-17 16:14:39 【问题描述】:我正在开发一个摄影应用程序,该应用程序允许以纵向或横向拍摄照片。由于项目的需要,我不能让设备方向自动旋转,但需要支持旋转。
当使用以下定位方法时:
override func shouldAutorotate() -> Bool
return true
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask
if self.orientation == .Landscape
return UIInterfaceOrientationMask.LandscapeRight
else
return UIInterfaceOrientationMask.Portrait
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation
if self.orientation == .Landscape
return UIInterfaceOrientation.LandscapeRight
else
return UIInterfaceOrientation.Portrait
我能够在启动时正确设置旋转。通过更改方向值并调用UIViewController.attemptRotationToDeviceOrientation()
,我能够支持旋转到所需的新界面。 但是,这种旋转仅在用户实际移动设备时发生。我需要它自动发生。
我可以调用:UIDevice.currentDevice().setValue(targetOrientation.rawValue, forKey: "orientation")
来强制更改,但这会导致其他副作用,因为UIDevice.currentDevice().orientation
从那时起只返回 setValue。 (而且非常脏)
我有什么遗漏吗?我已经研究过关闭并启动一个新的视图控制器,但这还有其他问题,例如在关闭并立即呈现新视图时出现持续的 UI 故障控制器。
编辑:
以下方法对我不起作用:
Trick preferredInterfaceOrientationForPresentation to fire on viewController change Forcing UIInterfaceOrientation changes on iPhone编辑 2:
对潜在解决方案的思考:
直接设置方向(使用setValue
)并处理这在 ios 9 上出现的所有副作用(不可接受)
我可以使用当前解决方案并指示用户需要旋转设备。设备物理旋转后,UI 会旋转,然后正确锁定到位。 (糟糕的用户界面)
我可以找到一种解决方案,它可以强制刷新方向并在没有物理动作的情况下旋转。 (我正在询问和寻找的内容)
全部手动完成。我可以将界面锁定为纵向或横向,并手动旋转和调整容器视图的大小。这是“肮脏的”,因为它放弃了所有尺寸类自动布局功能并导致代码更重。我正在努力避免这种情况。
【问题讨论】:
这是一个基于 Objective-C 的答案:***.com/questions/2689598/…,但我相信 Swift 的 API 是一样的;它对你有用吗? 应该有,但是这些方法在 iOS 9 中已弃用。曾经存在很多解决方案,我很难找到仍然有效的解决方案。我们的需求是非常明确的,并且是有道理的,但它们与当前的设计模式略有不同(有道理)。在这种情况下,让用户选择方向是不可行的。 哈,现在才看到弃用,真可惜。您的 VC 是否包含在导航控制器或选项卡栏中? Korey Hinton 在这里的答案:***.com/questions/26357162/… 试图解决这个具体案例,但我还没有尝试过。 不,它是完全独立的。我考虑过手动旋转整个界面,但如果可能的话,我想避免这种情况。这是一个比我想要的更肮脏的解决方案。 我认为不清楚:您所说的“非常肮脏”是什么意思。您是否参考旋转后的边界尺寸?请用一些屏幕截图解释您的问题,以便任何人都可以帮助您。陷入“过于宽泛”的问题或“有多种解决方案”的问题的风险非常高。 【参考方案1】:我能够在这个答案的帮助下找到解决方案:Programmatic interface orientation change not working for iOS
我的基本定位逻辑如下:
// Local variable to tracking allowed orientation. I have specific landscape and
// portrait targets and did not want to remember which I was supporting
enum MyOrientations
case Landscape
case Portrait
var orientation: MyOrientations = .Landscape
// MARK: - Orientation Methods
override func shouldAutorotate() -> Bool
return true
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask
if self.orientation == .Landscape
return UIInterfaceOrientationMask.LandscapeRight
else
return UIInterfaceOrientationMask.Portrait
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation
if self.orientation == .Landscape
return UIInterfaceOrientation.LandscapeRight
else
return UIInterfaceOrientation.Portrait
// Called on region and delegate setters
func refreshOrientation()
if let newOrientation = self.delegate?.getOrientation()
self.orientation = newOrientation
然后当我想刷新方向时,我执行以下操作:
// Correct Orientation
let oldOrientation = self.orientation
self.refreshOrientation()
if self.orientation != oldOrientation
dispatch_async(dispatch_get_main_queue(),
self.orientationRefreshing = true
let vc = UIViewController()
UIViewController.attemptRotationToDeviceOrientation()
self.presentViewController(vc, animated: false, completion: nil)
UIView.animateWithDuration(0.3, animations:
vc.dismissViewControllerAnimated(false, completion: nil)
)
)
此解决方案的副作用是导致view[Will/Did]Appear
和view[Will/Did]Disappear
一次全部触发。我正在使用本地 orientationRefreshing
变量来管理这些方法的哪些方面被再次调用。
【讨论】:
【参考方案2】:我自己过去也遇到过这个确切的问题。我能够使用一个简单的解决方法(和GPUImage)来解决它。我的代码是用 Objective-C 编写的,但我相信你可以毫无问题地将它翻译成 Swift。
我首先将项目支持的旋转设置为我希望支持的所有旋转,然后覆盖相同的 UIViewController 方法:
-(BOOL)shouldAutorotate
return TRUE;
-(NSUInteger)supportedInterfaceOrientations
return UIInterfaceOrientationMaskPortrait;
这允许设备旋转,但会在纵向模式下保持不变。然后开始观察设备旋转:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(adjustForRotation:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
如果设备处于纵向模式或横向模式,则更新 UI:
-(void)adjustForRotation:(NSNotification*)notification
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
switch (orientation)
case UIDeviceOrientationLandscapeLeft:
// UPDATE UI
break;
case UIDeviceOrientationLandscapeRight:
// UPDATE UI
break;
default: // All other orientations - Portrait, Upside Down, Unknown
// UPDATE UI
break;
最后,GPUImage 根据设备的方向渲染图像。
[_gpuImageStillCamera capturePhotoAsImageProcessedUpToFilter:last_f
withCompletionHandler:^(UIImage *processedImage, NSError *error)
// Process the processedImage
];
【讨论】:
根据您的经验,您是如何处理 UI 更新的?您是否手动缩放和旋转容器 UI? 我会在 UI 元素 @NathanTornquist 上应用 CGAffineTransform,所以旋转 另外,我强烈推荐使用 GPUImage。这是迄今为止对开源 iOS 生态系统的最大贡献之一!如果您对使用这种方法很认真,我很乐意更详细地概述我的解决方案。 我之前没有在整个视图上使用 CGAffineTransformations。如果我理解正确,我将不得不旋转并调整视图大小,然后推入目标大小类,以便我仍然可以在界面中使用自动布局约束?你有这方面的经验/知道它是否有效吗? 我应该更清楚。我在视图的子视图上使用了 CGAffineTransformations。也有 UIView.frame 调整的可能性。这样想,UIViewController 仍会认为它处于纵向模式,但您自己修改代码中的子视图内容以实现横向。它对我来说很顺利。【参考方案3】:所以我查看了UIDevice 的私有标头,似乎有两个setter 和两个属性定义,用于定位,目前让我感到困惑。这是我看到的……
@property (nonatomic) int orientation;
@property (nonatomic, readonly) int orientation;
- (void)setOrientation:(int)arg1;
- (void)setOrientation:(int)arg1 animated:(BOOL)arg2;
所以当我看到你使用setValue:forKey:
时,我想看看有一个合成的 setter 和 getter,老实说,我不能 100% 确定哪个正在设置,哪个正在被设备确认...我尝试在演示应用程序中使用 setValue:forKey:
无济于事,但使用了我过去的一个应用程序中的这个技巧,它马上就成功了 :) 我希望这会有所帮助
UIDevice.currentDevice().performSelector(Selector("setOrientation:"), withObject: UIInterfaceOrientation.Portrait.rawValue)
【讨论】:
虽然可行,但它是一个私有 API,也是让我的应用被拒绝的一种非常可靠的方法。 大约三年前,在 TMGGO 强制视频播放器进行自我定位时,我实际上已经对这个审查过程感到不满。从技术上讲,setValue:forKey:
,实际上是在后台调用setOrientation:
,所以我很惊讶它会被拒绝,因为你在技术上做同样的事情,奇怪的是结果不同:/以上是关于在 iOS 9 中强制视图控制器方向的主要内容,如果未能解决你的问题,请参考以下文章
在 Objective-C 中的 iOS 6 上强制横向方向