在 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]Appearview[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 中强制视图控制器方向的主要内容,如果未能解决你的问题,请参考以下文章

iOS 6 中的模态视图控制器强制横向方向

在 Objective-C 中的 iOS 6 上强制横向方向

在一个模式视图控制器上强制 iOS 方向为横向

IOS 6强制设备方向为横向

关闭 ViewController 后,我的视图保持在 iOS7 中以前的视图控制器方向

在 ios cordova 插件中,对于视图控制器,如何确定是不是应该发生方向更改