Подтвердить что ты не робот

Контроллер детского просмотра для поворота, пока контроллер родительского контроля не

Что я пытаюсь сделать:

Родительский просмотр, управляемый контроллером родительского контроля НЕ ДОПУСКАЕТ.

Child View, управляемый контроллером детского просмотра СЛЕДУЕТ ВОСПРОИЗВЕДЕНИЕ ко всем ориентациям.


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

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


Что я пробовал:

ParentViewController

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

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

fun addChildViewController() {
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    self.childViewController = storyBoard("Child View Controller") as? ChildViewController
    self .addChildViewController(self.childViewController!)
    self.childViewController! .didMoveToParentViewController(self)
    self.view .addSubview(self.childViewController!.view)
    self.childViewController!.view.frame = CGRectMake (40, 200, 400, 250)
}

ChildViewController

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

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

Поддерживаемые ориентации в информации о развертывании Xcode установлены на все четыре.

Что я получаю:

Ни один вид View не вращается. Если я устанавливаю родительский поворот ко всем, все представления вращаются вместе. Так что все это или ничего.


ОБНОВЛЕНИЕ

Когда я пытаюсь помещать наблюдателя для UIDeviceOrientationDidChangeNotification и использовать UIDevice.currentDevice(). Ориентация для поворота childView с использованием CGAffaineTransformMakeRotate, я получаю желаемые результаты. ОДНАКО, т.е. Если я повернусь к пейзажу и попытаюсь вытащить центр уведомлений (или, если я получу системное уведомление), который все еще находится в портретной ориентации (потому что приложение по-прежнему остается в портрете), поворачивается childView поворачивается назад к портрету, чтобы почтить статус-бар/центр уведомлений/уведомлений.

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

4b9b3361

Ответ 1

После многих взад и вперед с Apple сами, и они указали на ту же ссылку Technical Q & A QA1890, мне пришлось это сделать с плавным путем:

MotherViewController

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

        coordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
            let deltaTransform: CGAffineTransform = coordinator.targetTransform()
            let deltaAngle: CGFloat = atan2(deltaTransform.b, deltaTransform.a)
            var currentRotation: CGFloat = self.mainView.layer.valueForKeyPath("transform.rotation.z") as! CGFloat

            // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
            currentRotation += -1 * deltaAngle + 0.0001
            self.mainView.layer.setValue(currentRotation, forKeyPath: "transform.rotation.z")

            }, completion: {(
                context: UIViewControllerTransitionCoordinatorContext) -> Void in
                // Integralize the transform to undo the extra 0.0001 added to the rotation angle.
                var currentTransform: CGAffineTransform = self.mainView.transform
                currentTransform.a = round(currentTransform.a)
                currentTransform.b = round(currentTransform.b)
                currentTransform.c = round(currentTransform.c)
                currentTransform.d = round(currentTransform.d)
                self.mainView.transform = currentTransform
        })
    }

MainViewController

NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name:UIDeviceOrientationDidChangeNotification, object: nil)

func orientationChanged ( notification: NSNotification){
        switch UIDevice.currentDevice().orientation {
        case .Portrait:
            self.rotateChildViewsForOrientation(0)
        case .PortraitUpsideDown:
            self.rotateChildViewsForOrientation(0)
        case .LandscapeLeft:
            self.rotateChildViewsForOrientation(90)
        case .LandscapeRight:
            self.rotateChildViewsForOrientation(-90)
        default:
            print ("Default")
                }
    }

Кажется, что он поворачивает дочерний вид, сохраняя основной вид статическим. Также приложение, похоже, знает правильную ориентацию, когда появляются уведомления (поскольку просмотр матери фактически вращается за кулисами).

Особая благодарность jamesk и Warif Akhand Rishi за всю помощь!

Ответ 2

Эффект, достигнутый в приложении "Камера", аналогичен эффекту:

В технической заметке объясняется, что базовая реализация авторотации включает в себя применение преобразования непосредственно к окну приложения:

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

Обратите внимание, что приложение "Камера" не отключает авторотацию: при использовании приложения "Камера" ориентации Центра уведомлений и Центра управления отражают ориентацию устройства. Только определенные представления в приложении "Камера", как представляется, исключают авторотацию. Фактически, этот ответ предполагает, что такие приложения обычно используют свойства videoGravity и videoOrientation, предоставляемые классами AVFoundation, такие как AVCaptureVideoPreviewLayer и AVCaptureConnection.

Подход, который вы должны предпринять, зависит от того, хотите ли вы остановить поворот родительского представления или хотите ли вы остановить контроллер просмотра контейнера, такой как UINavigationController, от поворота (т.е. ориентация устройства блокировки).

Если первый использует технику, описанную в технической заметке, для исправления ориентации родительского представления и (для iOS 9) обертывает вращающиеся подпункты в UIStackView и использует свойство axis для изменения подзонов на вращении устройства, или (для iOS 8) вручную поверните подпункты на вращение устройства (что касается этого ответа и этот пример кода).

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

Добавление: причина, по которой вызовы shouldAutorotate и supportedInterfaceOrientations не распространялись на контроллеры дочерних представлений, объяснялась в Технический Q & A QA1688:

Начиная с iOS 6, только самый верхний контроллер вида (рядом с объектом UIApplication) участвует в принятии решения о том, следует ли вращаться в ответ на изменение ориентации устройства. Чаще всего контроллер просмотра, отображающий ваше содержимое, является дочерним элементом UINavigationController, UITabBarController или настраиваемым контроллером представления контейнера. Возможно, вам понадобится подкласс контроллера контейнера для изменения значений, возвращаемых его методами supportedInterfaceOrientations и shouldAutorotate.

В Swift вы можете переопределить эти методы с помощью расширений.

Ответ 3

Из библиотеки разработчиков iOS Технические вопросы Q & A

Авторотация реализуется путем применения преобразования поворота к окну приложения, когда система определяет, что интерфейс должен вращаться. Затем окно корректирует свои границы для новой ориентации и распространяет это изменение вниз через иерархию диспетчера представлений посредством вызовов каждому контроллеру представления и контроллеру представления -viewWillTransitionToSize: withTransitionCoordinator: method. Вызов этого метода предоставляется объект-координатор перехода, содержащий дельта окна текущего преобразования и новое преобразование, которое может быть получено путем отправки сообщения -targetTransform координатору перехода. Контроллер вашего представления или контроллер представления могут получить соответствующее обратное преобразование и применить его к целевому представлению. Это аннулирует эффект оконного преобразования на этом конкретном представлении и дает вид, что вид остается фиксированным в текущей ориентации, так как остальная часть интерфейса нормально вращается.

то есть. если у вас есть вид с именем noRotatingView

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    noRotatingView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))
}

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in

        let deltaTransform = coordinator.targetTransform()
        let deltaAngle = atan2f(Float(deltaTransform.b), Float(deltaTransform.a))

        var currentRotation = self.noRotatingView.layer.valueForKeyPath("transform.rotation.z")?.floatValue

        // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
        currentRotation = currentRotation! + (-1 * Float(deltaAngle)) + 0.0001
        self.noRotatingView.layer.setValue(currentRotation, forKeyPath:"transform.rotation.z")

        }) { (UIViewControllerTransitionCoordinatorContext) -> Void in

            var currentTransform = self.noRotatingView.transform;
            currentTransform.a = round(currentTransform.a);
            currentTransform.b = round(currentTransform.b);
            currentTransform.c = round(currentTransform.c);
            currentTransform.d = round(currentTransform.d);
            self.noRotatingView.transform = currentTransform;
    }
}

Я попытался создать демонстрационный проект https://github.com/rishi420/TestCameraRotation