Как заблокировать ориентацию одного контроллера вида в портретном режиме только в Swift

Так как мое приложение получило поддержку для всей ориентации. Я хотел бы заблокировать только портретный режим для конкретного UIViewController.

(например, предположим, что это приложение с вкладками и когда вид входа в систему отображается модально, я хочу, чтобы этот режим SignIn View только в портретном режиме как пользователь поворачивает устройство или как текущее устройство ориентация будет)

Ответ 1

Когда у вас сложная иерархия представлений, например, наличие нескольких контроллеров навигации и/или контроллеров представления с вкладками, все может стать довольно запутанным.

Эта реализация позволяет отдельным контроллерам представления устанавливать, когда они хотели бы заблокировать ориентации, вместо того, чтобы полагаться на делегат приложения, чтобы найти их, просматривая подпредставления.

Свифт 3 и 4

В AppDelegate:

/// set orientations you want to be allowed in this property by default
var orientationLock = UIInterfaceOrientationMask.all

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        return self.orientationLock
}

В некотором другом глобальном классе struct или helper, здесь я создал AppUtility:

struct AppUtility {

    static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {

        if let delegate = UIApplication.shared.delegate as? AppDelegate {
            delegate.orientationLock = orientation
        }
    }

    /// OPTIONAL Added method to adjust lock and rotate to the desired orientation
    static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {

        self.lockOrientation(orientation)

        UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
        UINavigationController.attemptRotationToDeviceOrientation()
    }

}

Затем в желаемом ViewController вы хотите заблокировать ориентации:

 override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    AppUtility.lockOrientation(.portrait)
    // Or to rotate and lock
    // AppUtility.lockOrientation(.portrait, andRotateTo: .portrait)

}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    // Don't forget to reset when view is being removed
    AppUtility.lockOrientation(.all)
}

Если iPad или универсальное приложение

Убедитесь, что "Требуется полноэкранный режим" отмечен в Target Settings → General → Deployment Info. supportedInterfaceOrientationsFor делегат не будет вызван, если это не проверяется. enter image description here

Ответ 2

Swift 4

enter image description here AppDelegate

var orientationLock = UIInterfaceOrientationMask.all

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return self.orientationLock
}
struct AppUtility {
    static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
        if let delegate = UIApplication.shared.delegate as? AppDelegate {
            delegate.orientationLock = orientation
        }
    }

    static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
        self.lockOrientation(orientation)
        UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
    }
}

Ваш ViewController Добавьте следующую строку, если вам нужна только портретная ориентация. вам необходимо применить это ко всем ViewController, чтобы отобразить портретный режим.

override func viewWillAppear(_ animated: Bool) {
AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
    }

и это сделает ориентацию экрана для других Viewcontroller в соответствии с физической ориентацией устройства.

override func viewWillDisappear(_ animated: Bool) {
        AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.all)

    }

Ответ 3

Добавьте этот код, чтобы заставить портрет и заблокировать его:

override func viewDidLoad() {
    super.viewDidLoad()

    // Force the device in portrait mode when the view controller gets loaded
    UIDevice.currentDevice().setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation") 
}

override func shouldAutorotate() -> Bool {
    // Lock autorotate
    return false
}

override func supportedInterfaceOrientations() -> Int {

    // Only allow Portrait
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {

    // Only allow Portrait
    return UIInterfaceOrientation.Portrait
}

В AppDelegate - установите поддержкуInterfaceOrientationsForWindow на любые ориентации, которые вы хотите, чтобы все приложение поддерживало:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.All
} 

Ответ 4

Это общее решение для вашей проблемы и других.

1. Создайте auxiliar class UIHelper и примените следующие методы:

    /**This method returns top view controller in application  */
    class func topViewController() -> UIViewController?
    {
        let helper = UIHelper()
        return helper.topViewControllerWithRootViewController(rootViewController: UIApplication.shared.keyWindow?.rootViewController)
    }

    /**This is a recursive method to select the top View Controller in a app, either with TabBarController or not */
    private func topViewControllerWithRootViewController(rootViewController:UIViewController?) -> UIViewController?
    {
        if(rootViewController != nil)
        {
            // UITabBarController
            if let tabBarController = rootViewController as? UITabBarController,
                let selectedViewController = tabBarController.selectedViewController {
                return self.topViewControllerWithRootViewController(rootViewController: selectedViewController)
            }

            // UINavigationController
            if let navigationController = rootViewController as? UINavigationController ,let visibleViewController = navigationController.visibleViewController {
                return self.topViewControllerWithRootViewController(rootViewController: visibleViewController)
            }

            if ((rootViewController!.presentedViewController) != nil) {
                let presentedViewController = rootViewController!.presentedViewController;
                return self.topViewControllerWithRootViewController(rootViewController: presentedViewController!);
            }else
            {
                return rootViewController
            }
        }

        return nil
    }

2. Создайте протокол с вашим желанием, для вашего конкретного случая будет портрет.

ориентация протоколаIsOnlyPortrait {}

Примечание. Если вы хотите, добавьте его в верхнюю часть класса UIHelper.

3. Расширьте свой контроллер просмотра

В вашем случае:

class Any_ViewController: UIViewController,orientationIsOnlyPortrait {

   ....

}

4. В классе делегата приложения добавьте этот метод:

 func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        let presentedViewController = UIHelper.topViewController()
        if presentedViewController is orientationIsOnlyPortrait {
            return .portrait
        }
        return .all
    }

Заключительные заметки:

  • Если вы используете этот класс в портретном режиме, просто протокол.
  • Если вы хотите, чтобы поведение других пользователей с контроллеров представлений, создайте другие протоколы и следуйте одной и той же структуре.
  • В этом примере решается проблема с изменениями ориентации после нажатия контроллеры просмотра

Ответ 5

Куча замечательных ответов в этой теме, но ни одна из них не соответствовала моим потребностям. У меня есть приложение с вкладками с навигационными контроллерами на каждой вкладке, а одно изображение необходимо повернуть, а остальные нужно заблокировать в портрете. По какой-то причине контроллер навигации не изменял размер, поскольку он должным образом рассматривается. Нашел решение (в Swift 3), объединив этот ответ, и проблемы с макетом исчезли. Создайте структуру как предложение @bmjohns:

import UIKit

struct OrientationLock {

    static func lock(to orientation: UIInterfaceOrientationMask) {
        if let delegate = UIApplication.shared.delegate as? AppDelegate {
            delegate.orientationLock = orientation
        }
    }

    static func lock(to orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation) {
        self.lock(to: orientation)
        UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
    }
} 

Затем подкласс UITabBarController:

    import UIKit

class TabBarController: UITabBarController, UITabBarControllerDelegate {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.delegate = self
    }

    func tabBarControllerSupportedInterfaceOrientations(_ tabBarController: UITabBarController) -> UIInterfaceOrientationMask {
        if tabBarController.selectedViewController is MyViewControllerNotInANavigationControllerThatShouldRotate {
            return .allButUpsideDown
        } else if let navController = tabBarController.selectedViewController as? UINavigationController, navController.topViewController is MyViewControllerInANavControllerThatShouldRotate {
            return .allButUpsideDown
        } else {
            //Lock view that should not be able to rotate
            return .portrait
        }
    }

    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
        if viewController is MyViewControllerNotInANavigationControllerThatShouldRotate {
            OrientationLock.lock(to: .allButUpsideDown)
        } else if let navController = viewController as? UINavigationController, navController.topViewController is MyViewControllerInANavigationControllerThatShouldRotate {
            OrientationLock.lock(to: .allButUpsideDown)
        } else {
            //Lock orientation and rotate to desired orientation
            OrientationLock.lock(to: .portrait, andRotateTo: .portrait)
        }
        return true
    }
}

Не забудьте изменить класс TabBarController в раскадровке во вновь созданный подкласс.

Ответ 6

Свифт 3 и 4

Установите supportedInterfaceOrientations свойство конкретных UIViewControllers, как это:

class MyViewController: UIViewController {

    var orientations = UIInterfaceOrientationMask.portrait //or what orientation you want
    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
    get { return self.orientations }
    set { self.orientations = newValue }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //...
}

ОБНОВИТЬ

Это решение работает только тогда, когда ваш viewController не встроен в UINavigationController, поскольку ориентация наследуется от родительского viewController.
Для этого случая вы можете создать подкласс UINavigationViewController и установить для него эти свойства.

Ответ 7

Чтобы настроить ориентацию ландшафта для всех видов приложения и разрешить только один вид для всех ориентаций (например, чтобы добавить крен камеры):

В AppDelegate.swift:

var adaptOrientation = false

В: didFinishLaunchingWithOptions

NSNotificationCenter.defaultCenter().addObserver(self, selector: "adaptOrientationAction:", name:"adaptOrientationAction", object: nil)

В другом месте AppDelegate.swift:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
    return checkOrientation(self.window?.rootViewController)
}

func checkOrientation(viewController:UIViewController?)-> Int{
    if (adaptOrientation == false){
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }else {
        return Int(UIInterfaceOrientationMask.All.rawValue)
    }
}

func adaptOrientationAction(notification: NSNotification){
    if adaptOrientation == false {
        adaptOrientation = true
    }else {
        adaptOrientation = false
    }
}

Тогда в представлении, которое перейдет к тому, которое вы хотите иметь, чтобы иметь все ориентации:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if (segue.identifier == "YOURSEGUE") {
        NSNotificationCenter.defaultCenter().postNotificationName("adaptOrientationAction", object: nil)
    }
}

override func viewWillAppear(animated: Bool) {
    if adaptOrientation == true {
        NSNotificationCenter.defaultCenter().postNotificationName("adaptOrientationAction", object: nil)
    }
}

Последнее, что нужно отметить. Ориентация устройства: - Портрет - Пейзаж слева - Пейзаж справа

Ответ 8

bmjohns → Ты мой спаситель жизни. Это единственное работающее решение (с конструкцией AppUtility)

Я создал этот класс:

class Helper{
    struct AppUtility {

        static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {

            if let delegate = UIApplication.shared.delegate as? AppDelegate {
                delegate.orientationLock = orientation
            }
        }

        /// OPTIONAL Added method to adjust lock and rotate to the desired orientation
        static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {

            self.lockOrientation(orientation)

            UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
        }

    }
}

и выполнил ваши инструкции, и все отлично работает для Swift 3 → xcode version 8.2.1

Ответ 9

Как и в IOS 10 и 11, iPad поддерживает функцию "Слайд-режим" и "Сплит-просмотр". Чтобы включить приложение в режиме слайд-просмотра и разделения, Requires full screen необходимо отключить. Это означает, что принятый ответ не может быть использован, если приложение хочет поддерживать слайд-просмотр и разделение. См. Больше от Apple Принятие улучшений многозадачности на iPad здесь.

У меня есть решение, которое позволяет (1) снять отметку Requires full screen, (2) только одну функцию, которая будет реализована в appDelegate (особенно если вы не хотите/не можете изменять контроллеры целевого представления) и (3) избежать рекурсивных вызовов. Нет необходимости в вспомогательном классе и расширениях.

appDelegate.swift (Swift 4)

func application(_ application: UIApplication,
                 supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    // Search for the visible view controller
    var vc = window?.rootViewController
    // Dig through tab bar and navigation, regardless their order 
    while (vc is UITabBarController) || (vc is UINavigationController) {
        if let c = vc as? UINavigationController {
            vc = c.topViewController
        } else if let c = vc as? UITabBarController {
            vc = c.selectedViewController
        }
    }
    // Look for model view controller
    while (vc?.presentedViewController) != nil {
        vc = vc!.presentedViewController
    }
    print("vc = " + (vc != nil ? String(describing: type(of: vc!)) : "nil"))
    // Final check if it our target class.  Also make sure it isn't exiting.
    // Otherwise, system will mistakenly rotate the presentingViewController.
    if (vc is TargetViewController) && !(vc!.isBeingDismissed) {
        return [.portrait]
    }
    return [.all]
}

Edit

@bmjohns отметил, что эта функция не вызывается на iPad. Я проверял и да, он не был вызван. Итак, я сделал немного больше тестов и выяснил некоторые факты:

  • Я снял флажок Requires full screen, потому что я хочу включить слайд-шоу и слайд-шоу на iPad. Для этого требуется, чтобы приложение поддерживало все 4 ориентации для iPad, в Info.plist: Supported interface orientations (iPad).

Мое приложение работает так же, как Facebook: на iPhone, большую часть времени он заблокирован для портрета. При просмотре изображения в полноэкранном режиме пользователи могут поворачивать пейзаж для лучшего просмотра. На iPad пользователи могут поворачиваться на любую ориентацию в любых контроллерах представлений. Таким образом, приложение выглядит красиво, когда iPad стоит на Smart Cover (альбом слева).

  1. Для того, чтобы iPad вызывал application(_:supportedInterfaceOrientationsFor), в Info.plist сохраняйте портрет только для iPad. Приложение потеряет способность "Слайд + Сплит". Но вы можете заблокировать или разблокировать ориентацию для любого контроллера вида в одном месте и не нужно изменять класс ViewController.

  2. Наконец, эта функция вызывается в режиме жизненного цикла контроллера, когда вид отображается/удаляется. Если вашему приложению необходимо заблокировать/разблокировать/изменить ориентацию в другое время, это может не сработать

Ответ 10

Создайте новое расширение с помощью

import UIKit

extension UINavigationController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}

extension UITabBarController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}

Ответ 11

Вот простой способ, который работает для меня с Swift 4.2 (iOS 12.2), поместите это в UIViewController для которого вы хотите отключить shouldAutorotate:

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
}

Часть .portrait сообщает, в какой ориентации оставаться, вы можете изменить это, как вам нравится. Возможные варианты: .portrait, .all, .allButUpsideDown, .landscape, .landscapeLeft, .landscapeRight, .portraitUpsideDown.

Ответ 12

Спасибо @bmjohn ответить выше. Вот рабочая версия Xamarin/С# этого кода ответа, чтобы сохранить другие время транскрипции:

AppDelegate.cs

 public UIInterfaceOrientationMask OrientationLock = UIInterfaceOrientationMask.All;
 public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
 {
     return this.OrientationLock;
 }

Статический OrientationUtility.cs класс:

public static class OrientationUtility
{
    public static void LockOrientation(UIInterfaceOrientationMask orientation)
    {
        var appdelegate = (AppDelegate) UIApplication.SharedApplication.Delegate;
        if(appdelegate != null)
        {
            appdelegate.OrientationLock = orientation;
        }
    }

    public static void LockOrientation(UIInterfaceOrientationMask orientation, UIInterfaceOrientation RotateToOrientation)
    {
        LockOrientation(orientation);

        UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)RotateToOrientation), new NSString("orientation"));
    }
}

Контроллер просмотра:

    public override void ViewDidAppear(bool animated)
    {
       base.ViewWillAppear(animated);
       OrientationUtility.LockOrientation(UIInterfaceOrientationMask.Portrait, UIInterfaceOrientation.Portrait);
    }

    public override void ViewWillDisappear(bool animated)
    {
        base.ViewWillDisappear(animated);
        OrientationUtility.LockOrientation(UIInterfaceOrientationMask.All);
    }

Ответ 13

Фактически проверенное решение для этого. В моем примере все мое приложение должно быть в портретном режиме, но только одна ориентация экрана должна быть в ландшафтном режиме. Make a Portrait orientation for app , by check only portrait mode

Код в AppDelegate, как описано выше ответы.

var orientationLock = UIInterfaceOrientationMask.all

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask 
{
  return self.orientationLock
}
struct AppUtility {  

static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
    if let delegate = UIApplication.shared.delegate as? AppDelegate {
        delegate.orientationLock = orientation
    }
}
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
self.lockOrientation(orientation)     
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}  
}

Затем запишите этот код, прежде чем ваш контроллер представления альбомной ориентации будет представлен /push.

override func viewWillAppear(_ animated: Bool) {  
super.viewWillAppear(animated)
AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
}  

Затем запишите этот код в реальном viewcontroller (для альбомной ориентации)

override func viewWillAppear(_ animated: Bool) {  
super.viewWillAppear(animated)
AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.landscape, andRotateTo: UIInterfaceOrientation.landscape)
}