Контроллер дочернего представления будет вращаться, а контроллер родительского вида - нет

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

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

Дочернее представление, которым управляет Дочернее Представление КонтроллерДОЛЖЕН ВРАЩАТЬ для всех направлений.

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

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 установлены на все четыре.

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

Ни один из вида не вращается. Если я установлю вращение родителя для всех, все виды вращаются вместе. Так что все или ничего.

ОБНОВИТЬ

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

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

 Gizmodo06 авг. 2016 г., 16:24
Вы имеете в виду вращаться, используя что-то вроде CGAffineTransform?
 Carter10 авг. 2016 г., 08:54
Должен ли вращающийся ViewController быть дочерним по отношению к невращающемуся ViewController? Если бы это был не ребенок, вы могли бы просто иметь два отдельных контроллера представления, аналогичноэтот ответ
 Andrej06 авг. 2016 г., 16:11
Не уверен, что это сработает, просто примите это за идею. Я определенно настроил бы приложение на поддержку только одной (книжной) ориентации, а затем попытался бы обнаружить поворот устройства (не экрана) и отреагировать на это, вручную повернув вид в середине экрана. ПохожеviewWillTransition(to:with:) не будет работать, поэтому вам нужно найти подходящее решение.
 Gizmodo07 авг. 2016 г., 05:56
Обновлен OP с упоминанием проблемы с этим маршрутом ...
 Gizmodo06 авг. 2016 г., 16:31
Ну, я оставляю метод поворота CGAffineTransform в качестве плана Z. Я удивлен, как приведенный выше код не работает. Он просто не передает 'shouldAutorotate ()' в контроллеры представления.
 jamesk10 авг. 2016 г., 15:10
Обратите внимание, что из-за изменений в базовой реализации авторотации в iOS 8 добавление подпредставлений в окно приложения может перестать быть эффективным методом:developer.apple.com/library/ios/qa/qa1890/_index.html
 Andrej06 авг. 2016 г., 16:28
Да, именно CGAffineTransform. У вас интересная ситуация. Хотел бы помочь и на самом деле попытаться написать код для решения этой проблемы, но я боюсь, что я буду тихо занят, поэтому я не смогу запачкать здесь свои руки.

Ответы на вопрос(3)

Решение Вопроса

После многих взад и вперед с Apple, они сами, и они указывают на одну и ту же ссылкуТехнические вопросы и ответы 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 за всю помощь!

 Lance Samaria21 мар. 2018 г., 22:21
Можете ли вы уточнить этот ответ? что такое rotateChildViewsForOrientation? Там нет объявления функции
 Vaddadi Kartick14 мар. 2017 г., 06:31
Это не работает для меня, как описано вstackoverflow.com/questions/42778355/... Есть идеи почему?

Эффект, достигнутый в приложении «Камера», аналогичен использованию комбинации:

техника, описанная вТехнические вопросы и ответы QA1890 для предотвращения вращения вида; а такжеиспользование дочерних UIStackViews для реорганизации подвидов при вращении устройства (см., например,SimpleExampleViewController класс вAdaptiveElements Пример кода показан вWWDC 2016 Сессия 233).

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

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

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

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

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

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

добавление: Причина, по которой звонкиshouldAutorotate а такжеsupportedInterfaceOrientations не были распространены на дочерние контроллеры представления было объяснено вТехнические вопросы и ответы QA1688:

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

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

 Gizmodo13 авг. 2016 г., 20:55
К сожалению, все еще изо всех сил пытается заставить это работать ....
 Vaddadi Kartick14 мар. 2017 г., 06:31
Это не работает для меня, как описано вstackoverflow.com/questions/42778355/... Есть идеи почему?
 jamesk13 авг. 2016 г., 21:17
Позвольте нам помочь! Что плохого?
 Gizmodo13 авг. 2016 г., 21:27
Ну, ваши ссылки были чрезвычайно полезны. Я знаю, что это достижимо, используя stackView's & Autolayout; Тем не менее, viewWillTransitionToSize: метод, кажется, держит вещи чище для этого конкретного проекта.
 Gizmodo13 авг. 2016 г., 21:31
До сих пор я подошел к сложному методу, используя viewWillTransitionToSize: и UIDevice.currentDevice (). Ориентация с UIDeviceOrientationDidChangeNotification KVO. Используйте viewWillTransitionToSize: чтобы остановить вращение основного вида. используйте ориентацию UIDevice.currentDevice (). с UIDeviceOrientationDidChangeNotification KVO для поворота дочернего представления с использованием CGAffineTransformMakeRotation.
 jamesk10 авг. 2016 г., 23:25
Вот хитрость: не возвращайсяfalse отshouldAutorotate, Используйте метод, показанный в техническом примечании, чтобы предотвратить вращение motherView, которое не будет мешатьviewWillTransitionToSize:- и использовать последний (илиviewWillLayoutSubviews), чтобы повернуть ваши подпредставления соответственно.
 jamesk13 авг. 2016 г., 22:07
Является ли проблема с этим методом той же, что и описанная в OP, - о том, что дочернее представление сбрасывает свое вращение при доступе к Центру уведомлений? Вы переопределяетеshouldAutorotate с этим решением?
 Gizmodo10 авг. 2016 г., 20:50
Отличный ответ! Я все еще читаю ссылки. Как я упоминал ниже, у меня возникают проблемы с использованием viewWillTransitionToSize: withTransitionCoordinator: потому что я хочу, чтобы motherView НЕ вращался, а childView вращался. Если motherView вообще не вращается, viewWillTransitionToSize: withTransitionCoordinator: не будет вызываться внутри него самостоятельно.
 Gizmodo13 авг. 2016 г., 21:27
Возвращаясь к началу: я хочу, чтобы mainView оставался статичным, пока childView вращается. После всего этого, когда приходит уведомление, строка состояния должна быть в правильной ориентации. Что касается iOS Camera App, основной интерфейс остается на месте, значок вспышки вращается. Мне нужно точное поведение. Если бы использовать viewWillTransitionToSize: метод, как я могу использовать его внутри mainViewController, если это представление, которое я пытаюсь остановить вращением? Кроме того, я хочу, чтобы childView вращался.

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

Автоповорот реализуется путем применения преобразования поворота к окну приложения, когда система определяет, что интерфейс должен вращаться. Затем окно корректирует свои границы для новой ориентации и распространяет это изменение вниз по иерархии контроллера представления посредством вызовов к каждому контроллеру представления и контроллеру представления -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

 Warif Akhand Rishi11 авг. 2016 г., 07:28
@gizmodo Я думаю, что взгляд матери вращается в приложении камеры iOS. Потому что центр уведомлений и центр управления вращается. Если вы хотите заблокировать вид матери, вы можете увидетьRotateButton.
 Gizmodo14 авг. 2016 г., 23:57
Комбинация вашей TestCameraRotation + RotateButton имеет лучший смысл в этой точке. 1. Поместите MainView в другое представление и используйте viewWillTransitionToSize, чтобы остановить его вращение. 1. Используйте ориентацию устройства KVO для поворота childView.
 Vaddadi Kartick14 мар. 2017 г., 06:28
Это не работает для меня, как описано вstackoverflow.com/questions/42778355/... Есть идеи почему? @Gizmodo
 Gizmodo10 авг. 2016 г., 20:48
Отличная работа с вашим ответом! Глядя на ваш пример кода, я продолжаю приходить к основной проблеме, которая у меня все еще возникает, даже когда я искал техническую документацию Apple для этого: MotherView не вращается, ChildView вращается. Метод viewWillTransitionToSize не будет вызван, если содержащий представление не вращается вообще. Это где я все еще застрял ...

Ваш ответ на вопрос