2015-02-02 2 views
7

У меня есть контроллер вида с контроллером детского вида.Принудительный просмотр iOS, чтобы не вращаться, но все же позволяя ребенку вращаться

tab bar controller 
| 
| 
nav controller 
| 
| 
UIPageViewController (should rotate) 
| 
| 
A (Video Player) (shouldn't rotate) 
| 
| 
B (Controls overlay) (should rotate) 

A должен быть вынужден постоянно сохранять портрет, но B должен иметь возможность свободно вращаться.

Я знаю, что shouldAutorotate применим к любым контроллерам вида и его детям, но есть ли способ обойти это? Похоже, я мог использовать shouldAutorotateToInterfaceOrientation, но это заблокировано в iOS 8.

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

Я использую Swift.

+0

Вы нажимаете ViewControllerB из ViewControllerA? –

+0

Я добавляю его в качестве подзаголовка. 'self.view.addSubview (viewController.view)' – switz

ответ

17

У меня была эта точная проблема, и быстро выяснилось, что вокруг авторотации существует множество плохих советов, особенно потому, что iOS 8 обрабатывает ее иначе, чем предыдущие версии.

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

Во время изменения ориентации в iOS 8 само окно вращается, а не содержит вид (ы), который он содержит. Вы можете добавить виды нескольких контроллеров просмотра к одному UIWindow, но только rootViewController получит возможность ответить через shouldAutorotate(). Несмотря на то, что вы принимаете решение о ротации на уровне контроллера представления, это фактическое вращение родительского окна, таким образом, вращение всех его подзонов (в том числе и от других контроллеров представлений).

Решение состоит из двух UIWindow, уложенных друг на друга, каждый из которых (или нет) имеет свой собственный контроллер корневого представления. В большинстве приложений есть только один, но нет причин, по которым вы не можете иметь два и наложить их так же, как и любой другой подкласс UIView.

Настоящая рабочая концепция, which I've also put on GitHub here. Ваш конкретный случай немного сложнее, потому что у вас есть стек содержащихся контроллеров представлений, но основная идея такая же. Я коснусь некоторых конкретных пунктов ниже.

@UIApplicationMain 
class AppDelegate: UIResponder, UIApplicationDelegate { 
    var cameraWindow: UIWindow! 
    var interfaceWindow: UIWindow! 

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool { 
     let screenBounds = UIScreen.mainScreen().bounds 
     let inset: CGFloat = fabs(screenBounds.width - screenBounds.height) 

     cameraWindow = UIWindow(frame: screenBounds) 
     cameraWindow.rootViewController = CameraViewController() 
     cameraWindow.backgroundColor = UIColor.blackColor() 
     cameraWindow.hidden = false 

     interfaceWindow = UIWindow(frame: CGRectInset(screenBounds, -inset, -inset)) 
     interfaceWindow.rootViewController = InterfaceViewController() 
     interfaceWindow.backgroundColor = UIColor.clearColor() 
     interfaceWindow.opaque = false 
     interfaceWindow.makeKeyAndVisible() 

     return true 
    } 
} 

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

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

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

class InterfaceViewController: UIViewController { 
    var contentView: UIView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     contentView = UIView(frame: CGRectZero) 
     contentView.backgroundColor = UIColor.clearColor() 
     contentView.opaque = false 

     view.backgroundColor = UIColor.clearColor() 
     view.opaque = false 
     view.addSubview(contentView) 
    } 

    override func viewWillLayoutSubviews() { 
     super.viewWillLayoutSubviews() 

     let screenBounds = UIScreen.mainScreen().bounds 
     let offset: CGFloat = fabs(screenBounds.width - screenBounds.height) 

     view.frame = CGRectOffset(view.bounds, offset, offset) 
     contentView.frame = view.bounds 
    } 

    override func supportedInterfaceOrientations() -> Int { 
     return Int(UIInterfaceOrientationMask.All.rawValue) 
    } 

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

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

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

Извините, что опубликовать роман; Я не видел, чтобы это обращалось в другом месте, и мне потребовалось некоторое время, чтобы понять, надеюсь, это поможет вам и всем, кто пытается добиться такого же поведения. Даже пример приложения Apple AVCam не справляется с этим совершенно правильно.

The example repo I posted также включает версию с уже установленной камерой. Удачи!

+0

Я попытался использовать MainWindow как способ сделать это с помощью PreviewView в этом окне и UIViewController выше, чтобы обрабатывать вращение для других видов сверху. Он работал до iOS 7, но сломался с 7. Я не пробовал iOS8. Вы пробовали свой код с iOS 7? Использование двух окон; блестящий! – mahboudz

+0

Ожидаете ли вы, что для FixedViewController будет использоваться любой пользовательский интерфейс, например кнопки или распознаватели жестов? – mahboudz

+0

Возможно, это не сработает из коробки для 7, но может быть немного изменено (я только нацелился на 8). Пользовательское взаимодействие с FixedViewController работает нормально, потому что основное представление получает вставку с линией 'view.frame = CGRectOffset (view.bounds, offset, offset)' (вы все равно захотите использовать 'contentView' для своего фактического контента, хотя) , – jstn

0

Я не уверен, но я думаю, что вы можете создать собственный класс для своего подсмотра и переопределить метод shouldAutorotate и т. Д. Таким образом, он должен переопределить с родительского контроля.

1

Вы можете попробовать это -

Цель -C код, если у вас есть его альтернатива в скор:

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window 
{ 
    if()//Place your condition here like if A is visible 
    { 
    return UIInterfaceOrientationMaskPortrait; 
    } 
    return UIInterfaceOrientationMaskAll; 
} 
1

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

0

Короткий ответ: Нет, все видимые контроллеры и виды вращаются (или не вращаются) вместе.

Длинный ответ:

Во-первых, вы должны реализовать АВТОПОВ функции принятия решения в корневом контроллере; что может означать создание подконтрольного контроллера.

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

Или, вы не можете авторотацию, но слушать уведомления о том, что физическое устройство поворачивается, и вручную вращать любой вид вы хотите, например: Replicate camera app rotation to landscape IOS 6 iPhone

см Также FYI:

How to force a UIViewController to Portrait orientation in iOS 6

shouldAutoRotate Method Not Called in iOS6

iOS6: supportedInterfaceOrientations not working (is invoked but the interface still rotates)

How to implement UIViewController rotation in response to orientation changes?

0

Самый простой, самый прямой ответ на этот вопрос - посмотреть пример кода Apple AVCam. Основные части для меня было то, что он:

  1. использует вид которого layerClass является AVCaptureVideoPreviewLayer.
  2. Устанавливает videoOrientation соединения AVCaptureVideoPreviewLayer в соответствии с statusBarOrientation приложения при представлении представления, в основном viewWillAppear(_:).
  3. Устанавливает videoOrientation для соответствия UIDevice.currentDevice().orientation в viewWillTransitionToSize(_:withTransitionCoordinator:).
  4. Позволяет авторотировать и поддерживает все ориентации интерфейса.

Я реализовал подход к фоновому окну, описанный jstn, и он работал достаточно хорошо, но реальность такова, что она намного сложнее, чем это необходимо. AVCam отлично работает и имеет относительно простой подход.