Как программно установить ориентацию устройства в iOS 7?

Я работаю над приложением для iPad, используя AutoLayout, где, если пользователь включает определенный режим (режим "голова-вверх"), я хочу поддерживать только портретную (или портретную вверх ногами) ориентацию и, более того, если устройство находится в пейзаж, я хотел бы автоматически переключиться в портретный режим.

В контроллере вида сверху у меня есть следующее:

- (NSUInteger) supportedInterfaceOrientations {
    if (self.modeHeadsUp) {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

- (BOOL) shouldAutorotate {
    return TRUE;
}

Основываясь на ответах, которые я видел в другом месте здесь, кажется, что я должен использовать "application setStatusBarOrientation". Поэтому в методе, в котором пользователь выбрал режим "один на один", я включил:

    UIApplication *application = [UIApplication sharedApplication];
    [application setStatusBarOrientation:UIInterfaceOrientationPortrait
                                animated:YES];

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

На самом деле, когда в ландшафтном режиме после запуска вышеуказанного кода попытка программно установить ориентацию, когда я запрашиваю приложение "statusBarOrientation" со следующим кодом, оно остается на "4" для ландшафтного:

UIApplication *application = [UIApplication sharedApplication];
int orientation = [application statusBarOrientation];
self.movesTextView.text = [NSString stringWithFormat:@"ORIENTATION %d", orientation];

Казалось, что, возможно, autolayout не запускается с помощью setStatusBarOrientation, поэтому я попытался добавить этот код после, но безрезультатно:

    [super updateViewConstraints];
    [self.view updateConstraints];

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

Я что-то упускаю, чтобы заставить меня изменить ориентацию?

Ответ 1

Для iOS 7 и 8:

Objective-C:

NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];

Свифт 3+:

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

Я называю это в - viewDidAppear:

Ответ 2

Используйте это. Идеальное решение проблемы ориентации..ios7 и ранее

[[UIDevice currentDevice] setValue:
    [NSNumber numberWithInteger: UIInterfaceOrientationPortrait]
        forKey:@"orientation"];

Ответ 4

Это работает для меня на Xcode 6 и 5.

- (BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return (UIInterfaceOrientationMaskPortrait);
}

Ответ 5

NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]; [[UIDevice currentDevice] setValue:value forKey:@"orientation"];

работает, но вы должны вернуть shouldAutorotate с YES в вашем контроллере просмотра

- (BOOL)shouldAutorotate
{
    return self.shouldAutoRotate;
}

Но если вы это сделаете, ваш VC будет авторотировать, если пользователь поворачивает устройство... поэтому я изменил его на:

@property (nonatomic, assign) BOOL shouldAutoRotate;

- (BOOL)shouldAutorotate
{
    return self.shouldAutoRotate;
}

и я вызываю

- (void)swithInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    self.rootVC.shouldAutoRotate = YES;

    NSNumber *value = [NSNumber numberWithInt: orientation];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}

чтобы принудительно установить новую ориентацию нажатием кнопки. Чтобы отменить shouldAutoRotate на NO, я добавил в свой rootVC

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    self.shouldAutoRotate = NO;
}

PS: Это обходное решение действительно работает и во всех симуляторах.

Ответ 6

Единственный способ, который работал у меня, - создать фиктивный modal view controller.

UIViewController* dummyVC = [[UIViewController alloc] init];
dummyVC.view = [[UIView alloc] init];
[self presentModalViewController:dummyVC animated:NO];
[self dismissModalViewControllerAnimated:NO];

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

Любопытно, что UINavigationController делает именно это при нажатии/выводе дочерних контроллеров представления с различными поддерживаемыми ориентациями интерфейса (проверено на iOS 6.1, 7.0).

Ответ 7

Это решение позволяет принудительно установить определенную ориентацию интерфейса, временно переопределив значение UIDevice.current.orientation и затем попросив систему повернуть интерфейс в соответствии с вращением устройства:

Важно: это взлом и может перестать работать в любой момент

Добавьте в контроллер корневого приложения приложение:

class RootViewController : UIViewController {
    private var _interfaceOrientation: UIInterfaceOrientation = .portrait
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return UIInterfaceOrientationMask(from: _interfaceOrientation) }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return _interfaceOrientation }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Register for notifications
        NotificationCenter.default.addObserver(self, selector: #selector(RootViewController.handleInterfaceOrientationChangeRequestedNotification(_:)), name: .interfaceOrientationChangeRequested, object: nil)
    }

    deinit { NotificationCenter.default.removeObserver(self) }

    func handleInterfaceOrientationChangeRequestedNotification(_ notification: Notification) {
        guard let interfaceOrientation = notification.object as? UIInterfaceOrientation else { return }
        _interfaceOrientation = interfaceOrientation
        // Set device orientation
        // Important:
        // • Passing a UIDeviceOrientation here doesn't work, but passing a UIInterfaceOrientation does
        // • This is a hack, and could stop working at any moment
        UIDevice.current.setValue(interfaceOrientation.rawValue, forKey: "orientation")
        // Rotate the interface to the device orientation we just set
        UIViewController.attemptRotationToDeviceOrientation()
    }
}

private extension UIInterfaceOrientationMask {

    init(from interfaceOrientation: UIInterfaceOrientation) {
        switch interfaceOrientation {
        case .portrait: self = .portrait
        case .landscapeLeft: self = .landscapeLeft
        case .landscapeRight: self = .landscapeRight
        case .portraitUpsideDown: self = .portraitUpsideDown
        case .unknown: self = .portrait
        }
    }
}

extension Notification.Name {
    static let interfaceOrientationChangeRequested = Notification.Name(rawValue: "interfaceOrientationChangeRequested")
}

Убедитесь, что все ориентации интерфейса отмечены в разделе "Информация о развертывании":

Ориентация интерфейса

Запросите изменения ориентации интерфейса там, где они вам нужны:

NotificationCenter.default.post(name: .interfaceOrientationChangeRequested, object: UIInterfaceOrientation.landscapeRight)

Ответ 8

Если вы хотите заблокировать основной вид своего приложения на портрете, но хотите открыть всплывающие окна в альбомной ориентации, а вы используете tabBarController как rootViewController, как и я, вы можете использовать этот код в AppDelegate.

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UITabBarController *tabBarController;

@end

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Create a tab bar and set it as root view for the application
    self.tabBarController = [[UITabBarController alloc] init];
    self.tabBarController.delegate = self;
    self.window.rootViewController = self.tabBarController;

    ...
}

- (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController
{
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)tabBarControllerPreferredInterfaceOrientationForPresentation:(UITabBarController *)tabBarController
{
    return UIInterfaceOrientationPortrait;
}

Это работает очень хорошо.

В вашем viewController, который вы хотите представить в ландшафте, вы просто используете следующее:

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate {
    return YES;
}

Ответ 9

  • Добавьте это утверждение в AppDelegate.h

    //whether to allow cross screen marker 
    @property (nonatomic, assign) allowRotation BOOL;
    
  • Запишите этот раздел кода в AppDelegate.m

    - (UIInterfaceOrientationMask) application: (UIApplication *) supportedInterfaceOrientationsForWindow: application (UIWindow *) window {
        If (self.allowRotation) {
            UIInterfaceOrientationMaskAll return;
        }
        UIInterfaceOrientationMaskPortrait return;
    }
    
  • Измените свойство allowRotation приложения-делегата

Ответ 10

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

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}

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

Ответ 11

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

-(NSUInteger)supportedInterfaceOrientations {
    UIViewController *topVC = self.topViewController;
    return topVC.supportedInterfaceOrientations;
}

-(BOOL)shouldAutorotate {
   UIViewController *topVC = self.topViewController;
   return [topVC shouldAutorotate];
}

Ответ 12

Если вы хотите только портретный режим, в iOS 9 (Xcode 7) вы можете:

  • Переход к Info.plist
  • Выберите пункт "Поддерживаемые интерфейсные ориентиры"
  • Удалить "Пейзаж (левая кнопка дома)" и "Пейзаж (правая кнопка дома)"

введите описание изображения здесь

Ответ 13

У меня была аналогичная проблема, чем у вас. Мне нужно заблокировать ориентацию устройства для некоторых экранов (например, Login) и разрешить вращение в других.

После нескольких изменений и последующих ответов ниже я сделал это:

  • Включение всех ориентаций в Project Info.plist.

введите описание изображения здесь

  • Отключение ориентации в тех ViewControllers, где мне нужно, чтобы устройство не вращалось, например, на экране "Вход" в моем случае. Мне нужно было переопределить метод shouldAutorotate в этом VC:

-(BOOL)shouldAutorotate{ return NO; }

Надеюсь, это сработает для вас.

Ответ 14

здесь приведен пример FULL WORKING для iOS 7, 8, 9, 10, как изменить ориентацию приложения на его текущую противоположность.

Objective-C

- (void)flipOrientation
{
    NSNumber *value;
    UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if(UIInterfaceOrientationIsPortrait(currentOrientation))
    {
        if(currentOrientation == UIInterfaceOrientationPortrait)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown];
        }
        else //if(currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
        }
    }
    else
    {
        if(currentOrientation == UIInterfaceOrientationLandscapeRight)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
        }
        else //if(currentOrientation == UIInterfaceOrientationLandscapeLeft)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
        }
    }
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
}

Swift 3

func flipOrientation() -> Void
{
    let currentOrientation : UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
    var value : Int = 0;
    if(UIInterfaceOrientationIsPortrait(currentOrientation))
    {
        if(currentOrientation == UIInterfaceOrientation.portrait)
        {
            value = UIInterfaceOrientation.portraitUpsideDown.rawValue
        }
        else //if(currentOrientation == UIInterfaceOrientation.portraitUpsideDown)
        {
            value = UIInterfaceOrientation.portrait.rawValue
        }
    }
    else
    {
        if(currentOrientation == UIInterfaceOrientation.landscapeRight)
        {
            value = UIInterfaceOrientation.landscapeLeft.rawValue
        }
        else //if(currentOrientation == UIInterfaceOrientation.landscapeLeft)
        {
            value = UIInterfaceOrientation.landscapeRight.rawValue
        }
    }
    UIDevice.current.setValue(value, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation()
}

Ответ 15

Попробуйте это вместе с вашим кодом.

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

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

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

Ответ 16

Это сработало у меня отлично....

NSNumber *value = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];

Ответ 18

Для таких, как я, которые боролись за то, чтобы @Sunny Shah принял ответ на работу на iPad. Вам необходимо установить флажок "Требуется полный экран" в настройках проекта. Обратите внимание, что это помешает вашему приложению работать в многозадачном режиме, который может быть или не быть приемлемым.

enter image description here