Question Contrôleur enfant vue à faire pivoter alors que le contrôleur de la vue parent ne le fait pas


Ce que je tente de faire:

Vue parent gérée par Parent View Controller NE DEVRAIT PAS TOURNER.

Vue enfant gérée par Child View Controller 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 je reçois:

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


METTRE À JOUR

Quand j'essaye de mettre un observateur pour UIDeviceOrientationDidChangeNotification et utilisez UIDevice.currentDevice (). orientation pour faire pivoter childView en utilisant CGAffaineTransformMakeRotate, j'obtiens les résultats souhaités. Toutefois, si je fais une rotation vers le paysage et que j'essaie de supprimer le centre de notification (ou si je reçois une notification système), qui est toujours en orientation portrait (parce que l'application est encore en mode natif dans Portrait), pivotée childView retourne en mode portrait pour respecter la barre d'état / le centre de notification / notification.

App Camera App iOS est le meilleur exemple que je puisse présenter. Le canevas principal ne tourne pas dans différentes orientations, mais la barre d'état, en arrière-plan, fait pivoter la rotation de l'appareil. Les sous-vues tournent également entre elles pour respecter différentes orientations. J'essaie d'atteindre ce comportement ....


22
2017-07-31 14:28


origine


Réponses:


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 consiste à appliquer directement une transformation à 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 aux contrôleurs de chaque vue et au contrôleur de présentation. viewWillTransitionToSize:withTransitionCoordinator: méthode.

Notez que l'application Camera ne pas Désactiver l'autorotation: lorsque vous utilisez l'application Appareil photo, les orientations du Centre de notifications et du Centre de contrôle reflètent l'orientation du périphérique. Ce ne sont que des vues particulières de l'application Appareil photo qui semblent se désinscrire de l'autorotation. En réalité, cette réponse suggère que ces applications utilisent généralement le videoGravity et videoOrientation les propriétés fournies par les classes AVFoundation telles que AVCaptureVideoPreviewLayer et AVCaptureConnection.

L'approche que vous devez suivre dépend si vous souhaitez uniquement empêcher la rotation d'une vue parent ou si vous souhaitez empêcher un contrôleur de vue de conteneur, tel que UINavigationController, de tourner (par exemple, verrouiller l'orientation du périphérique).

Si c'est le cas, utilisez la technique décrite dans la note technique pour fixer l'orientation de la vue parente et (pour iOS 9) pour envelopper les sous-vues tournantes dans UIStackView et utiliser le axis propriété pour réorganiser les sous-vues sur la rotation du périphérique, ou (pour iOS 8), faire pivoter manuellement les sous-vues sur la rotation du périphérique (voir cette réponse et cet exemple de code).

Si c'est le 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 plus d'informations sur la hiérarchie de votre contrôleur de vue pour nous permettre de diagnostiquer les bizarreries impliquant Notification Center.

Addenda: La raison pour laquelle les appels à shouldAutorotate et supportedInterfaceOrientations ne sont pas propagés aux contrôleurs de vue enfants a été expliqué dans Questions techniques QA1688:

A partir d’iOS 6, seul le contrôleur de vue le plus haut (à côté du UIApplication object) participe à la décision de faire pivoter 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 son supportedInterfaceOrientations et shouldAutorotate méthodes.

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


6
2017-08-10 13:30



De la bibliothèque de développeurs iOS Questions 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 ce changement dans la hiérarchie du contrôleur de vue via des appels à chaque contrôleur de vue et au contrôleur de présentation -viewWillTransitionToSize: withTransitionCoordinator: method. Les invocations de cette méthode sont fournies avec un objet coordinateur de transition contenant le delta de la transformation en cours et de la nouvelle transformation de la fenêtre, qui peut être récupéré en envoyant un message -targetTransform au coordinateur de la transition. Votre contrôleur de vue ou contrôleur 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'apparence que la vue est restée fixe dans son orientation actuelle lorsque le reste de l'interface tourne 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 à https://github.com/rishi420/TestCameraRotation


2
2017-08-10 13:38



Après beaucoup de va-et-vient avec Apple eux-mêmes, et eux pointant vers le même lien Questions techniques QA1890, Je devais le faire de manière 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 conservant la vue principale statique. En outre, l'application semble connaître l'orientation correcte lorsque les notifications arrivent (parce que la vue mère tourne dans les coulisses).

Merci spécial à Jamesk et Warif Akhand Rishi pour toute l'aide!


1
2017-08-16 15:56