[英]How to force view controller orientation in iOS 8?
在iOS 8之前,我們將下面的代碼與supportedInterfaceOrientations和shouldAutoRotate委托方法結合使用,以強制應用程序方向指向任何特定方向。 我使用下面的代碼段以編程方式將應用程序旋轉到所需的方向。 首先,我正在改變狀態欄方向。 然后只顯示並立即關閉模態視圖會將視圖旋轉到所需的方向。
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];
但這在iOS 8中失敗了。而且,我已經看到堆棧溢出的一些答案,人們建議我們應該始終從iOS 8開始避免這種方法。
更具體地說,我的應用程序是一種通用類型的應用程序。 總共有三個控制器。
First View控制器 - 它應該支持iPad中的所有方向,並且只支持iPhone中的縱向(主頁按鈕)。
第二視圖控制器 - 它應該只支持所有條件下的橫向
第三視圖控制器 - 它應該只支持所有條件下的橫向視圖
我們使用導航控制器進行頁面導航。 從第一個視圖控制器,在按鈕單擊操作上,我們在堆棧上推送第二個。 因此,當第二個視圖控制器到達時,無論設備方向如何,應用程序都應僅鎖定橫向。
下面是我在第二個和第三個視圖控制器中的shouldAutorotate
和supportedInterfaceOrientations
方法。
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscapeRight;
}
-(BOOL)shouldAutorotate {
return NO;
}
對於這種或任何更好的方法來鎖定iOS 8的特定方向的視圖控制器是否有任何解決方案。請幫忙!!
對於iOS 7 - 10:
Objective-C的:
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];
斯威夫特3:
let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()
只需在顯示的視圖控制器的- viewDidAppear:
調用它即可。
如果你在UINavigationController
或UITabBarController
方向旋轉會更復雜一些。 問題是如果視圖控制器嵌入在這些控制器之一中,則導航或標簽欄控制器優先,並在自動旋轉和支持的方向上做出決定。
我在UINavigationController和UITabBarController上使用以下2個擴展,以便嵌入其中一個控制器的視圖控制器可以做出決定。
斯威夫特2.3
extension UINavigationController {
public override func supportedInterfaceOrientations() -> Int {
return visibleViewController.supportedInterfaceOrientations()
}
public override func shouldAutorotate() -> Bool {
return visibleViewController.shouldAutorotate()
}
}
extension UITabBarController {
public override func supportedInterfaceOrientations() -> Int {
if let selected = selectedViewController {
return selected.supportedInterfaceOrientations()
}
return super.supportedInterfaceOrientations()
}
public override func shouldAutorotate() -> Bool {
if let selected = selectedViewController {
return selected.shouldAutorotate()
}
return super.shouldAutorotate()
}
}
斯威夫特3
extension UINavigationController {
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
}
open override var shouldAutorotate: Bool {
return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
}
}
extension UITabBarController {
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if let selected = selectedViewController {
return selected.supportedInterfaceOrientations
}
return super.supportedInterfaceOrientations
}
open override var shouldAutorotate: Bool {
if let selected = selectedViewController {
return selected.shouldAutorotate
}
return super.shouldAutorotate
}
}
現在您可以覆蓋supportedInterfaceOrientations
方法,或者您可以在要鎖定的視圖控制器中覆蓋shouldAutoRotate
,否則您可以忽略其他視圖控制器中要繼承應用程序plist中指定的默認方向行為的覆蓋
class ViewController: UIViewController {
override func shouldAutorotate() -> Bool {
return false
}
}
class ViewController: UIViewController {
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}
}
從理論上講,這應該適用於所有復雜的視圖控制器層次結構,但我注意到UITabBarController存在問題。 出於某種原因,它想要使用默認方向值。 如果您有興趣了解如何解決某些問題,請參閱以下博文:
這對我有用:
在viewDidAppear:方法中調用它。
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[UIViewController attemptRotationToDeviceOrientation];
}
我發現如果它是一個呈現的視圖控制器,你可以覆蓋preferredInterfaceOrientationForPresentation
迅速:
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return UIInterfaceOrientation.LandscapeLeft
}
override func shouldAutorotate() -> Bool {
return false
}
這種方式適用於Swift 2 iOS 8.x:
PS(這種方法不需要在每個viewController上覆蓋定向函數,比如shouldautorotate,只需要AppDelegate上的一個方法)
所以,在AppDelegate.swift上創建一個變量:
var enableAllOrientation = false
所以,還把這個功能:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if (enableAllOrientation == true){
return UIInterfaceOrientationMask.All
}
return UIInterfaceOrientationMask.Portrait
}
因此,在項目的每個類中,您都可以在viewWillAppear中設置此var:
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.enableAllOrientation = true
}
如果您需要根據設備類型進行選擇,可以執行以下操作:
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
switch UIDevice.currentDevice().userInterfaceIdiom {
case .Phone:
// It's an iPhone
print(" - Only portrait mode to iPhone")
appDelegate.enableAllOrientation = false
case .Pad:
// It's an iPad
print(" - All orientation mode enabled on iPad")
appDelegate.enableAllOrientation = true
case .Unspecified:
// Uh, oh! What could it be?
appDelegate.enableAllOrientation = false
}
}
首先 - 這是一個壞主意,一般來說,你的應用程序架構出現問題,但是, sh..t happens
,你可以嘗試制作如下內容:
final class OrientationController {
static private (set) var allowedOrientation:UIInterfaceOrientationMask = [.all]
// MARK: - Public
class func lockOrientation(_ orientationIdiom: UIInterfaceOrientationMask) {
OrientationController.allowedOrientation = [orientationIdiom]
}
class func forceLockOrientation(_ orientation: UIInterfaceOrientation) {
var mask:UIInterfaceOrientationMask = []
switch orientation {
case .unknown:
mask = [.all]
case .portrait:
mask = [.portrait]
case .portraitUpsideDown:
mask = [.portraitUpsideDown]
case .landscapeLeft:
mask = [.landscapeLeft]
case .landscapeRight:
mask = [.landscapeRight]
}
OrientationController.lockOrientation(mask)
UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
}
}
比,在AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// do stuff
OrientationController.lockOrientation(.portrait)
return true
}
// MARK: - Orientation
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return OrientationController.allowedOrientation
}
每當你想改變方向時,請執行以下操作:
OrientationController.forceLockOrientation(.landscapeRight)
注意:有時,設備可能無法通過此類呼叫進行更新,因此您可能需要執行以下操作
OrientationController.forceLockOrientation(.portrait)
OrientationController.forceLockOrientation(.landscapeRight)
就這樣
這應該從iOS 6開始向上,但我只在iOS 8上測試它。子類UINavigationController
並覆蓋以下方法:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
- (BOOL)shouldAutorotate {
return NO;
}
或詢問可見的視圖控制器
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return self.visibleViewController.preferredInterfaceOrientationForPresentation;
}
- (BOOL)shouldAutorotate {
return self.visibleViewController.shouldAutorotate;
}
並在那里實施方法。
這是對Sid Shah的回答中的評論的反饋,關於如何使用以下方法禁用動畫:
[UIView setAnimationsEnabled:enabled];
碼:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:NO];
[UIView setAnimationsEnabled:NO];
// Stackoverflow #26357162 to force orientation
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:NO];
[UIView setAnimationsEnabled:YES];
}
如果您使用的是navigationViewController,則應為此創建自己的超類並覆蓋:
- (BOOL)shouldAutorotate {
id currentViewController = self.topViewController;
if ([currentViewController isKindOfClass:[SecondViewController class]])
return NO;
return YES;
}
這將禁用SecondViewController中的旋轉,但如果您在設備處於縱向方向時按下SecondViewController ,則SecondViewController將以縱向模式顯示。
假設您正在使用故事板。 您必須在“onClick”方法中創建手動segue( 如何 ):
- (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender {
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
[self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];
}
這將在您的超類禁用自動旋轉功能之前強制橫向方向。
在Xcode 8
,方法轉換為屬性,因此以下適用於Swift
:
override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait
}
override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return UIInterfaceOrientation.portrait
}
override public var shouldAutorotate: Bool {
return true
}
我嘗試了很多解決方案,但是工作的解決方案如下:
無需編輯ios 8和9中的info.plist
。
- (BOOL) shouldAutorotate {
return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);
}
UIInterfaceOrientationUnknown
無法確定設備的方向。
UIInterfaceOrientationPortrait
設備處於縱向模式,設備直立,主頁按鈕位於底部。
UIInterfaceOrientationPortraitUpsideDown
設備處於縱向模式但是上下顛倒,設備直立,主頁按鈕位於頂部。
UIInterfaceOrientationLandscapeLeft
設備處於橫向模式,設備直立,主頁按鈕位於左側。
UIInterfaceOrientationLandscapeRight
設備處於橫向模式,設備直立,主頁按鈕位於右側。
Sids和Koreys答案的組合對我有用。
擴展導航控制器:
extension UINavigationController {
public override func shouldAutorotate() -> Bool {
return visibleViewController.shouldAutorotate()
}
}
然后在單個View上禁用旋轉
class ViewController: UIViewController {
override func shouldAutorotate() -> Bool {
return false
}
}
並且在segue之前旋轉到適當的方向
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "SomeSegue")
{
let value = UIInterfaceOrientation.Portrait.rawValue;
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
}
上面的頂級解決方案:
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
當我在所呈現的視圖控制器的viewDidAppear中調用它時,我沒有工作。 但是,當我在呈現視圖控制器中的preparForSegue中調用它時,它確實有效。
(對不起,沒有足夠的聲譽點評論該解決方案,所以我不得不像這樣添加它)
我的要求是
AVPlayerViewController
播放視頻 播放視頻時,如果是風景,則允許屏幕向右旋轉並向左旋轉。 如果是肖像,則僅以縱向模式鎖定視圖。
首先,在AppDelegate.swift
定義supportedInterfaceOrientationsForWindow
var portrait = true
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if portrait {
return .Portrait
} else {
return .Landscape
}
}
其次,在主視圖控制器中,定義以下功能
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
print("\(#function)")
return .Portrait
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return .Portrait
}
override func shouldAutorotate() -> Bool {
return false
}
然后,您需要子類化AVPlayerViewController
class MyPlayerViewController: AVPlayerViewController {
var size: CGSize?
var supportedOrientationMask: UIInterfaceOrientationMask?
var preferredOrientation: UIInterfaceOrientation?
override func viewDidLoad() {
super.viewDidLoad()
if let size = size {
if size.width > size.height {
self.supportedOrientationMask =[.LandscapeLeft,.LandscapeRight]
self.preferredOrientation =.LandscapeRight
} else {
self.supportedOrientationMask =.Portrait
self.preferredOrientation =.Portrait
}
}
}
在MyPlayerViewController.swift
覆蓋這三個函數
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return self.supportedOrientationMask!
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return self.preferredOrientation!
}
因為用戶可能會向左或向右旋轉設備橫向,我們需要將自動旋轉設置為true
override func shouldAutorotate() -> Bool {
return true
}
最后,在視圖控制器中創建MyPlayerViewController
實例並設置屬性大小值。
let playerViewController = MyPlayerViewController()
// Get the thumbnail
let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......)
let size = thumbnail?.size
playerViewController.size = size
使用正確的videoUrl
啟動播放器,然后將播放器分配給playerViewController
。 快樂的編碼!
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[UIViewController attemptRotationToDeviceOrientation];
}
[iOS9 +]如果有人一直拖到這里,因為以上解決方案都沒有工作,如果你想要通過segue改變方向,你可能想檢查一下。
檢查您的segue的演示文稿類型。 我的是“當前的背景”。 當我將其更改為全屏時,它工作正常。
感謝@GabLeRoux,我找到了這個解決方案。
Swift 2.2:
extension UINavigationController {
public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return visibleViewController!.supportedInterfaceOrientations()
}
public override func shouldAutorotate() -> Bool {
return visibleViewController!.shouldAutorotate()
}
}
extension UITabBarController {
public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if let selected = selectedViewController {
return selected.supportedInterfaceOrientations()
}
return super.supportedInterfaceOrientations()
}
public override func shouldAutorotate() -> Bool {
if let selected = selectedViewController {
return selected.shouldAutorotate()
}
return super.shouldAutorotate()
}
}
禁用旋轉
override func shouldAutorotate() -> Bool {
return false
}
鎖定特定方向
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
我在這里嘗試了一些解決方案,重要的是要理解它是根視圖控制器,它將確定它是否會旋轉。
我創建了以下Objective-c項目github.com/GabLeRoux/RotationLockInTabbedViewChild ,其中包含一個TabbedViewController
的工作示例,其中一個子視圖允許旋轉,另一個子視圖以縱向鎖定。
它並不完美,但它的工作原理和相同的想法應該適用於其他類型的根視圖,例如NavigationViewController
。 :)
我有同樣的問題,浪費了很多時間。 所以現在我有了解決方案。 我的應用程序設置只是支持肖像。但是,我的應用程序中的某些屏幕只需要橫向。我通過在AppDelegate上有一個變量isShouldRotate來修復它。 而AppDelegate的功能:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
if isShouldRotate == true {
return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
最后當ViewControllerA需要橫向狀態時。 就這樣做: 在推送/顯示到ViewControllerA 之前 ,將isShouldRotate賦值為true 。 不要忘記在viewWillDisappear中彈出/關閉控制器將isShouldRotate指定為false時。
根據@ sid-sha顯示的解決方案,您必須將所有內容放在viewDidAppear:
方法中,否則您將無法獲取didRotateFromInterfaceOrientation:
fired,因此類似於:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
NSNumber *value = [NSNumber numberWithInt:interfaceOrientation];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}
}
在AppDelegate
:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if let topController = UIViewController.topMostViewController() {
if topController is XXViewController {
return [.Portrait, .LandscapeLeft]
}
}
return [.Portrait]
}
XXViewController
是您想要支持Landscape模式的ViewController。
那么Sunny Shah的解決方案 XXViewController
於任何iOS版本的XXViewController
:
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
這是查找最頂層ViewController的實用程序函數。
extension UIViewController {
/// Returns the current application's top most view controller.
public class func topMostViewController() -> UIViewController? {
let rootViewController = UIApplication.sharedApplication().windows.first?.rootViewController
return self.topMostViewControllerOfViewController(rootViewController)
}
/// Returns the top most view controller from given view controller's stack.
class func topMostViewControllerOfViewController(viewController: UIViewController?) -> UIViewController? {
// UITabBarController
if let tabBarController = viewController as? UITabBarController,
let selectedViewController = tabBarController.selectedViewController {
return self.topMostViewControllerOfViewController(selectedViewController)
}
// UINavigationController
if let navigationController = viewController as? UINavigationController,
let visibleViewController = navigationController.visibleViewController {
return self.topMostViewControllerOfViewController(visibleViewController)
}
// presented view controller
if let presentedViewController = viewController?.presentedViewController {
return self.topMostViewControllerOfViewController(presentedViewController)
}
// child view controller
for subview in viewController?.view?.subviews ?? [] {
if let childViewController = subview.nextResponder() as? UIViewController {
return self.topMostViewControllerOfViewController(childViewController)
}
}
return viewController
}
}
使用此方法鎖定視圖控制器方向,在IOS 9上測試:
//將方向鎖定到橫向右側
-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeRight;
}
-(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController {
return UIInterfaceOrientationMaskLandscapeRight;
}
關於如何最好地完成這項任務似乎還有一些爭論,所以我想我會分享我的(工作)方法。 在UIViewController
實現中添加以下代碼:
- (void) viewWillAppear:(BOOL)animated
{
[UIViewController attemptRotationToDeviceOrientation];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
-(BOOL)shouldAutorotate
{
return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft;
}
對於此示例,您還需要在項目設置中(或直接在info.plist
)將允許的設備方向設置為“Landscape Left”。 如果您想要除LandscapeLeft.
其他內容,只需更改要強制的特定方向即可LandscapeLeft.
對我來說,關鍵是viewWillAppear
的attemptRotationToDeviceOrientation
調用 - 如果沒有物理旋轉設備,視圖將無法正確旋轉。
對我來說,頂級VC需要實現方向覆蓋。 如果頂級VC未實現,則使用VC向下堆棧將不起作用。
VC-main
|
-> VC 2
|
-> VC 3
只聽VC-Main,主要是在我的測試中。
看起來即使你在這里也是如此多的答案,對我來說沒有人足夠。 我想強制定位然后返回到設備方向但是[UIViewController attemptRotationToDeviceOrientation];
只是沒有工作。 什么也復雜的整個事情是我根據一些答案添加了shouldAutorotate為假,並且無法在所有場景中正確旋轉回來獲得所需的效果。
所以這就是我做的:
在他的init構造函數中調用控制器之前:
_userOrientation = UIDevice.currentDevice.orientation;
[UIDevice.currentDevice setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
[self addNotificationCenterObserver:@selector(rotated:)
name:UIDeviceOrientationDidChangeNotification];
所以我保存最后的設備方向並注冊方向更改事件。 方向改變事件很簡單:
- (void)rotated:(NSNotification*)notification {
_userOrientation = UIDevice.currentDevice.orientation;
}
在視圖中,我只是強行回到我作為userOreintation的任何方向:
- (void)onViewDismissing {
super.onViewDismissing;
[UIDevice.currentDevice setValue:@(_userOrientation) forKey:@"orientation"];
[UIViewController attemptRotationToDeviceOrientation];
}
這也必須在那里:
- (BOOL)shouldAutorotate {
return true;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
並且導航控制器必須委托給shouldAutorotate和supportedInterfaceOrientations,但是大多數人已經相信了。
PS:對不起,我使用了一些擴展和基類,但名稱非常有意義,所以概念是可以理解的,會進行更多擴展,因為它現在不是太多了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.