Восстановление автоматического поворота после принудительной ориентации в Swift

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

Итак, моя идея состояла в том, чтобы

  1. сохранить начальную ориентацию (сохранить в currentOrientation)
  2. подпишитесь на событие изменения ориентации, чтобы отслеживать изменения ориентации, когда содержимое заблокировано в книжной ориентации (обновление currentOrientation)
  3. при выходе из вида восстановить правильную ориентацию, используя значение currentOrientation.

Изменить (код удален): помимо того, что это не работало, это был опасный путь, поскольку он широко использовал неподдерживаемые API.


Редактировать:

Я считаю, что теперь этот вопрос можно свести к следующему:

  1. Существует ли задокументированный поддерживаемый способ принудительной ориентации интерфейса независимо от ориентации устройства? setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation") много раз рекомендовали на SO и в других местах, но это действительно кажется неподдерживаемым взломом.

  2. Существует ли задокументированный поддерживаемый способ обновить ориентацию интерфейса в соответствии с ориентацией устройства? Это было бы необходимо для «восстановления» после принудительной ориентации интерфейса в другом представлении без необходимости запуска автоматического поворота путем поворота устройства вперед и назад.

Поддерживаются supportedInterfaceOrientations() и shouldAutorotate(). Но они заблокируют интерфейс только после того, как устройство будет повернуто в это положение. Они не предотвращают неправильную начальную ориентацию.

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


person jerry    schedule 02.07.2015    source источник
comment
То, что вы делаете для установки ориентации, недокументировано и не поддерживается. Вероятно, проще просто добавить поддержку ландшафта для этого вида, чем пытаться контролировать повороты.   -  person Aaron Brager    schedule 03.07.2015
comment
В идеале система позаботится о правильной ориентации после выхода из вида с заблокированной ориентацией, но этого не произойдет. Есть ли более простой способ добиться этого? Создание альбомной версии этого вида не вариант.   -  person jerry    schedule 03.07.2015
comment
Вы должны удалить бит setValue(… forKey: "orientation"), потому что это недокументировано и поддерживается. Вы должны быть в состоянии достичь того, чего хотите, используя supportedInterfaceOrientations(), shouldAutorotate() и attemptRotationToDeviceOrientation().   -  person Aaron Brager    schedule 03.07.2015
comment
Просто любопытно (а не спорить) - почему нельзя создать альбомную версию представления? Меня интересует UX, над которым вы работаете.   -  person Aaron Brager    schedule 03.07.2015
comment
Спасибо, Аарон, я еще немного поиграю с этим и попытаюсь придумать более простое решение, используя поддерживаемые интерфейсы. Да, не идущий ландшафт — это дизайнерское решение. Это кроссплатформенное приложение, запущенное в Windows и Android, где это представление является центральной страницей или пейджером соответственно. Он имеет полноэкранное фоновое изображение, и это в значительной степени ядро ​​​​приложения. В горизонтальном виде это выглядело бы не очень хорошо, даже если бы мне удалось переставить все элементы. Полностью перепроектировать это представление, например. используя вкладки, я чувствую, что приложение потеряет свой идентификатор (бренда).   -  person jerry    schedule 03.07.2015
comment
Еще один момент: setValue() используется для принудительного портретного просмотра, когда он открывается, когда устройство находится в альбомной ориентации. supportedInterfaceOrientations() и shouldAutoRotate() не позаботились об этом. Мне нужно было сначала повернуть устройство в портретный режим, а затем вид был заблокирован в портретном режиме. Я нашел несколько ссылок на метод setValue(), что, конечно, не делает его хорошей практикой. Как еще я могу этого добиться?   -  person jerry    schedule 03.07.2015


Ответы (1)


У меня была аналогичная проблема, за исключением того, что мне нужно было, чтобы один контроллер вида работал только в ландшафтном режиме, а другой - в портретном. Я добился этого, создав собственный «корневой» контроллер представления. Затем в методе viewWillTransitionToSize для этого контроллера проверяется ориентация и без анимации нажимается правильный контроллер представления (так что для пользователя это выглядит как вращение). А затем в Interface Builder я устанавливаю свойство ориентации контроллера представления явно, а не выводимое. Вы можете применить это решение, установив только альбомную ориентацию на контроллере с ограниченным просмотром, а затем на портретном вращении, ничего не делая и отключив автоматический поворот на контроллере с ограниченным просмотром.

Обновить

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

Прежде всего убедитесь, что вы настроили ограничения, как показано на скриншоте. У моего iPad полноэкранный и ландшафтный режим, потому что именно этим я и занимался, меняя ваш на все, что вам нужно (можно сделать вывод о портрете и размере).

Конструктор интерфейса View Controller

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

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

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

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

Если что-то по-прежнему не работает, вы можете, наконец, попробовать способ корневого контроллера (но я бы использовал его в последнем случае). Вот его набросок:

class VC : UIViewController { 
      override func viewDidLoad () { 
           UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
           NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name: "UIDeviceOrientationDidChangeNotification", object: nil)
         // this gives you access to notifications about rotations
      }
      func orientationChanged(sender: NSNotification)
      {
              // Here check the orientation using this:
              if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) { // Landscape }
              if UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation) { // Portrait }
           // Now once only allow the portrait one to go in that conditional part of the view. If you're using a navigation controller push the vc otherwise just use presentViewController:animated:
      }
}

Я использовал разные пути для операторов if, чтобы подтолкнуть тот, который я хотел, соответственно, но вы можете просто нажать портрет вручную для обоих, и, надеюсь, один из приведенных выше способов поможет вам.

person Manav Gabhawala    schedule 03.07.2015
comment
Выглядит многообещающе, но мне нужно это переварить. Правильно ли я понимаю, что ваше решение приводит только к альбомной ориентации (которую, конечно, можно применить так же и к портретной ориентации)? Что произойдет, если пользователь перейдет к этому представлению, когда устройство находится в неподдерживаемой ориентации? Застревает ли он на предыдущем виде, пока пользователь не повернет устройство в поддерживаемую ориентацию? - person jerry; 03.07.2015
comment
Нет, если пользователь находится в альбомной ориентации, поскольку вы поддерживаете только портретную, когда пользователь переходит к этому контроллеру, вид будет в портретной ориентации, и поэтому пользователь поймет, что он не может использовать его в альбомной ориентации для этого бита. Это похоже на игры, которые поддерживают только альбомную ориентацию, пользователь понимает, что им нужно повернуться в альбомную, поскольку вид не вращается, но в вашем случае он применяется только к части приложения. - person Manav Gabhawala; 03.07.2015
comment
Не удалось с этим, но я не уверен, что полностью вас понял. Если позволяет время, не могли бы вы вкратце набросать код контроллера корневого представления? - person jerry; 03.07.2015
comment
Спасибо за подробное описание. К сожалению, это не работает. Прежде всего, фактическое представление выталкивается только тогда, когда происходит событие поворота, когда загружается корневой контроллер представления. После этого каждое изменение ориентации вызывает отправку другого экземпляра основного (ограниченного) представления в стек до тех пор, пока корневой контроллер представления не будет извлечен из стека. shouldAutoRotate(), похоже, не имеет никакого эффекта. - person jerry; 03.07.2015
comment
Чтобы решить проблему с контроллером корневого представления, который изначально не нажимал, просто вызовите функцию OrientChanged в viewDidAppear:animated. Кроме того, чтобы предотвратить многократные нажатия, вам нужно сохранить текущую ориентацию, а когда выполнять нажатие только при изменении ориентации, я забыл упомянуть, что вам нужно dismiss контроллер представления или вытолкнуть его в зависимости от того, используете ли вы UINavigationController. Делайте это все без анимации, чтобы пользователь этого не видел. - person Manav Gabhawala; 03.07.2015
comment
Давайте продолжим обсуждение в чате. - person jerry; 03.07.2015