web-dev-qa-db-fra.com

Comment détecter le changement d'orientation?

J'utilise Swift et je veux pouvoir charger un UIViewController lorsque je passe en mode paysage. Quelqu'un peut-il m'indiquer la bonne direction?

Je ne trouve rien en ligne et un peu dérouté par la documentation.

124
David

Voici comment je l'ai fait fonctionner:

Dans AppDelegate.Swift dans la fonction didFinishLaunchingWithOptions, je mets:

NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

puis à l'intérieur de la classe AppDelegate, j'ai mis la fonction suivante: 

func rotated() {
    if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
        print("Landscape")
    }

    if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) {
        print("Portrait")
    }

}

J'espère que cela aide quelqu'un d'autre!

Merci!

177
David
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}
163

Nécessité de détecter une rotation lors de l’utilisation de la caméra avec AVFoundation, et constaté que les méthodes didRotate (now deprecated) & willTransition ne répondaient pas à mes besoins. L'utilisation de la notification publiée par David a fonctionné, mais n'est pas à jour pour Swift 3.x/4.x.

Swift 4.2 Le nom de la notification a été modifié.

La valeur de fermeture reste la même que celle de Swift 4.0:

var didRotate: (Notification) -> Void = { notification in
        switch UIDevice.current.orientation {
        case .landscapeLeft, .landscapeRight:
            print("landscape")
        case .portrait, .portraitUpsideDown:
            print("Portrait")
        default:
            print("other")
        }
    }

Pour configurer la notification pour Swift 4.2:

NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification,
                                       object: nil,
                                       queue: .main,
                                       using: didRotate)

Pour supprimer la notification pour Swift 4.2:

NotificationCenter.default.removeObserver(self,
                                          name: UIDevice.orientationDidChangeNotification,
                                          object: nil)

Concernant la déclaration de dépréciation, mon commentaire initial était trompeur, je voulais donc le mettre à jour. Comme indiqué, l'utilisation de l'inférence @objc a été déconseillée, ce qui était nécessaire pour utiliser un #selector. En utilisant plutôt une fermeture, cela peut être évité et vous disposez maintenant d'une solution qui devrait éviter un blocage en raison de l'appel d'un sélecteur non valide.

Tous les éléments ci-dessous sont obsolètes à partir de XCode 10 et iOS 4.2.

Swift 4.0 Avec Swift 4.0, Apple nous a encouragés à ne pas utiliser le #selector; cette approche utilise donc maintenant un bloc d'achèvement. Cette approche est également rétro-compatible avec Swift 3.x et serait l'approche recommandée pour les années à venir.

Ceci est l'avertissement du compilateur que vous recevrez dans un projet Swift 4.x si vous utilisez la fonction #selector en raison de la désapprobation de l'inférence @objc:

 enter image description here

Entrée dans Swift-évolution sur ce changement .

Configurez le rappel:

// If you do not use the notification var in your callback, 
// you can safely replace it with _
    var didRotate: (Notification) -> Void = { notification in
        switch UIDevice.current.orientation {
        case .landscapeLeft, .landscapeRight:
            print("landscape")
        case .portrait, .portraitUpsideDown:
            print("Portrait")
        default:
            print("other")
        }
    }

Configurez la notification:

NotificationCenter.default.addObserver(forName: .UIDeviceOrientationDidChange,
                                       object: nil,
                                       queue: .main,
                                       using: didRotate)

Détruit-le:

NotificationCenter.default.removeObserver(self, name: .UIDeviceOrientationDidChange, object: nil)
48
CodeBender

L'utilisation de la propriété -orientation de UIDevice n'est pas correcte (même si cela pourrait fonctionner dans la plupart des cas) et pourrait conduire à des bugs, par exemple UIDeviceOrientation considère également l'orientation du périphérique s'il est face visible ou non, UIInterfaceOrientation enum pour ces valeurs.
De plus, si vous verrouillez votre application dans une orientation particulière, UIDevice vous donnera l’orientation de l’appareil sans en tenir compte.
D'un autre côté, iOS8 a déconseillé d'utiliser la propriété interfaceOrientation sur la classe UIViewController
Deux options sont disponibles pour détecter l’orientation de l’interface:

  • Utiliser l'orientation de la barre d'état
  • Utilisez des classes de taille. Sur l’iPhone, si elles ne sont pas remplacées, elles pourraient vous permettre de comprendre l’orientation actuelle de l’interface

Ce qui manque encore, c'est un moyen de comprendre le sens d'un changement d'orientation de l'interface, ce qui est très important lors des animations.
Dans la session de WWDC 2014 "Progression du contrôleur de visualisation dans iOS8", le haut-parleur fournit également une solution à ce problème, à l'aide de la méthode qui remplace -will/DidRotateToInterfaceOrientation

Ici la solution proposée partiellement mise en œuvre, plus d'infos ici

func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        let orientation = orientationFromTransform(coordinator.targetTransform())
        let oldOrientation = UIApplication.sharedApplication().statusBarOrientation
        myWillRotateToInterfaceOrientation(orientation,duration: duration)
        coordinator.animateAlongsideTransition({ (ctx) in
            self.myWillAnimateRotationToInterfaceOrientation(orientation,
            duration:duration)
            }) { (ctx) in
                self.myDidAnimateFromInterfaceOrientation(oldOrientation)
        }
    }
17
Andrea

Facile, cela fonctionne dans iOS8 et 9/Swift 2/Xcode7, il suffit de mettre ce code dans votre viewcontroller.Swift. Il imprimera les dimensions de l'écran à chaque changement d'orientation, vous pouvez mettre votre propre code à la place:

override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
        getScreenSize()
    }
    var screenWidth:CGFloat=0
    var screenHeight:CGFloat=0
    func getScreenSize(){
        screenWidth=UIScreen.mainScreen().bounds.width
        screenHeight=UIScreen.mainScreen().bounds.height
        print("SCREEN RESOLUTION: "+screenWidth.description+" x "+screenHeight.description)
    }
11
Josh

Je sais que cette question est pour Swift, mais puisqu'il s'agit d'un des principaux liens pour une recherche Google et si vous recherchez le même code dans Objective-C:

// add the observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rotated:) name:UIDeviceOrientationDidChangeNotification object:nil];

// remove the observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];

// method signature
- (void)rotated:(NSNotification *)notification {
    // do stuff here
}
9
mikeho
9
Aditya Wirayudha

Swift 3 | Notification UIDeviceOrientationDidChange observée trop souvent

Le code suivant imprime "deviceDidRotate" chaque fois que votre périphérique change d'orientation dans l'espace 3D - indépendamment du passage de l'orientation portrait à l'orientation paysage. Par exemple, si vous tenez votre téléphone en orientation portrait et que vous l'inclinez en avant et en arrière, deviceDidRotate () est appelé à plusieurs reprises.

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(
        self, 
        selector:  #selector(deviceDidRotate), 
        name: .UIDeviceOrientationDidChange, 
        object: nil
    )
}

func deviceDidRotate() {
    print("deviceDidRotate")
}

Pour contourner ce problème, vous pouvez conserver l'orientation précédente du périphérique et rechercher une modification dans deviceDidRotate ().

var previousDeviceOrientation: UIDeviceOrientation = UIDevice.current.orientation

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(
        self, 
        selector:  #selector(deviceDidRotate), 
        name: .UIDeviceOrientationDidChange, 
        object: nil
    )
}

func deviceDidRotate() {
    if UIDevice.current.orientation == previousDeviceOrientation { return }
    previousDeviceOrientation = UIDevice.current.orientation
    print("deviceDidRotate")
}

Vous pouvez également utiliser une notification différente qui n'est appelée que lorsque le périphérique passe de paysage à portrait. Dans ce cas, vous voudriez utiliser la notification UIApplicationDidChangeStatusBarOrientation.

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(
        self, 
        selector:  #selector(deviceDidRotate), 
        name: .UIApplicationDidChangeStatusBarOrientation, 
        object: nil
    )
}

func deviceDidRotate() {
    print("deviceDidRotate")
}
8
Derek Soike

En objectif c

-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator

En rapide

func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

Ignorez cette méthode pour détecter le changement d'orientation.

7
Mahendra GP
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
    //Swift 3
    getScreenSize()
}


func getScreenSize(){
   let screenWidth = UIScreen.main.bounds.width
   let  screenHeight = UIScreen.main.bounds.height
    print("SCREEN RESOLUTION: \(screenWidth.description) x \(screenHeight.description)")
}
5
Jeet Gandhi

Mise en œuvre complète de la procédure de détection du changement d’orientation dans Swift 3.0.

J'ai choisi d'utiliser cette implémentation car les orientations téléphoniques face up et face down étaient importantes pour moi et je souhaitais que la vue ne change que lorsque je savais que l'orientation était dans la position spécifiée. 

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //1
        NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

    }

    deinit {
        //3
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
    }

    func deviceOrientationDidChange() {
        //2
        switch UIDevice.current.orientation {
        case .faceDown:
            print("Face down")
        case .faceUp:
            print("Face up")
        case .unknown:
            print("Unknown")
        case .landscapeLeft:
            print("Landscape left")
        case .landscapeRight:
            print("Landscape right")
        case .portrait:
            print("Portrait")
        case .portraitUpsideDown:
            print("Portrait upside down")
        }
    }

}

Les pièces importantes à noter sont:

  1. Vous écoutez le flux de notification DeviceOrientationDidChange et le liez à la fonction deviceOrientationDidChange.
  2. Vous activez ensuite l'orientation du périphérique, assurez-vous de noter qu'il existe parfois une orientation unknown.
  3. Comme toute notification, avant de désinitialiser le viewController, veillez à ne plus observer le flux de notifications.

J'espère que quelqu'un trouve cela utile.

5
Rob Norback

Voici un moyen simple de détecter l’orientation du périphérique: ( Swift 3 )

override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
            handleViewRotaion(orientation: toInterfaceOrientation)
        }

    //MARK: - Rotation controls
    func handleViewRotaion(orientation:UIInterfaceOrientation) -> Void {
        switch orientation {
        case .portrait :
            print("portrait view")
            break
        case .portraitUpsideDown :
            print("portraitUpsideDown view")
            break
        case .landscapeLeft :
            print("landscapeLeft view")
            break
        case .landscapeRight :
            print("landscapeRight view")
            break
        case .unknown :
            break
        }
    }
4
Ram Madhavan

Vérifiez si la rotation a été modifiée avec: viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

Avec coordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext), vous pouvez vérifier si la transition est terminée.

Voir le code ci-dessous:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    coordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext) in
        // if you want to execute code after transition finished
        print("Transition finished")
    }

    if size.height < size.width {
        // Landscape
        print("Landscape")
    } else {
        // Portrait
        print("Portrait")
    }

}
4
SDW

J'aime vérifier la notification d'orientation car vous pouvez ajouter cette fonctionnalité à n'importe quelle classe. Il n'est pas nécessaire que ce soit une vue ou un contrôleur de vue. Même dans votre délégué de l'application.

    //ask the system to start notifying when interface change
    UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
    //add the observer
    NSNotificationCenter.defaultCenter().addObserver(
        self,
        selector: #selector(self.orientationChanged(_:)),
        name: UIDeviceOrientationDidChangeNotification,
        object: nil)

que de mettre en cache la notification

    func orientationChanged(notification : NSNotification) {
        //your code there
    }
3
Alberto Scampini

Si vous voulez faire quelque chose APRÈS que la rotation soit terminée, vous pouvez utiliser le gestionnaire d'achèvement UIViewControllerTransitionCoordinator comme ceci

public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    // Hook in to the rotation animation completion handler
    coordinator.animate(alongsideTransition: nil) { (_) in
        // Updates to your UI...
        self.tableView.reloadData()
    }
}
3
David Pettigrew

Pour Swift 3

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.current.orientation.isLandscape {
        //Landscape
    }
    else {
        //Portrait
    }
}
2
Mamta

Mon approche est similaire à ce que bpedit montre ci-dessus, mais avec un focus iOS 9+. Je voulais changer le champ d'application de FSCalendar lorsque la vue pivote.

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    coordinator.animateAlongsideTransition({ (context) in
        if size.height < size.width {
            self.calendar.setScope(.Week, animated: true)
            self.calendar.appearance.cellShape = .Rectangle
        }
        else {
            self.calendar.appearance.cellShape = .Circle
            self.calendar.setScope(.Month, animated: true)

        }

        }, completion: nil)
}

Ce ci-dessous a fonctionné, mais je me suis senti penaud à ce sujet :)

coordinator.animateAlongsideTransition({ (context) in
        if size.height < size.width {
            self.calendar.scope = .Week
            self.calendar.appearance.cellShape = .Rectangle
        }
        }) { (context) in
            if size.height > size.width {
                self.calendar.scope = .Month
                self.calendar.appearance.cellShape = .Circle
            }
    }
2
Curtis Forrester

J'utilise UIUserInterfaceSizeClass pour détecter une orientation modifiée dans une classe UIViewController comme ceci:

override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {

    let isiPadLandscapePortrait = newCollection.horizontalSizeClass == .regular && newCollection.verticalSizeClass == .regular
    let isiPhonePlustLandscape = newCollection.horizontalSizeClass == .regular && newCollection.verticalSizeClass == .compact
    let isiPhonePortrait = newCollection.horizontalSizeClass == .compact && newCollection.verticalSizeClass == .regular
    let isiPhoneLandscape = newCollection.horizontalSizeClass == .compact && newCollection.verticalSizeClass == .compact

     if isiPhonePortrait {
         // do something...
     }
}
2
pableiros

Swift 4:

override func viewWillAppear(_ animated: Bool) {
    NotificationCenter.default.addObserver(self, selector: #selector(deviceRotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
}

@objc func deviceRotated(){
    if UIDevice.current.orientation.isLandscape {
        //Code here
    } else {
        //Code here
    }
}

Beaucoup de réponses ne permettent pas de détecter plusieurs contrôleurs de vue. Celui-ci fait le tour.

2
Joel

Depuis iOS 8, c’est la bonne façon de le faire.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    coordinator.animate(alongsideTransition: { context in
        // This is called during the animation
    }, completion: { context in
        // This is called after the rotation is finished. Equal to deprecated `didRotate`
    })
}
2
PatrickDotStar
- (void)viewDidLoad {
  [super viewDidLoad];
  [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(OrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

-(void)OrientationDidChange:(NSNotification*)notification {
  UIDeviceOrientation Orientation=[[UIDevice currentDevice]orientation];

  if(Orientation==UIDeviceOrientationLandscapeLeft || Orientation==UIDeviceOrientationLandscapeRight) {
    NSLog(@"Landscape");
  } else if(Orientation==UIDeviceOrientationPortrait) {
    NSLog(@"Potrait Mode");
  }
}

NOTE: Utilisez juste ce code pour identifier UIViewController dans quelle orientation

0
Mannam Brahmam