Який "правильний" спосіб вирішити зміни орієнтації в iOS 8?


104

Може хтось скажіть, будь ласка, "правильний" чи "найкращий" підхід до роботи з орієнтацією на портрет та пейзаж в інтерфейсі iOS 8? Здається, що всі функції, які я хочу використовувати для цієї мети, застаріли в iOS 8, і моє дослідження не знайшло чіткої, елегантної альтернативи. Невже я повинен дивитись на ширину та висоту, щоб визначити, чи ми перебуваємо в портретному чи пейзажному режимі?

Наприклад, як на моєму контролері перегляду, як я повинен реалізувати наступний псевдокод?

if we are rotating from portrait to landscape then
  do portrait things
else if we are rotating from landscape to portrait then
  do landscape things

3
Прочитайте документи для UIViewController. Дивіться розділ під назвою "Поводження з обертаннями перегляду". Там пояснюється, що вам потрібно зробити.
rmaddy,

Про те, що вони застарілі, є підказкою. Вам потрібно скористатися чимось іншим .... що-небудь інше повинно бути автоматичним розкладом та класами розміру :-)
Аарон,

Відповіді:


262

Apple рекомендує використовувати класи розмірів як грубую міру того, скільки місця на екрані є, щоб ваш інтерфейс міг значно змінити його макет і зовнішній вигляд. Вважайте, що iPad у портреті має ті ж класи розмірів, що й у ландшафті (Регулярна ширина, Регулярна висота). Це означає, що ваш інтерфейс повинен бути більш-менш схожим між двома орієнтаціями.

Однак перехід від портретного до ландшафтного в iPad є досить значним, що вам може знадобитися внести деякі менші налаштування інтерфейсу користувача, навіть якщо класи розмірів не змінилися. Оскільки методи, пов'язані з орієнтацією на інтерфейс UIViewController, були застарілими, Apple тепер рекомендує застосовувати наступний новий метод UIViewControllerяк заміну:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    // Code here will execute before the rotation begins.
    // Equivalent to placing it in the deprecated method -[willRotateToInterfaceOrientation:duration:]

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Place code here to perform animations during the rotation.
        // You can pass nil or leave this block empty if not necessary.

    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Code here will execute after the rotation has finished.
        // Equivalent to placing it in the deprecated method -[didRotateFromInterfaceOrientation:]

    }];
}

Чудово! Тепер ви отримуєте зворотній зв'язок перед початком обертання та після його завершення. Але як насправді знати, чи обертання - це портрет чи пейзаж?

Apple рекомендує думати про обертання як про просто зміну розміру батьківського подання. Іншими словами, під час обертання iPad від портрета до пейзажу, ви можете вважати це переглядом кореневого рівня, просто змінюючи його bounds.sizeз {768, 1024}на {1024, 768}. Знаючи це , то ви повинні використовувати sizeпередаються в viewWillTransitionToSize:withTransitionCoordinator:метод вище , щоб з'ясувати , є чи ви обертаючись портрет або пейзаж.

Якщо ви хочете зробити ще більш легким способом переміщення застарілого коду до нового способу виконання iOS 8, спробуйте скористатися цією простою категорією на UIView, яка може бути використана для визначення того, чи подання є "портретним" чи "пейзажним" на основі його розмір.

Для резюме:

  1. Ви повинні використовувати класи розмірів, щоб визначити, коли відображати принципово різні інтерфейси користувача (наприклад, користувальницький інтерфейс, подібний до iPhone, порівняно з інтерфейсом, подібним до iPad)
  2. Якщо вам потрібно зробити менші налаштування вашого інтерфейсу, коли класи розмірів не змінюються, але розмір контейнера (батьківського виду) робить, наприклад, коли обертається iPad, використовуйте viewWillTransitionToSize:withTransitionCoordinator:зворотний виклик у UIViewController.
  3. Кожен погляд у вашій програмі повинен приймати рішення лише щодо макета, виходячи з простору, який йому було надано для розміщення. Нехай природна ієрархія поглядів зменшує цю інформацію.
  4. Аналогічно, не використовуйте statusBarOrientation- що в основному є властивістю на рівні пристрою - для визначення, чи планувати подання для "портрет" проти "пейзаж". Орієнтацію на рядок стану слід використовувати лише за допомогою коду, який стосується речей, UIWindowякі насправді живуть на самому кореневому рівні програми.

5
Відмінна відповідь! До речі, ваше відео на YouTube на AL було неймовірно інформативним. Дякую за обмін. Перевір! youtube.com/watch?v=taWaW2GzfCI
smileBot

2
Єдина проблема, яку я знайшов, полягає в тому, що іноді ви хочете знати орієнтацію пристрою, і це не дасть вам знати про це. На iPad він викликається перед зміною значення орієнтації пристрою або пристрою. Ви не можете визначити, чи користувач тримає пристрій у портреті чи портреті догори дном. Це також неможливо перевірити, оскільки глузування UIViewControllerTransitionCoordinator- це кошмар :-(
Даррарски

@Darrarski Ви знайшли елегантне рішення для вирішення цих питань?
Xvolks

так де ж viewdidlayoutsubviewsтоді? Я думав viewdidlayoutsubviews, що використовується для зйомки пов'язаних змін через обертання. Чи можете ви детальніше про це деталізувати?
Мед

1
Як розмежовувати ландшафт ліворуч від краєвиду праворуч, якщо ми не використовуємо барієнтування статусу? Мені потрібна ця відмінність. Крім того, є й інші проблеми, описані тут - stackoverflow.com/questions/53364498/…
Deepak Sharma

17

На основі дуже добре деталізованої (і прийнятої) відповіді смайборга, ось адаптація за допомогою швидкого 3:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: nil, completion: {
        _ in
        self.collectionView.collectionViewLayout.invalidateLayout()
    })        
}

І в UICollectionViewDelegateFlowLayoutреалізації,

public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    // retrieve the updated bounds
    let itemWidth = collectionView.bounds.width
    let itemHeight = collectionView.bounds.height
    // do whatever you need to do to adapt to the new size
}

11

Я просто використовую Центр сповіщень:

Додати змінну орієнтації (пояснимо в кінці)

//Above viewdidload
var orientations:UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation

Додати повідомлення, коли з'явиться перегляд

override func viewDidAppear(animated: Bool) {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name: UIDeviceOrientationDidChangeNotification, object: nil)
}

Видалити сповіщення, коли перегляд відходить

override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIDeviceOrientationDidChangeNotification, object: nil) 
}

Отримує поточну орієнтацію при запуску сповіщення

func orientationChanged (notification: NSNotification) {
    adjustViewsForOrientation(UIApplication.sharedApplication().statusBarOrientation)
}

Перевіряє орієнтацію (портрет / пейзаж) та обробляє події

func adjustViewsForOrientation(orientation: UIInterfaceOrientation) {
    if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown)
    {
        if(orientation != orientations) {
            println("Portrait")
            //Do Rotation stuff here
            orientations = orientation
        }
    }
    else if (orientation == UIInterfaceOrientation.LandscapeLeft || orientation == UIInterfaceOrientation.LandscapeRight)
    {
       if(orientation != orientations) {
            println("Landscape")
            //Do Rotation stuff here
            orientations = orientation
        }
    }
}

Причина, за якою я додаю змінну орієнтації, полягає в тому, що при тестуванні на фізичному пристрої повідомлення про орієнтацію викликається при кожному незначному русі пристрою, а не тільки при його обертанні. Додавання var та if операторів викликає код лише у тому випадку, якщо він перейшов у протилежну орієнтацію.


11
Це не рекомендований Apple підхід, оскільки це означає, що кожен погляд у вашій програмі вирішує, як відображати, залежно від орієнтації пристрою, замість того, щоб враховувати місце, яке йому надано.
усміхборг

2
Apple також не враховує обладнання з 8.0. Скажіть AVPreview шару, що він не повинен турбуватися про орієнтацію, якщо він представлений через контролер перегляду. Не працює, але це зафіксовано в 8.2
Нік Тернер

superвід viewDidAppearта viewWillDisappearмає бути названо
Андрій Богаєвський

Примітка: UIDeviceOrientation! = UIInterfaceOrientation. У моїх експериментах statusBarOrientation не є надійним.
nnrales

2

З точки зору користувальницького інтерфейсу, я вважаю, що використання класів розмірів є рекомендованим підходом Apple для роботи з інтерфейсами різної спрямованості, розміру та масштабу.

Дивіться розділ: Риси Опишіть клас розмірів та масштаб інтерфейсу тут: https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

"iOS 8 додає нові функції, які роблять роботу з розміром та орієнтацією екрана набагато більш універсальною."

Ця хороша стаття також: https://carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

EDIT Оновлено посилання: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/ (Кредит: Koen)


Так, схоже, весь його сайт наразі не працює.
Аарон

Статтю справді варто прочитати, і ось правильна адреса: carpeaqua.com/thinking-in-terms-of-ios-8-size-classes
uem

Але що робити, якщо ми говоримо не про класи розмірів та інтерфейс користувача, а про AVFoundation, наприклад. Під час запису відео в деяких випадках потрібно знати орієнтацію viewControllers, щоб встановити правильні метадані. Це просто неможливо. "універсальний".
Джуліан Ф. Вайнерт

Це не те, про що просили / говорили ОП.
Аарон

1
Оновлене посилання: carpeaqua.com/2014/06/14/…
koen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.