19

То, что я пытаюсь сделать:Child View Controller для поворота Хотя Родитель View Controller Не ли

родительского вида, который управляется Родитель View Controller НЕ ДОЛЖНЫ ПОВОРОТ.

Ребенок Вид, который управляется Child View Controller ДОЛЖЕН ПОВОРОТ для всех ориентаций.


enter image description here

enter image description here


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

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 Информации развертывания установлены на все четыре.

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

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


UPDATE

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

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

+1

Не уверен, что это сработает, возьмите его как идею. Я определенно установил бы, что приложение поддерживает только одну (портретную) ориентацию, а затем я попытаюсь обнаружить вращение устройства (а не экрана) и отреагировать на это, вращая ручку вручную в середине экрана. Я думаю, 'viewWillTransition (to: with:)' не будет работать, поэтому вам нужно найти подходящее решение. – Andrej

+0

Вы имеете в виду вращение с помощью чего-то вроде CGAffineTransform? – Gizmodo

+1

Да, точно CGAffineTransform. У вас интересная ситуация. Хотелось бы помочь и на самом деле попытаться написать какой-то код для решения этой проблемы, но я боюсь, что я буду занят, поэтому я не смогу получить здесь свои грязные руки. – Andrej

ответ

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 Риши за помощь!

+0

Это, похоже, не работает для меня, как описано в http://stackoverflow.com/questions/42778355/why-am-i-seeing-a-glitch-in-the-counter-rotation-animation. Любой идея почему? –

4

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

  • способ, описанный в Technical Q&A QA1890 для предотвращения вид с вращающейся; и
  • использование дочерних UIStackViews для перераспределения подсмотров при вращении устройства (см., например, класс SimpleExampleViewController в примере кода AdaptiveElements, приведенном в WWDC 2016 Session 233).

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

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

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

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

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

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

Добавление: Причина обращения к shouldAutorotate и supportedInterfaceOrientations не распространятся на контроллерах зрения ребенка было объяснено в Technical Q&A QA1688:

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

В Swift вы можете override those methods by using extensions.

+0

Отличный ответ! Я все еще читаю ссылки. Как я уже упоминал ниже, у меня возникают проблемы с использованием viewWillTransitionToSize: withTransitionCoordinator: потому что я хочу, чтобы motherView НЕ вращался, а childView - для вращения. Если motherView вообще не вращается, viewWillTransitionToSize: withTransitionCoordinator: не будет вызван внутри него. – Gizmodo

+0

Вот трюк: не возвращайте 'false' из' shouldAutorotate'.Используйте технику, показанную в технической ноте, чтобы предотвратить поворот MotherView, что не будет мешать «viewWillTransitionToSize:», и использовать последнюю (или «viewWillLayoutSubviews») для соответствующего поворота ваших подзонов. – jamesk

+0

К сожалению, все еще изо всех сил пытается заставить его работать. – Gizmodo

2

Из библиотеки разработчиков IOS Technical Q&A

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

+0

Отличная работа с ответом! Глядя на ваш пример кода, я продолжаю сталкиваться с главным вопросом, который у меня есть, даже когда я смотрел техническую документацию Apple для этого: MotherView не вращается, ChildView вращается. Метод viewWillTransitionToSize не будет вызываться, если содержащее представление вообще не вращается. Это где я все еще застрял ... – Gizmodo

+0

@gizmodo Я думаю, что мама смотрит в приложение камеры iOS. центр уведомлений Coz и центр управления вращаются. Если вы хотите заблокировать вид матери, вы можете увидеть [RotateButton] (https://github.com/rishi420/RotateButton). –

+1

Комбинация вашего TestCameraRotation + RotateButton имеет лучший смысл на этом этапе. 1. Поместите MainView в другой вид и используйте viewWillTransitionToSize, чтобы остановить его вращение. 1. Используйте ориентацию устройства KVO для поворота childView. – Gizmodo