在 iOS 9 中强制视图控制器方向
Force View Controller Orientation in iOS 9
我正在开发一款可以拍摄人像或风景照片的应用程序。由于项目需要,我不能让设备方向自动旋转,但是需要支持旋转。
使用以下定位方法时:
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
只有 returns 从那一点开始的 setValue。 (而且非常脏)
我遗漏了什么吗? 我研究过关闭并启动一个新的视图控制器,但是还有其他问题,例如常量 UI关闭并立即呈现新视图控制器时出现故障。
编辑:
以下方法对我不起作用:
- Trick preferredInterfaceOrientationForPresentation to fire on viewController change
- Forcing UIInterfaceOrientation changes on iPhone
编辑 2:
关于潜在解决方案的想法:
直接设置方向(使用setValue
)并处理这对iOS 9 带来的所有副作用(不可接受)
我可以使用当前的解决方案并提示用户需要旋转设备。物理旋转设备后,UI 会旋转并正确锁定到位。 (可怜UI)
我可以找到一个解决方案,强制刷新方向并在没有物理动作的情况下旋转。 (我正在询问和寻找的内容)
全部手工完成。我可以将界面锁定为纵向或横向,并手动旋转和调整容器视图的大小。这是 'dirty' 因为它放弃了所有大小 class 自动布局功能并导致代码更重。我正在努力避免这种情况。
我自己过去也遇到过这个问题。我能够使用简单的解决方法(和 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
}];
于是我查看了UIDevice的私有headers,好像有两个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)
借助这个答案,我找到了解决方案: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
变量来管理再次调用这些方法的哪些方面。
我正在开发一款可以拍摄人像或风景照片的应用程序。由于项目需要,我不能让设备方向自动旋转,但是需要支持旋转。
使用以下定位方法时:
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
只有 returns 从那一点开始的 setValue。 (而且非常脏)
我遗漏了什么吗? 我研究过关闭并启动一个新的视图控制器,但是还有其他问题,例如常量 UI关闭并立即呈现新视图控制器时出现故障。
编辑:
以下方法对我不起作用:
- Trick preferredInterfaceOrientationForPresentation to fire on viewController change
- Forcing UIInterfaceOrientation changes on iPhone
编辑 2:
关于潜在解决方案的想法:
直接设置方向(使用
setValue
)并处理这对iOS 9 带来的所有副作用(不可接受)我可以使用当前的解决方案并提示用户需要旋转设备。物理旋转设备后,UI 会旋转并正确锁定到位。 (可怜UI)
我可以找到一个解决方案,强制刷新方向并在没有物理动作的情况下旋转。 (我正在询问和寻找的内容)
全部手工完成。我可以将界面锁定为纵向或横向,并手动旋转和调整容器视图的大小。这是 'dirty' 因为它放弃了所有大小 class 自动布局功能并导致代码更重。我正在努力避免这种情况。
我自己过去也遇到过这个问题。我能够使用简单的解决方法(和 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
}];
于是我查看了UIDevice的私有headers,好像有两个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)
借助这个答案,我找到了解决方案: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
变量来管理再次调用这些方法的哪些方面。