Как принудительно настроить ориентацию контроллера вида в 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. Кроме того, я видел некоторые ответы в Stack overflow, где люди предлагали нам всегда избегать этого подхода с iOS 8 и далее.



чтобы быть более конкретным, мое приложение является универсальным типом приложения. Есть три контроллера в общей сложности.




  1. первый вид-контроллер - он должен поддерживать все ориентации в iPad и только портрет (главная кнопка вниз) в айфон.


  2. контроллер второго вида - он должен поддерживать только ландшафт прямо во всех условиях


  3. третий вид-контроллер - он должен поддерживать только ландшафт прямо во всех условиях



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



ниже shouldAutorotate и supportedInterfaceOrientations методы во втором и третьем контроллере вида.



-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
return NO;
}


есть ли какое-либо решение для этого или какой-либо лучший способ блокировки контроллера вида в определенной ориентации для iOS 8. Пожалуйста, помогите!!

861   24  

24 ответов:

для iOS 7 - 10:

Цель-C:

[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];

Swift 3:

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()

просто позвоните в - viewDidAppear: представленного контроллера представления.

ориентация вращения немного сложнее, если вы находитесь внутри UINavigationController или UITabBarController. Проблема заключается в том, что если контроллер вида встроен в один из этих контроллеров, то контроллер навигации или панели вкладок имеет приоритет и принимает решения об авторотации и поддерживаемых ориентациях.

я использую следующие 2 расширения на UINavigationController и UITabBarController, чтобы контроллеры представления, встроенные в один из этих контроллеров, могли сделать решения.

дайте регуляторы взгляда силу!

Swift 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()
    }
}

Swift 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. По какой-то причине он хочет использовать значение ориентации по умолчанию. См. следующее сообщение в блоге, Если вы заинтересованы в получении информации о том, как обойти некоторые из проблем:

Блокировка Вращения Экрана

Это то, что сработало для меня:

https://developer.apple.com/library//ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/clm/UIViewController/attemptRotationToDeviceOrientation

вызовите его в своем методе viewDidAppear:.

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [UIViewController attemptRotationToDeviceOrientation];
}

я обнаружил, что если это представленный контроллер вида, вы можете переопределить preferredInterfaceOrientationForPresentation

Swift:

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 (этот метод не требует переопределения функций ориентации, таких как shouldautorotate на каждом viewController, только один метод на AppDelegate)

установите флажок "требуется полный экран"в общей информации проекта. enter image description here

Итак, на AppDelegate.swift сделать переменную:

var enableAllOrientation = false

Итак, поставьте также эту функцию:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
        if (enableAllOrientation == true){
            return UIInterfaceOrientationMask.All
        }
        return UIInterfaceOrientationMask.Portrait
}

Итак, в каждом классе в вашем проект вы можете установить этот var в viewWillAppear:

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
        }
    }

Это должно работать с 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;
}

и реализовать методы там.

это обратная связь с комментариями в ответ Сид Шаха, о том, как отключить анимацию, используя:

[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];
}

прежде всего-это плохая идея, в общем, что-то не так происходит с вашей архитектурой приложения, но, 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)

вот и все

On 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
}

Если вы используете navigationViewController вы должны создать свой собственный суперкласс для этого и переопределить:

- (BOOL)shouldAutorotate {
  id currentViewController = self.topViewController;

  if ([currentViewController isKindOfClass:[SecondViewController class]])
    return NO;

  return YES;
}

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

предположим, что вы используете раскадровки. Вы должны создать ручной переход (как к) и в вашем методе "onClick":

- (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender {
  NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
  [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
  [self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];
}

это заставит альбомную ориентацию перед вашим суперклассом отключить функцию авторотации.

Я пробовал много решений, но тот, который работал на следующее:

нет необходимости редактировать info.plist в ios 8 и 9.

- (BOOL) shouldAutorotate {
    return NO;
}   

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);
}

возможные ориентации из документации Apple:

UIInterfaceOrientationUnknown

ориентация устройства не может быть определен.

UIInterfaceOrientationPortrait

в устройство находится в портретном режиме, при этом устройство удерживается в вертикальном положении и главная кнопка на дне.

UIInterfaceOrientationPortraitUpsidedown

устройство находится в портретном режиме, но вверх ногами, удерживая устройство с вертикально и кнопка "Домой" вверху.

UIInterfaceOrientationLandscapeLeft

устройство находится в ландшафтном режиме, при этом устройство удерживается в вертикальном положении и главная кнопка слева сторона.

UIInterfaceOrientationLandscapeRight

устройство находится в ландшафтном режиме, при этом устройство удерживается в вертикальном положении и главная кнопка на правой стороне.

комбинация ответов Sids и Koreys сработала для меня.

расширение навигационного контроллера:

extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

затем отключение вращения на одном виде

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

и вращая в соответствующую ориентацию, прежде чем переходить

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 в контроллере представления представления.

(извините, недостаточно очков репутации, чтобы прокомментировать это решение, поэтому мне пришлось добавить его так)

мои требования

  1. блокировка всех видов в портретном режиме
  2. использовать AVPlayerViewController для воспроизведения видео

когда видео воспроизводится, если это пейзаж, то позвольте экрану вращать пейзаж вправо и пейзаж влево. Если это портрет, то заблокировать вид только в портретном режиме.

во-первых, определить supportedInterfaceOrientationsForWindow на AppDelegate.swift

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!
}

потому что пользователь может повернуть устройство ландшафт влево или вправо, нам нужно установить auto rotate, чтобы быть 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];
}

По данным ответ Кори Хинтона

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
}

я попробовал несколько решений здесь, и важно понять, что это корневой контроллер представления, который будет определять, будет ли он вращаться или нет.

Я создал следующее С проект github.com/GabLeRoux/RotationLockInTabbedViewChild с рабочим примером TabbedViewController где один дочерний вид разрешен поворот, а другой дочерний вид заблокирован в портрете.

это не идеально, но это работает и та же идея должен работать для других видов корневых представлений, таких как NavigationViewController. :)

Child view locks parent orientation

[iOS 9 охота+] Если кто-то тащил весь путь сюда, поскольку ни одно из вышеперечисленных решений не работало, и если вы представляете вид, который вы хотите изменить ориентацию на переход, возможно, вы захотите проверить это.

Проверьте тип презентации вашего segue. Мой был над текущим контекстом'. Когда я изменил его на полноэкранный режим, он работал.

благодаря @GabLeRoux, я нашел это решение.

  • это изменение работает только в сочетании с выше решений.

согласно решению, показанному @sid-sha, вы должны поместить все в viewDidAppear: метод, иначе вы не получите didRotateFromInterfaceOrientation: уволили, так что что-то вроде:

- (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 является ViewController вы хотите поддерживать ландшафтный режим.

затем решение солнечного Шаха будет работать в вашем XXViewController на любой версии iOS:

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). Просто измените конкретную ориентацию, которую вы хотите заставить, если вы хотите что-то другое, чем LandscapeLeft.

ключ для меня был элемент attemptRotationToDeviceOrientation вызов в viewWillAppear - без этого вид не будет правильно вращаться без физического вращения устройства.

У меня та же проблема и тратить так много времени на это. Так что теперь у меня есть свое решение. Мои настройки приложения просто поддерживают только портрет.Однако некоторые экраны в моем приложении должны иметь только ландшафт.Я исправляю это с помощью переменной isShouldRotate at AppDelegate. И функция в AppDelegate:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
    if isShouldRotate == true {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

и, наконец, когда a ViewControllerA нужно состояние ландшафта. Просто сделайте это:перед нажать/присутствует до ViewControllerA назначение isShouldRotate до правда. Не забывайте, когда pop / отклонить этот контроллер назначить isShouldRotate до false at viewWillDisappear.

для меня VC верхнего уровня необходим для реализации переопределений ориентации. Использование VC вниз по стеку не будет иметь никакого эффекта, если верхний VC не реализует.

VC-main
    |
    -> VC 2
        |
        -> VC 3

слушается только VC-Main, по существу, в моем тестировании.

Comments

    Ничего не найдено.