Каков "правильный" способ обработки изменений ориентации в iOS 8?


может ли кто-нибудь сказать мне "правильный" или "лучший" подход к работе с портретной и альбомной ориентацией интерфейса в 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
4 94

4 ответа:

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:]

    }];
}

великолепно! Теперь вы получаете обратные вызовы прямо перед началом вращения и после его завершения. Но как насчет того, чтобы на самом деле знать, является ли вращение портретом или пейзажем?

Яблоко рекомендует думать о вращении как о простом изменении размера родительского представления. Другими словами, во время поворота iPad от портрета к ландшафту вы можете думать об этом как о представлении корневого уровня, просто меняя его bounds.size С {768, 1024} до {1024, 768}. Зная это, вы должны использовать size перешел в viewWillTransitionToSize:withTransitionCoordinator: метод выше, чтобы выяснить, вращается ли вы к портретной или альбомной ориентации.

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

подведем итог:

  1. вы должны использовать классы размера, чтобы определить, когда показывать принципиально разные UI (например, "iPhone-подобный" UI против "iPad-подобного" UI)
  2. Если вам нужно внести небольшие изменения в свой пользовательский интерфейс, когда размер классов не изменить, но ваш контейнер (Родительский вид) размер делает, например, когда iPad вращается, используйте viewWillTransitionToSize:withTransitionCoordinator: обратный вызов в UIViewController.
  3. каждый вид в вашем приложении должен принимать решения о макете только на основе пространства, в котором он был предоставлен макету. Пусть естественная иерархия представлений каскадирует эту информацию вниз.
  4. точно так же, не используйте statusBarOrientation -- который в основном является свойством уровня устройства -- чтобы определить, нужно ли компоновать представление для "портрета" против "ландшафта". Строка состояния ориентация должна использоваться только кодом, имеющим дело с такими вещами, как UIWindow которые на самом деле живут на самом корневом уровне приложения.

на основе очень хорошо детализированного (и принятого) ответа smileyborg, вот адаптация с использованием swift 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
}

Я просто использую Центр уведомлений:

добавить переменную ориентации (объясню в конце)

//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 вызывает код только в том случае, если он переключился на противоположную ориентацию.

с точки зрения пользовательского интерфейса, я считаю, что использование классов размеров является рекомендуемым подходом 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)