Quelle est la «bonne» façon de gérer les changements d'orientation dans iOS 8?

104

Quelqu'un peut-il me dire la «bonne» ou «meilleure» approche pour travailler avec les orientations d'interface portrait et paysage dans iOS 8? Il semble que toutes les fonctions que je souhaite utiliser à cette fin soient obsolètes dans iOS 8, et mes recherches n'ont révélé aucune alternative claire et élégante. Suis-je vraiment censé regarder la largeur et la hauteur pour déterminer moi-même si nous sommes en mode portrait ou paysage?

Par exemple, dans mon contrôleur de vue, comment dois-je implémenter le pseudocode suivant?

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
la source
3
Lisez la documentation pour UIViewController. Consultez la section intitulée «Gestion des rotations de vues». Elle explique ce que vous devez faire.
rmaddy
Le fait qu'ils soient obsolètes est un indice. Vous devez utiliser autre chose ... que quelque chose d'autre devrait être AutoLayout et Size Classes :-)
Aaron

Réponses:

262

Apple recommande d'utiliser des classes de taille comme une mesure grossière de l'espace disponible sur l'écran, afin que votre interface utilisateur puisse modifier considérablement sa disposition et son apparence. Considérez qu'un iPad en mode portrait a les mêmes classes de taille qu'en mode paysage (largeur régulière, hauteur régulière). Cela signifie que votre interface utilisateur doit être plus ou moins similaire entre les deux orientations.

Cependant, le passage du portrait au paysage dans un iPad est suffisamment important pour que vous deviez peut-être apporter quelques ajustements plus petits à l'interface utilisateur, même si les classes de taille n'ont pas changé. Étant donné que les méthodes liées à l'orientation de l'interface sur UIViewControllersont obsolètes, Apple recommande désormais de mettre en œuvre la nouvelle méthode suivante en UIViewControllerremplacement:

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

    }];
}

Génial! Vous recevez désormais des rappels juste avant le début de la rotation et après la fin. Mais qu'en est-il de savoir si la rotation est en portrait ou en paysage?

Apple recommande de considérer la rotation comme un simple changement de taille de la vue parent. En d'autres termes, lors d'une rotation de l'iPad du portrait au paysage, vous pouvez le considérer comme une vue au niveau de la racine qui change simplement son bounds.sizede {768, 1024}à {1024, 768}. Sachant cela, vous devez utiliser le sizepassé dans la viewWillTransitionToSize:withTransitionCoordinator:méthode ci-dessus pour déterminer si vous effectuez une rotation en mode portrait ou paysage.

Si vous voulez un moyen encore plus transparent de migrer le code hérité vers la nouvelle façon de faire iOS 8, envisagez d'utiliser cette catégorie simple sur UIView, qui peut être utilisée pour déterminer si une vue est "portrait" ou "paysage" en fonction de son Taille.

Récapituler:

  1. Vous devez utiliser des classes de taille pour déterminer quand afficher des interfaces utilisateur fondamentalement différentes (par exemple, une interface utilisateur "de type iPhone" ou une interface "de type iPad")
  2. Si vous devez apporter de plus petits ajustements à votre interface utilisateur lorsque les classes de taille ne changent pas mais que la taille de votre conteneur (vue parent) le fait, par exemple lorsqu'un iPad pivote, utilisez le viewWillTransitionToSize:withTransitionCoordinator:rappel dans UIViewController.
  3. Chaque vue de votre application ne doit prendre des décisions de mise en page qu'en fonction de l'espace dans lequel elle a été mise en page. Laissez la hiérarchie naturelle des vues en cascade ces informations.
  4. De même, n'utilisez pas statusBarOrientation- qui est essentiellement une propriété au niveau de l'appareil - pour déterminer s'il faut mettre en page une vue pour «portrait» ou «paysage». L'orientation de la barre d'état ne doit être utilisée que par le code traitant de choses comme celles UIWindowqui vivent réellement au niveau racine de l'application.
smileyborg
la source
5
Excellente réponse! BTW, votre vidéo YouTube sur AL était incroyablement informative. Merci pour le partage. Vérifiez-le! youtube.com/watch?v=taWaW2GzfCI
smileBot
2
Le seul problème que j'ai trouvé est que parfois vous voulez connaître l'orientation de l'appareil, et cela ne vous le fera pas savoir. Sur iPad, il est appelé avant que la barre d'état ou la valeur d'orientation de l'appareil ne change. Vous ne pouvez pas dire si l'utilisateur tient l'appareil en portrait ou en portrait à l'envers. Il est également impossible de tester, car se moquer UIViewControllerTransitionCoordinatorest un cauchemar :-(
Darrarski
@Darrarski avez-vous trouvé une solution élégante pour faire face à ces problèmes?
Xvolks
alors où est viewdidlayoutsubviewsalors? Je pensais que c'était viewdidlayoutsubviewsutilisé pour capturer les changements liés dus à la rotation. Pouvez-vous nous en dire plus?
Honey
1
Comment faire la différence entre paysage gauche et paysage droit si on n'utilise pas d'orientation de la barre d'état? J'ai besoin de cette distinction. En outre, il y a d'autres problèmes comme décrit ici - stackoverflow.com/questions/53364498/…
Deepak Sharma
17

Basé sur la réponse très bien détaillée (et acceptée) de smileyborg, voici une adaptation utilisant 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()
    })        
}

Et dans la UICollectionViewDelegateFlowLayoutmise en œuvre,

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
la source
11

J'utilise simplement le centre de notification:

Ajouter une variable d'orientation (expliquera à la fin)

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

Ajouter une notification lorsque la vue apparaît

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

Supprimer la notification lorsque la vue disparaît

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

Obtient l'orientation actuelle lorsque la notification est déclenchée

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

Vérifie l'orientation (portrait / paysage) et gère les événements

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 raison pour laquelle j'ajoute une variable d'orientation est que lors du test sur un appareil physique, la notification d'orientation est appelée à chaque mouvement mineur dans l'appareil, et pas seulement lors de sa rotation. L'ajout des instructions var et if n'appelle le code que s'il est passé à l'orientation opposée.

inVINCEable
la source
11
Ce n'est pas l'approche recommandée par Apple, car cela signifie que chaque vue de votre application décide du mode d'affichage en fonction de l'orientation de l'appareil, au lieu de tenir compte de l'espace qui lui a été accordé.
smileyborg
2
Apple ne prend pas non plus en compte le matériel avec la version 8.0. Dites à une couche AVPreview qu'elle n'a pas besoin de se soucier de l'orientation si elle est présentée sur un contrôleur de vue. Ne fonctionne pas, mais il est corrigé dans la version 8.2
Nick Turner
superde viewDidAppearet viewWillDisappeardevrait être appelé
Andrew Bogaevskyi
Remarque: UIDeviceOrientation! = UIInterfaceOrientation. Dans mes expériences, statusBarOrientation n'est pas fiable.
nnrales
2

Du point de vue de l'interface utilisateur, je pense que l'utilisation des classes de taille est l'approche recommandée par Apple pour gérer les interfaces dans différentes orientations, tailles et échelles.

Voir la section: Les traits décrivent la classe de taille et l'échelle d'une interface ici: https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html

"iOS 8 ajoute de nouvelles fonctionnalités qui rendent la gestion de la taille et de l'orientation de l'écran beaucoup plus polyvalente."

Celui-ci est également un bon article: https://carpeaqua.com/thinking-in-terms-of-ios-8-size-classes/

EDIT Mise à jour du lien: https://carpeaqua.com/2014/06/14/thinking-in-terms-of-ios-8-size-classes/ (Crédit: Koen)

Aaron
la source
Ouais, on dirait que tout son site est en panne pour le moment.
Aaron
L'article vaut en effet la peine d'être lu et voici la bonne adresse: carpeaqua.com/thinking-in-terms-of-ios-8-size-classes
uem
Mais que se passe-t-il si nous ne parlons pas de classes de taille et d'interface utilisateur, mais d'AVFoundation, par exemple. Lors de l'enregistrement vidéo, dans certains cas, vous devez connaître l'orientation de viewControllers pour définir les métadonnées correctes. C'est tout simplement impossible. "polyvalent".
Julian F. Weinert
Ce n'est pas ce que le PO demandait / parlait.
Aaron
1
Lien mis à jour: carpeaqua.com/2014/06/14/…
koen