iOS: отключить авторотацию для подпредставления

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

UIViewController
    UIView
        - UIView
            - UIImageView (disable rotation)
            - UIImageView
            - UIView (disable rotation)
        - UIView
        - UIView
        ...

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

Один из подходов, по-видимому, вращает подпредставления вручную в willAnimateRotationToInterfaceOrientation:. Это не особенно привлекательно, учитывая, что SDK выполняет ротацию, которую я бы просто отменил.

Есть ли способ просто отключить изменение ориентации для подвидов или какой-либо другой метод реструктуризации моей иерархии?


person Jason George    schedule 08.09.2011    source источник
comment
Простое решение: stackoverflow.com/a/6292506/294884   -  person Fattie    schedule 14.07.2016


Ответы (3)


Автоповорот обрабатывается UIViewController представления (shouldAutorotateToInterfaceOrientation:), поэтому один из подходов состоит в том, чтобы упорядочить вашу иерархию таким образом, чтобы вращаемые представления управлялись одним контроллером представления, а невращаемые представления - другим контроллером представления. Затем оба этих корневых представления UIViewController необходимо добавить в окно/суперпредставление.

Тонкость здесь в том, что если у вас есть два представления контроллера представления на одном уровне (т.е. добавленные через addSubview:), только первый контроллер представления (обычно rootViewController окна) получит сообщение shouldAutorotateToInterfaceOrientation:.

Я сам использовал этот подход для создания панели инструментов, которая вращается, а основной вид - нет.

Технические вопросы и ответы Apple QA1688 (" Почему мой UIViewController не вращается вместе с устройством?") немного рассказывает об этой проблеме.


Обновление для iOS 6:

Авторотация теперь использует методы shouldAutorotate и supportedInterfaceOrientations UIViewController. shouldAutorotate возвращает YES по умолчанию, но помните, что контроллер представления, отличный от rootViewController, представление которого является прямым подпредставлением окна, в любом случае НЕ будет получать обратные вызовы поворота.


Пример кода для iOS 6:

Создайте новый проект, используя шаблон «Приложение с одним представлением», и убедитесь, что установлен флажок «Использовать раскадровки». Мы будем использовать предоставленный класс ViewController в качестве контроллера вращающегося представления (переименуйте его, если хотите!), и создадим второй подкласс UIViewController с именем NonRotatingViewController. Хотя этот контроллер представления никогда не будет даже получать обратные вызовы вращения, для полноты и ясности добавьте следующий код в NonRotatingViewController.m:

- (BOOL)shouldAutorotate
{
    return NO;
}

В файле MainStoryboard перетащите новый объект контроллера представления и установите для его класса значение NonRotatingViewController, а для идентификатора раскадровки установите значение «NonRotatingVC». Пока вы там, измените цвет фона представления контроллера вращающегося представления на прозрачный (невращающееся представление будет добавлено под этим) и добавьте метку к каждому представлению. В AppDelegate.m добавьте следующий код:

#import "NonRotatingViewController.h"

// ...
// ...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    NonRotatingViewController *nonRotatingVC = [mainStoryboard instantiateViewControllerWithIdentifier:@"NonRotatingVC"];
    [self.window addSubview:nonRotatingVC.view];
    return YES;
}

Это просто создание экземпляра невращающегося контроллера представления и добавление его представления непосредственно в окно (примечание: в этот момент окно rootViewController уже установлено раскадровкой).

Запустите проект. Вращайте устройство и удивляйтесь, как одна этикетка вращается, а другая остается неподвижной!


Пример кода до iOS 6:

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

'RotatingViewController.m'

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


'NonRotatingViewController.m'

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (interfaceOrientation == UIInterfaceOrientationPortrait) {    // Or whatever orientation it will be presented in.
        return YES;
    }
    return NO;
}


'AppDelegate.m'

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    RotatingViewController *rotating = [[RotatingViewController alloc] initWithNibName:@"RotatingViewController" bundle:nil];
    self.rotatingViewController = rotating;
    [rotating release];

    NonRotatingViewController *nonRotating = [[NonRotatingViewController alloc] initWithNibName:@"NonRotatingViewController" bundle:nil];
    self.nonRotatingViewController = nonRotating;
    [nonRotating release];

    [self.window addSubview:self.rotatingViewController.view];
    [self.window insertSubview:self.nonRotatingViewController.view belowSubview:self.rotatingViewController.view];

    [self.window makeKeyAndVisible];

    return YES;
}

Надеюсь, это поможет.

person Stuart    schedule 08.09.2011
comment
Этот подход, кажется, не работает для меня. Я настроил иерархию представлений с родительским контроллером представлений, который содержит два дочерних контроллера представлений (portraitViewController и rotateViewController), каждый из которых содержит UIImageView, причем shouldAutorotateToInterfaceOrientation возвращает NO для портретной ориентации и YES для поворота. Если я установлю для shouldAutorotateToInterfaceOrientation значение NO в моем родителе, ориентация будет статической, как и ожидалось. Однако установка для shouldAutorotateToInterfaceOrientation значения YES в моем родительском элементе приводит к повороту обоих. Есть ли шанс, что вы могли бы опубликовать код? Я где-то ошибся. - person Jason George; 10.09.2011
comment
@Jason: Контроллеры просмотра могут быть довольно темпераментными и специфическими - использование пользовательского контроллера «контейнера», как вы описали, может быть причиной проблемы. Я бы позаботился о том, чтобы ваши представления контроллера дочернего представления были прикреплены непосредственно к окну приложения. - person Stuart; 10.09.2011
comment
Спасибо, СтуДев. +1 вокруг. Контроллер «контейнера» определенно вызывает проблему. - person Jason George; 13.09.2011
comment
спасибо, это сложная штука, и это решение отлично сработало для меня. я также хотел бы добавить, что разделение представлений означает, что мой вращающийся вид должен проходить через события касания... этот ответ помог с этим: stackoverflow.com/questions/ 7381155/ - person jd.; 09.04.2012
comment
В iOS 6 вам, вероятно, потребуется установить корневой контроллер представления. Это сработало для меня: self.window.rootViewController = self.rotatingViewController; Затем вы звоните [self.window insertSubview: self.nonRotatingViewController.view belowSubview:self.rotatingViewController.view]; (В моем случае - у вас может быть по-другому.) - person Zev Eisenberg; 02.10.2012
comment
Это не кажется правильным. Где вы устанавливаете self.window.rootViewController? У вас есть два viewController, но ни один из них не установлен в качестве rootViewController вашего приложения. - person mahboudz; 18.01.2013
comment
@mahboudz: Интересный момент - я предполагаю, что шаблоны проектов Apple изменились и что контроллер корневого представления был установлен в построителе интерфейса (хотя у меня нет возможности это проверить). Контроллер представления, который должен получать обратные вызовы вращения, действительно должен быть установлен как rootViewController окна. Я обновлю ответ, чтобы предоставить актуальный рабочий пример. - person Stuart; 18.01.2013
comment
Наверное, мне трудно понять, как это работает. Во-первых, вставка представления nonRotatingViewController в иерархию представлений окна на самом деле не активирует nonRotatingViewController, а просто помещает это представление под управление окна, которое не выполняет такие действия, как автоповорот. По сути, вы могли бы использовать любое представление, которое вы создали и загрузили таким же образом, а nonRotatingViewController не работает. Можете ли вы сказать мне, где я ошибаюсь? - person mahboudz; 18.01.2013
comment
@mahboudz: я не уверен, что вы подразумеваете под активацией контроллера представления. Контроллер представления управляет иерархией представлений, и это верно для nonRotatingViewController — он по-прежнему управляет загрузкой и представлением своего представления (viewDidLoad, viewWillAppear и т. д.) и другими задачами контроллера, такими как представление дополнительных контроллеров представления. Добавляя его вид непосредственно в окно, все, что мы делаем, — это предотвращаем получение nonRotatingViewController методов поворота. При вращении окно регулирует только рамку и вращение rootViewController. - person Stuart; 18.01.2013
comment
@mahboudz: хотя можно было бы добиться чего-то подобного, добавив любой экземпляр UIView в качестве подпредставления окна, вы не должны этого делать, поскольку каждое представление должно управляться UIViewController (либо как корневое представление контроллера представления, либо как часть иерархии, корневое представление которой управляется контроллером представления). Возможно, вы запутались, предполагая, что UIWindow выполняет работу контроллера представления - UIWindow является подклассом UIView, поэтому вы можете добавлять к нему подпредставления. Представление NonRotatingViewController по-прежнему управляется NonRotatingViewController. - person Stuart; 18.01.2013
comment
Я думаю, что в iOS 8 они действительно вращают окно, поэтому, чтобы быть стабильным в вращающемся мире, ваш взгляд должен вращаться в противоположном направлении. Возникает вопрос: существует ли какое-либо действительно фиксированное представление в таком новом мире? - person Brian Cannard; 11.01.2015
comment
К вашему сведению, я решил проблему, создав 2 окна: одно вращающееся, а второе - фиксированное. Каждое окно имеет свой корневой вью-контроллер — один фиксированный, другой — вращающийся :-) - person Brian Cannard; 11.01.2015

Вот еще один способ. Просто поместите этот код в свой viewController viewDidLoad:

    YourAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
        // the view you don't want rotated (there could be a heierarchy of views here):
    UIView *nonRotatingView = [[UIView alloc] initWithFrame:CGRectMake(100,0,30,500)];
    nonRotatingView.backgroundColor = [UIColor purpleColor];

        // make sure self.view and its children are transparent so you can see through to this view that will not be rotated behind self.view.

    [delegate.window insertSubview:nonRotatingView  belowSubview:self.view];

        // you can't put it in the front using:
        //    [delegate.window insertSubview:nonRotatingView aboveSubview:self.view];
        // It will show up the same as before

    // you should declare nonRotatingView as a property so you can easily access it for removal, etc.
person mahboudz    schedule 18.01.2013
comment
Это работает отлично для меня! Проще, чем другое решение. - person Luca Carlon; 25.02.2013
comment
Кто-то должен объяснить, почему это работает, потому что код не делает это очевидным. - person Steven Lu; 01.09.2013
comment
NonRotatingView размещается между окном приложения и представлением ViewControllers и не управляется контроллером представления. Контроллер представления будет обрабатывать изменения ориентации только для своих представлений и будет игнорировать это представление, которое было помещено между ним и окном. - person mahboudz; 01.09.2013
comment
Я реализовал это, так как у меня есть AVCaptureSession в моем контроллере представления, который, как я хочу, не должен вращаться, но я хочу, чтобы невращающийся вид был полной ширины и высоты, как мой контроллер представления self.view, когда я открываю свою камеру, но когда я это делаю в полном размере кнопки на моем self.view контроллера просмотра не видны, нет никакой идеи, как я могу сделать их видимыми, сохраняя при этом не вращающийся вид в полноэкранном режиме. - person Avinash Sharma; 13.03.2016