¿Cuál es la forma "correcta" de manejar los cambios de orientación en iOS 8?

104

¿Puede alguien decirme cuál es el enfoque "correcto" o el "mejor" para trabajar con orientaciones de interfaz vertical y horizontal en iOS 8? Parece que todas las funciones que quiero usar para ese propósito están obsoletas en iOS 8, y mi investigación no ha encontrado una alternativa clara y elegante. ¿De verdad se supone que debo mirar el ancho y la altura para determinar por mí mismo si estamos en modo retrato o paisaje?

Por ejemplo, en mi controlador de vista, ¿cómo debo implementar el siguiente pseudocódigo?

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
rmp251
fuente
3
Lea los documentos para UIViewController. Consulte la sección titulada "Manejo de rotaciones de vista". Explica lo que debe hacer.
rmaddy
Que estén en desuso es una pista. Tienes que usar algo más ... que algo más debería ser AutoLayout y Clases de tamaño :-)
Aaron

Respuestas:

262

Apple recomienda usar clases de tamaño como una medida aproximada de cuánto espacio de pantalla está disponible, para que su interfaz de usuario pueda cambiar significativamente su diseño y apariencia. Tenga en cuenta que un iPad en vertical tiene las mismas clases de tamaño que en horizontal (ancho normal, alto normal). Esto significa que su interfaz de usuario debería ser más o menos similar entre las dos orientaciones.

Sin embargo, el cambio de retrato a paisaje en un iPad es lo suficientemente significativo como para que deba realizar algunos ajustes más pequeños en la interfaz de usuario, aunque las clases de tamaño no hayan cambiado. Dado que los métodos relacionados con la orientación de la interfaz UIViewControllerhan quedado obsoletos, Apple ahora recomienda implementar el siguiente método nuevo UIViewControllercomo reemplazo:

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

    }];
}

¡Excelente! Ahora está recibiendo devoluciones de llamada justo antes de que comience la rotación y después de que finalice. Pero, ¿qué hay de saber realmente si la rotación es vertical o horizontal?

Apple recomienda pensar en la rotación como simplemente un cambio en el tamaño de la vista principal. En otras palabras, durante una rotación del iPad de vertical a horizontal, puede pensar en ella como la vista de nivel de raíz simplemente cambiando bounds.sizede {768, 1024}a {1024, 768}. Sabiendo esto, entonces, se debe utilizar el sizepasado al viewWillTransitionToSize:withTransitionCoordinator:método anterior para averiguar si se está girando a vertical u horizontal.

Si desea una forma aún más fluida de migrar el código heredado a la nueva forma de hacer las cosas de iOS 8, considere usar esta categoría simple en UIView, que se puede usar para determinar si una vista es "vertical" o "horizontal" según su Talla.

Recordar:

  1. Debes usar clases de tamaño para determinar cuándo mostrar interfaces de usuario fundamentalmente diferentes (por ejemplo, una interfaz de usuario "similar a un iPhone" frente a una interfaz de usuario "similar a un iPad")
  2. Si necesita realizar ajustes más pequeños en su interfaz de usuario cuando las clases de tamaño no cambian pero el tamaño de su contenedor (vista principal) sí, como cuando un iPad gira, use la viewWillTransitionToSize:withTransitionCoordinator:devolución de llamada en UIViewController.
  3. Cada vista en su aplicación solo debe tomar decisiones de diseño basadas en el espacio que se le ha dado para el diseño. Deje que la jerarquía natural de vistas haga descender esta información en cascada.
  4. Del mismo modo, no utilice statusBarOrientation, que es básicamente una propiedad a nivel de dispositivo, para determinar si se debe diseñar una vista para "retrato" o "paisaje". La orientación de la barra de estado solo debe ser utilizada por el código que se ocupa de cosas como las UIWindowque realmente viven en el nivel raíz de la aplicación.
smileyborg
fuente
5
¡Excelente respuesta! Por cierto, su video de YouTube en AL fue increíblemente informativo. Gracias por compartir. ¡Echale un vistazo! youtube.com/watch?v=taWaW2GzfCI
smileBot
2
El único problema que encontré es que a veces quieres saber la orientación del dispositivo, y esto no te lo hará saber. En iPad, se llama antes de que cambie la barra de estado o el valor de orientación del dispositivo. No se puede saber si el usuario sostiene el dispositivo en vertical o en vertical al revés. También es imposible de probar, ya que burlarse UIViewControllerTransitionCoordinatores una pesadilla :-(
Darrarski
@Darrarski, ¿ha encontrado una solución elegante para hacer frente a esos problemas?
Xvolks
entonces, ¿dónde está viewdidlayoutsubviewsentonces? Pensé que viewdidlayoutsubviewsse usa para capturar cambios vinculados debido a la rotación. ¿Puedes dar más detalles sobre eso?
Honey
1
¿Cómo diferenciar entre el paisaje a la izquierda y el paisaje a la derecha si no usamos la orientación de la barra de estado? Necesito esa distinción. Además, hay otros problemas como se describe aquí - stackoverflow.com/questions/53364498/…
Deepak Sharma
17

Basado en la respuesta muy bien detallada (y aceptada) de smileyborg, aquí hay una adaptación usando 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()
    })        
}

Y en la UICollectionViewDelegateFlowLayoutimplementación,

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
}
CMont
fuente
11

Simplemente uso el Centro de notificaciones:

Agregue una variable de orientación (se explicará al final)

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

Agregar notificación cuando aparezca la vista

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

Eliminar notificación cuando la vista desaparece

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

Obtiene la orientación actual cuando se activa la notificación

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

Comprueba la orientación (retrato / paisaje) y gestiona los eventos.

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
        }
    }
}

La razón por la que agrego una variable de orientación es porque al probar en un dispositivo físico, la notificación de orientación se llama en cada movimiento menor en el dispositivo, y no solo cuando gira. Agregar las declaraciones var y if solo llama al código si cambia a la orientación opuesta.

inVINCEable
fuente
11
Este no es el enfoque recomendado por Apple porque significa que cada vista en su aplicación está decidiendo cómo mostrar en función de la orientación del dispositivo, en lugar de considerar el espacio que se le ha asignado.
smileyborg
2
Apple tampoco está tomando en consideración el hardware con 8.0. Dígale a una capa de AVPreview que no necesita preocuparse por la orientación si se presenta sobre un controlador de vista. No funciona, pero se corrigió en 8.2
Nick Turner
superde viewDidAppeary viewWillDisappeardebería llamarse
Andrew Bogaevskyi
Nota: UIDeviceOrientation! = UIInterfaceOrientation. En mis experimentos, statusBarOrientation no es confiable.
nnrales
2

Desde la perspectiva de la interfaz de usuario, creo que el uso de clases de tamaño es el enfoque recomendado por Apple para manejar interfaces en diferentes orientaciones, tamaños y escalas.

Consulte la sección: Los rasgos describen la clase de tamaño y la escala de una interfaz aquí: https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

"iOS 8 agrega nuevas características que hacen que lidiar con el tamaño y la orientación de la pantalla sea mucho más versátil".

Este también es un buen artículo: https://carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

EDITAR Enlace actualizado: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/ (Crédito: Koen)

Aaron
fuente
Sí, parece que todo su sitio está caído en este momento.
Aaron
De hecho, vale la pena leer el artículo y aquí está la dirección correcta: carpeaqua.com/thinking-in-terms-of-ios-8-size-classes
uem
Pero, ¿qué pasa si no estamos hablando de clases de tamaño y UI, sino de AVFoundation, por ejemplo? Al grabar un video, en algunos casos debe conocer la orientación de viewControllers para configurar los metadatos correctos. Esto es simplemente imposible. "versátil".
Julian F. Weinert
Eso no es de lo que el OP estaba preguntando / hablando.
Aaron
1
Enlace actualizado: carpeaqua.com/2014/06/14/…
koen