web-dev-qa-db-fra.com

Le contrôleur de vue enfant doit pivoter, contrairement au contrôleur de vue parent

Ce que j'essaie de faire:

Vue parente gérée par le contrôleur de vue par parent NE DEVRAIT PAS ROTER.

Vue enfant gérée par le contrôleur de vue enfant DEVRAIT TOURNER à toutes les orientations.


 enter image description here

 enter image description here


Ce que j'ai essayé:

ParentViewController

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return .Portrait
}

override func shouldAutorotate() -> Bool {
    return true
}

fun addChildViewController() {
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    self.childViewController = storyBoard("Child View Controller") as? ChildViewController
    self .addChildViewController(self.childViewController!)
    self.childViewController! .didMoveToParentViewController(self)
    self.view .addSubview(self.childViewController!.view)
    self.childViewController!.view.frame = CGRectMake (40, 200, 400, 250)
}

ChildViewController

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return  .All
}

override func shouldAutorotate() -> Bool {
    return true
}

Les orientations prises en charge dans les informations de déploiement Xcode sont définies sur les quatre.

Ce que j'obtiens:

Aucune des vues ne tourne. Si je règle la rotation du parent sur tout, toutes les vues pivotent ensemble. Donc c'est tout ou rien.


METTRE À JOUR

Lorsque j'essaie de placer un observateur pour UIDeviceOrientationDidChangeNotification et d'utiliser UIDevice.currentDevice (). Orientation pour faire pivoter childView à l'aide de CGAffaineTransformMakeRotate, j'obtiens les résultats souhaités. CEPENDANT, c’est-à-dire si je passe en mode paysage et que je tente d’abaisser le centre de notification (ou si j’obtiens une notification système), qui est toujours en orientation portrait (car l’application est toujours laissée en mode Portrait dans Portrait), pivoté childView revient à portrait pour honorer la barre d'état/notification/centre de notification.

Application pour appareil photo iOS est le meilleur exemple que je puisse présenter. Le canevas principal ne pivote pas selon différentes orientations, mais la barre d'état, en coulisse, pivote pour tenir compte de la rotation du périphérique. De plus, les sous-vues tournent en elles-mêmes pour tenir compte de différentes orientations. J'essaie d'obtenir ce comportement ....

23
Gizmodo

Après de nombreux échanges avec Apple eux-mêmes, et les liens pointant vers le même lien Questions-réponses technique QA1890 , je devais le faire avec la méthode fluide:

MotherViewController

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

        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

        coordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
            let deltaTransform: CGAffineTransform = coordinator.targetTransform()
            let deltaAngle: CGFloat = atan2(deltaTransform.b, deltaTransform.a)
            var currentRotation: CGFloat = self.mainView.layer.valueForKeyPath("transform.rotation.z") as! CGFloat

            // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
            currentRotation += -1 * deltaAngle + 0.0001
            self.mainView.layer.setValue(currentRotation, forKeyPath: "transform.rotation.z")

            }, completion: {(
                context: UIViewControllerTransitionCoordinatorContext) -> Void in
                // Integralize the transform to undo the extra 0.0001 added to the rotation angle.
                var currentTransform: CGAffineTransform = self.mainView.transform
                currentTransform.a = round(currentTransform.a)
                currentTransform.b = round(currentTransform.b)
                currentTransform.c = round(currentTransform.c)
                currentTransform.d = round(currentTransform.d)
                self.mainView.transform = currentTransform
        })
    }

MainViewController

NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name:UIDeviceOrientationDidChangeNotification, object: nil)

func orientationChanged ( notification: NSNotification){
        switch UIDevice.currentDevice().orientation {
        case .Portrait:
            self.rotateChildViewsForOrientation(0)
        case .PortraitUpsideDown:
            self.rotateChildViewsForOrientation(0)
        case .LandscapeLeft:
            self.rotateChildViewsForOrientation(90)
        case .LandscapeRight:
            self.rotateChildViewsForOrientation(-90)
        default:
            print ("Default")
                }
    }

Cela semble faire pivoter la vue enfant tout en maintenant la vue principale statique. De plus, l'application semble connaître la bonne orientation lorsque les notifications arrivent (car la vue mère tourne en arrière-plan).

Un merci spécial à jamesk et Warif Akhand Rishi pour toute l'aide!

2
Gizmodo

L'effet obtenu dans l'application Appareil photo est similaire à l'utilisation d'une combinaison de:

La note technique explique que l'implémentation sous-jacente de l'autorotation implique l'application d'une transformation directement à la fenêtre de l'application:

L'autorotation est implémentée en appliquant une transformation de rotation à la fenêtre de l'application lorsque le système détermine que l'interface doit pivoter. La fenêtre ajuste ensuite ses limites pour la nouvelle orientation et propage cette modification dans la hiérarchie du contrôleur de vue via des appels à la méthode viewWillTransitionToSize:withTransitionCoordinator: de chaque contrôleur de vue et de chaque contrôleur de présentation.

Notez que l'application Appareil photo n'autorise pas à désactiver l'auto-rotation: lors de l'utilisation de l'application Appareil photo, les orientations de Notification Center et Control Center reflètent l'orientation du périphérique. Seules les vues particulières de l'application Appareil photo qui semblent ne pas être autorisées à l'autorotation. En fait, cette réponse suggère que ces applications utilisent généralement les propriétés videoGravity et videoOrientation fournies par les classes AVFoundation telles que AVCaptureVideoPreviewLayer et AVCaptureConnection .

L’approche à adopter dépend de la décision d’arrêter uniquement la rotation d’une vue parent ou d’arrêter la rotation d’un contrôleur de vue conteneur tel que UINavigationController (autrement dit, l’orientation du verrouillage).

Dans ce cas, utilisez la technique décrite dans la note technique pour définir l’orientation de la vue parent et (pour iOS 9) encapsulez les sous-vues en rotation dans un UIStackView et utilisez la propriété axis pour réorganiser les sous-vues lors de la rotation du périphérique, ou (pour iOS 8) faire pivoter manuellement les vues secondaires sur la rotation des appareils (pour savoir qui voit cette réponse et cet exemple de code ).

Dans ce dernier cas, l'approche que vous prenez est sur la bonne voie. Pour un exemple de code, voir cette réponse . Vous devrez partager votre code et davantage d’informations sur la hiérarchie de votre contrôleur de vue pour que nous puissions diagnostiquer les anomalies impliquant Notification Center.

Addendum: La raison pour laquelle les appels à shouldAutorotate et supportedInterfaceOrientations n'ont pas été propagés aux contrôleurs de vue enfant a été expliquée dans Questions techniques (QA1688) :

À compter de iOS 6, seul le contrôleur de vue le plus élevé (à côté de l'objet UIApplication) participe à la décision de faire une rotation en réponse à un changement d'orientation du périphérique. Le plus souvent, le contrôleur de vue affichant votre contenu est un enfant de UINavigationController, UITabBarController ou un contrôleur de vue de conteneur personnalisé. Vous devrez peut-être sous-classer un contrôleur de vue de conteneur afin de modifier les valeurs renvoyées par ses méthodes supportedInterfaceOrientations et shouldAutorotate.

Dans Swift, vous pouvez remplacer ces méthodes en utilisant des extensions .

7
jamesk

Depuis la bibliothèque de développeurs iOS Questions/Réponses techniques

L'autorotation est implémentée en appliquant une transformation de rotation à la fenêtre de l'application lorsque le système détermine que l'interface doit pivoter. La fenêtre ajuste ensuite ses limites pour la nouvelle orientation et propage cette modification dans la hiérarchie du contrôleur de vue via des appels à la méthode -viewWillTransitionToSize: withTransitionCoordinator: de chaque contrôleur de présentation et de présentation. Les invocations de cette méthode fournissent un objet coordinateur de transition contenant le delta de la transformation actuelle et de la nouvelle transformation de la fenêtre, qui peut être récupéré en envoyant un message -targetTransform au coordinateur de transition. Votre contrôleur de vue ou de présentation peut dériver la transformation inverse appropriée et l'appliquer à la vue cible. Cela annule l'effet de la transformation de fenêtre sur cette vue particulière et donne l'impression que la vue est restée fixe dans son orientation actuelle, le reste de l'interface tournant normalement.

c'est-à-dire si vous avez une vue nommée noRotatingView

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    noRotatingView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))
}

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

    coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in

        let deltaTransform = coordinator.targetTransform()
        let deltaAngle = atan2f(Float(deltaTransform.b), Float(deltaTransform.a))

        var currentRotation = self.noRotatingView.layer.valueForKeyPath("transform.rotation.z")?.floatValue

        // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
        currentRotation = currentRotation! + (-1 * Float(deltaAngle)) + 0.0001
        self.noRotatingView.layer.setValue(currentRotation, forKeyPath:"transform.rotation.z")

        }) { (UIViewControllerTransitionCoordinatorContext) -> Void in

            var currentTransform = self.noRotatingView.transform;
            currentTransform.a = round(currentTransform.a);
            currentTransform.b = round(currentTransform.b);
            currentTransform.c = round(currentTransform.c);
            currentTransform.d = round(currentTransform.d);
            self.noRotatingView.transform = currentTransform;
    }
}

J'ai essayé de faire un projet de démonstration sur https://github.com/rishi420/TestCameraRotation

2
Warif Akhand Rishi