Как разрешить только один UIViewController вращаться как в альбомном, так и в книжном направлении?


мое приложение только для iphone устройство (как iphone 4 и 5) и построен для поддержки только ios 6.

всем мое приложение поддерживает только portrait режим. Но есть одна точка зрения, которая называется"ChatView", который я хочу поддержать оба landscape и portrait режима.

Я установил необходимые вращения устройства следующим образом -

Я также пробовал следующий код для поддержки вращения в "ChatView" -

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

но он не мог повернуть, что вид.

Я искал много для этого , но не мог найти решение для моей проблемы.

а также в" ChatView " есть некоторые объекты, такие как кнопки, текстовые поля, фреймы которых задаются программно. Поэтому я хочу знать, должен ли я также устанавливать рамки всех этих объектов для ландшафтного режима?

пожалуйста, помогите мне.

спасибо.....

14 57

14 ответов:

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

EDIT

подкласс UINavigationController создайте новый файл с названием, например CustomNavigationController и сделать его подклассом UINavigationController.

.файл H

#import <UIKit/UIKit.h>

@interface CustomNavigationController : UINavigationController

@end

.файл м

#import "CustomNavigationController.h"

@interface CustomNavigationController ()

@end


@implementation CustomNavigationController

-(BOOL)shouldAutorotate
{
    return NO;
}

-(UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}

@end

установлен класс UINavigationController в вашем основном классе xib как CustomNavigationController. Надеюсь, это поможет ypu..

простой, но он работает очень хорошо. IOS 7.1 и 8

AppDelegate.h

@property () BOOL restrictRotation;

AppDelegate.м

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if(self.restrictRotation)
    return UIInterfaceOrientationMaskPortrait;
else
    return UIInterfaceOrientationMaskAll;
}

ViewController

-(void) restrictRotation:(BOOL) restriction
{
    AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
    appDelegate.restrictRotation = restriction;
}

viewDidLoad

[self restrictRotation:YES]; or NO

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

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

в ChatView, это должно быть:

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

Если вам нужно изменить свой макет после поворота, вы должны реализовать соответствующие изменения в своих подвидах в

- (void)viewWillLayoutSubviews

использовать self.view.bounds для проверки текущего размера элемент view С self.view.frame не меняется после вращения.

конкретные viewcontroller.m вы хотите повернуть

добавить этот метод:

- (BOOL)canAutoRotate
{
    return YES;
}

затем внутри AppDelegate.m

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    UIViewController *currentViewController = [self topViewController];

    if ([currentViewController respondsToSelector:@selector(canAutoRotate)]) {
        NSMethodSignature *signature = [currentViewController methodSignatureForSelector:@selector(canAutoRotate)];

        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

        [invocation setSelector:@selector(canAutoRotate)];
        [invocation setTarget:currentViewController];

        [invocation invoke];

        BOOL canAutorotate = NO;
        [invocation getReturnValue:&canAutorotate];

        if (canAutorotate) {
            return UIInterfaceOrientationMaskAll;
        }
    }

    return UIInterfaceOrientationMaskPortrait;
}

- (UIViewController *)topViewController
{
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController *)topViewControllerWithRootViewController:(UIViewController *)rootViewController
{
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}

ответ Теда хорошо работает с проблемой, упомянутой Александром из Норвегии. Но я понял, что проблема происходит не так, как объяснил Александр,

когда ViewController B, который в настоящее время находится в ландшафте (все ориентация включена) возвращает обратно в ViewController A. (портрет только) после того, как пользователь нажимает на кнопку Назад, supportedInterfaceOrientationsForwindow не вызывается и ViewController a заканчивается в альбомной

на самом деле когда ViewController B, который в настоящее время находится в альбомной ориентации (все ориентации включены), возвращается обратно к ViewController A (только портрет) после нажатия пользователем кнопки "Назад", Appdelegate

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;

- это вызов. Но все же корневой контроллер вида-это ViewController B (этот контроллер вида с поддержкой вращения), ViewController A не возвращается к портретной ориентации, так как ViewController B все еще возвращает

-(BOOL)shouldAutorotate{

    return YES;
}

поэтому, когда вы нажимаете кнопку возврата, "shouldAutorotate - > NO" в ViewController B. Затем ViewController a перейдет в портретную ориентацию. Вот что я сделал

@property (nonatomic, assign) BOOL canAutoRotate;

#pragma mark - Public methods
- (BOOL)canAutoRotate
{
    return _canAutoRotate;
}

#pragma mark - Button actions
- (void)backButtonPressed:(UIButton *)sender {
    _canAutoRotate = NO;
   (...)
}

#pragma mark - Init
- (id)init{
    if(self=[super init]) {
        _canAutoRotate = YES;
    }
    return self;
}

исходя из ответа @iAnum, я с поддержкой автоповорота и обнаружения для UIViewController класса.

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

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

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    //Access the current top object.
    UIViewController *viewController = [self.viewControllers lastObject];
    //Is it one of the landscape supported ones?
    if ([viewController isMemberOfClass:[SpecialViewController class]]) {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
    } else
        return UIInterfaceOrientationMaskPortrait;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    //Access the current top object.
    UIViewController *viewController = [self.viewControllers lastObject];
    //Is it one of the landscape supported ones?
    if ([viewController isMemberOfClass:[SpecialViewController class]]) {
        return interfaceOrientation;
    } else
        return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}

здесь обсуждается проблема с появлением VCs https://stackoverflow.com/a/15057537/1277350 где нажатие назад в ландшафте даже не вызовет методы ориентации, поэтому вам нужно немного взломать его, показывая и отклоняя модальный вид.

а затем просто помните, что если вы хотите, чтобы willShowViewController выстрелил, вам нужно установить self.delegate = self и добавьте UINavigationControllerDelegate в свой пользовательский навигационный контроллер вместе с кодом ниже.

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    UIApplication* application = [UIApplication sharedApplication];
    if (application.statusBarOrientation != UIInterfaceOrientationPortrait)
    {
        UIViewController *c = [[UIViewController alloc]init];
        [c.view setBackgroundColor:[UIColor clearColor]];
        [navigationController presentViewController:c animated:NO completion:^{
            [self dismissViewControllerAnimated:YES completion:^{
            }];
        }];
    }
}

сделайте подкласс UINavigationController следующим образом:

MyNavigationController.h

#import <UIKit/UIKit.h>

@interface MyNavigationController : UINavigationController

@end

MyNavigationController.м

#import "MyNavigationController.h"
#import "ServicesVC.h"

@implementation MyNavigationController

-(BOOL)shouldAutorotate{

    return YES;
}

-(NSUInteger)supportedInterfaceOrientations{

    if ([[self.viewControllers lastObject] isKindOfClass:[ServicesVC class]]) {
        return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
    }

    return UIInterfaceOrientationMaskAll;
}

@end

предполагая, что ваш viewcontroller называется: ServicesVC

вот ответ Александра (https://stackoverflow.com/posts/25507963/revisions) в Swift:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {

    var currentViewController: UIViewController? = self.topViewController()
    if currentViewController != nil && currentViewController!.canAutoRotate() {
        return Int(UIInterfaceOrientationMask.All.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)


}

func topViewController() -> UIViewController? {
    if UIApplication.sharedApplication().keyWindow != nil
    {
        return self.topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
    }
    return nil
}

func topViewControllerWithRootViewController(rootViewController: UIViewController?) -> UIViewController? {
    if rootViewController == nil {
        return nil
    }
    if rootViewController!.isKindOfClass(UITabBarController) {
        var tabBarController: UITabBarController = (rootViewController as? UITabBarController)!
        return self.topViewControllerWithRootViewController(tabBarController.selectedViewController)
    }
    else {
        if rootViewController!.isKindOfClass(UINavigationController) {
            var navigationController: UINavigationController = (rootViewController as? UINavigationController)!
            return self.topViewControllerWithRootViewController(navigationController.visibleViewController)
        }
        else {
            if (rootViewController!.presentedViewController != nil) {
                var presentedViewController: UIViewController = rootViewController!.presentedViewController!
                return self.topViewControllerWithRootViewController(presentedViewController)
            }
            else {
                return rootViewController
            }
        }
    }
}

кроме того, вам нужно будет добавить следующий фрагмент кода в AppDelegate.Свифт:

extension UIViewController {
func canAutoRotate() -> Bool {
    return false
}}

и для ViewControllers, для которых вы хотите разрешить все вращения, добавьте эту функцию:

override func canAutoRotate() -> Bool {
    return true
}

Я не уверен в истории этого вопроса (сейчас = iOS 10 timeframe), но самое простое решение отсутствовало, когда я опубликовал это в Октября. 2016.

предполагая, что вы хотите это:

  1. поддержка iOS 7 и только новее (включая iOS 10)
  2. некоторые контроллеры вида должны поддерживать все ориентации, другие должны поддерживать подмножество ориентаций. пример того, что я имею в виду: один-представление-контроллер должен поддерживать только портрет, в то время как все остальные должны поддерживать все направления
  3. все контроллеры вида должны автоматически вращаться, если они поддерживают вращение (то есть вам не нужен код, который исправляет эту проблему в ваших контроллерах вида)
  4. добавлять UINavigationControllers в XIBs/NIBs / раскадровки без необходимости делать что-либо с ними

... тогда (ИМО) самое простое решение -сделать UINavigationControllerDelegate, не подкласс UINavigationController (что нарушает предположение 4 выше).

когда я решил это, я решил сделать мой первый ViewController a UINavigationControllerDelegate. Этот контроллер вида устанавливает себя в качестве делегата навигационного контроллера и возвращает разрешенные ориентации. В моем случае по умолчанию разрешены все ориентации с предпочтительным портретом, но в одном конкретном случае разрешен только портрет. Код ниже от Swift 3 / XCode 8:

    class iPhoneStartViewController: UIViewController {

        var navInterfaceOrientationMask: UIInterfaceOrientationMask?
        var navInterfaceOrientationPreferred: UIInterfaceOrientation! = .portrait

        override func viewDidLoad() {
            super.viewDidLoad()
            self.navigationController?.delegate = self
        }

        @IBAction func cameraButtonPressed(_ sender: AnyObject) {
            if PermissionsHelper.singleton().photosPermissionGranted() == false {
                self.navInterfaceOrientationMask = nil   // default is: all orientations supported
                self.performSegue(withIdentifier: "segueToPhotoAccess", sender: self)
            } else {
                self.navInterfaceOrientationMask = .portrait // this stops the next view controller from being to rotate away from portrait
                self.performSegue(withIdentifier: "segueToCamera", sender: self)
            }
        }
     }

     // lock orientation to portrait in certain cases only. Default is: all orientations supported
    extension iPhoneStartViewController : UINavigationControllerDelegate {
        public func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
            if let mask = self.navInterfaceOrientationMask {
                return mask
            } else {
                return .all
            }
        }

        public func navigationControllerPreferredInterfaceOrientationForPresentation(_ navigationController: UINavigationController) -> UIInterfaceOrientation {
            return self.navInterfaceOrientationPreferred
        }
    }

Swift 3 Кошерная Версия

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

документация Apple на supportedInterfaceOrientations говорит:

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

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

Итак, что вы должны сделать, это проверить, поддерживает ли приложение все режимы (перейдите к информации о развертывании в общих настройках целей или информации.plist), узнайте класс вашего корневого представления контроллер. Это может быть универсальный UIViewController, UINavigationController, UITabBarController или какой-то пользовательский класс. Вы можете проверить это, например, так:

dump(UIApplication.shared.keyWindow?.rootViewController)

или любым другим удобным для вас способом.

пусть будет немного CustomNavigationController. Так что вы должны переопределить supportedInterfaceOrientations такой:

class CustomNavigationController: UINavigationController {

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return topViewController?.supportedInterfaceOrientations ?? .allButUpsideDown
    }
}

в любом контроллере вида, который должен поддерживать только портретную ориентацию, например override supportedInterfaceOrientations таким образом:

class ChildViewController: UIViewController {

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}

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

override var shouldAutorotate: Bool {
    return true
}

иначе supportedInterfaceOrientations не будет вызван.

здесь вы идете!

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

надеюсь, что это будет помощь.

у меня была такая же ситуация. Поэтому я подкласс UINavigationController в CustomNavigationController, и внутри этого CustomNavigationController, я написал

#define IOS_OLDER_THAN_6 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] < 6.0 )
#define IOS_NEWER_OR_EQUAL_TO_6 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 6.0 )


#pragma mark - Rotation

#ifdef IOS_OLDER_THAN_6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
      return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
#endif
#ifdef IOS_NEWER_OR_EQUAL_TO_6
-(BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;;
}
#endif

я использовал этот CustomNavigationController вместо существующего NavigationController.

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

#pragma mark - Rotation

#ifdef IOS_OLDER_THAN_6

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight | toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

#endif

#ifdef IOS_NEWER_OR_EQUAL_TO_6

-(BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft;
}

#endif

внутри CustomNavigationController я представил этот контроллер представления, а не толкнул в навигационный стек. Таким образом, LandScapeView появился в ландшафтной ориентации.

LandScapeView *graph = [[LandScapeView alloc]init....];
[self presentViewController:graph animated:YES completion:nil];

Я ничего не менял в Поддерживается Ориентация Интерфейса в настройках проекта.

/ / вставьте этот метод в приложение deligate class

- (UIInterfaceOrientationMask)application:(UIApplication )application supportedInterfaceOrientationsForWindow:(UIWindow )window
{
 if ([self.window.rootViewController.presentedViewController isKindOfClass: [_moviePlayerController class]])
  {
   if (self.window.rootViewController.presentedViewController)
        return UIInterfaceOrientationMaskAll;
    else return UIInterfaceOrientationMaskPortrait;
  }
 else return UIInterfaceOrientationMaskPortrait;
}   

Если приложение поддерживает от IOS7 до iOS 9 охота используйте этот код для ориентации:

#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
- (NSUInteger)supportedInterfaceOrientations
#else
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
#endif
{
    if([AppDelegate isPad]) return UIInterfaceOrientationMaskAll;
    else return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
}

Я знаю, что этот вопрос очень старый, но он должен обновленный ответ. Самый простой и правильный способ достижения этого результата-включить портретную и альбомную ориентацию в настройках приложения. Затем добавьте этот код в делегат приложения:

 func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {

    if let navigationController = self.window?.rootViewController as? UINavigationController {

        if navigationController.visibleViewController is INSERTYOURVIEWCONTROLLERHERE  {
            return UIInterfaceOrientationMask.All
        }

        else {
            return UIInterfaceOrientationMask.Portrait
        }
    }

    return UIInterfaceOrientationMask.Portrait
}

Не забудьте заменить "INSERTYOURVIEWCONTROLLERHERE" на контроллер вида.