web-dev-qa-db-fra.com

Comment appliquer une transformation de perspective à UIView?

Je cherche à effectuer une transformation de perspective sur UIView (comme dans coverflow)

Est-ce que quelqu'un sait si c'est possible?

J'ai étudié avec CALayer et parcouru tous les podcasts du développeur pragmatique Core Animation, mais je ne vois toujours pas comment créer ce type de transformation sur un iPhone.

Toute aide, des pointeurs ou des exemples de fragments de code seraient vraiment appréciés!

198
Nick Cartwright

Comme Ben l'a dit, vous devrez travailler avec le UIView's couche, en utilisant un CATransform3D pour effectuer le layer'srotation. Le truc pour faire fonctionner la perspective, comme décrit ici , est d'accéder directement à l'une des matrices cells du CATransform3D (m34). Les mathématiques matricielles n’ont jamais été mon truc, je ne peux donc pas expliquer exactement pourquoi cela fonctionne, mais ça marche. Vous devez définir cette valeur sur une fraction négative pour votre transformation initiale, puis appliquer votre rotation à la couche. Vous devriez aussi pouvoir faire ce qui suit:

Objective-C

UIView *myView = [[self subviews] objectAtIndex:0];
CALayer *layer = myView.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 45.0f * M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;

Swift 5.0

if let myView = self.subviews.first {
    let layer = myView.layer
    var rotationAndPerspectiveTransform = CATransform3DIdentity
    rotationAndPerspectiveTransform.m34 = 1.0 / -500
    rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 45.0 * .pi / 180.0, 0.0, 1.0, 0.0)
    layer.transform = rotationAndPerspectiveTransform
}

qui reconstruit la transformation de la couche à partir de zéro pour chaque rotation.

Vous trouverez un exemple complet (avec code) ici , où j'ai implémenté la rotation et la mise à l'échelle tactiles sur quelques CALayers, à partir d'un exemple de Bill Dudney . La version la plus récente du programme, tout en bas de la page, implémente ce type d'opération de perspective. Le code doit être relativement simple à lire.

Le sublayerTransform auquel vous faites référence dans votre réponse est une transformation appliquée aux sous-couches de votre UIView'sCALayer. Si vous n'avez pas de sous-couches, ne vous inquiétez pas. J'utilise sublayerTransform dans mon exemple simplement parce qu'il y a deux CALayers dans le calque en rotation.

325
Brad Larson

Vous pouvez uniquement utiliser des transformations Core Graphics (Quartz, 2D uniquement) appliquées directement à la propriété de transformation d'UIView. Pour obtenir les effets dans Coverflow, vous devez utiliser CATransform3D, qui sont appliqués dans un espace 3D, et vous donner ainsi la vue en perspective de votre choix. Vous pouvez uniquement appliquer CATransform3D aux calques, pas aux vues, vous devrez donc basculer vers les calques pour cela.

Découvrez l'exemple "CovertFlow" fourni avec Xcode. C'est mac-only (c'est-à-dire pas pour iPhone), mais beaucoup de concepts sont bien transférés.

7
Ben Gottlieb

Pour ajouter à la réponse de Brad et donner un exemple concret, j'emprunte de ma propre réponse dans un autre message sur un sujet similaire. Dans ma réponse, j'ai créé un simple Swift terrain de jeu que vous pouvez télécharger et jouer avec , où je montre comment créer des effets de perspective d'un UIView avec une simple hiérarchie de couches, et je montre des résultats différents entre transform et sublayerTransform. Regardez-le.

Voici quelques images du post:

.sublayerTransform option

.transform option

3
HuaTham

Vous pouvez obtenir un effet Carrousel précis en utilisant le SDK iCarousel.

Vous pouvez obtenir un effet instantané Cover Flow sur iOS en utilisant la merveilleuse et gratuite bibliothèque iCarousel. Vous pouvez le télécharger depuis https://github.com/nicklockwood/iCarousel et le déposer assez facilement dans votre projet Xcode en ajoutant un en-tête de pontage (écrit en Objective-C).

Si vous n'avez pas ajouté de code Objective-C à un projet Swift auparavant, procédez comme suit:

  • Téléchargez iCarousel et décompressez-le
  • Allez dans le dossier que vous avez décompressé, ouvrez son sous-dossier iCarousel, puis sélectionnez iCarousel.h et iCarousel.m et faites-les glisser dans la navigation de votre projet - c'est le volet de gauche de Xcode. Juste en dessous de Info.plist va bien.
  • Cochez "Copier les éléments si nécessaire" puis cliquez sur Terminer.
  • Xcode vous demandera le message "Souhaitez-vous configurer un en-tête de pontage Objective-C?" Cliquez sur "Créer un en-tête de pontage". Vous devriez voir un nouveau fichier dans votre projet, nommé YourProjectName-Bridging-Header.h.
  • Ajoutez cette ligne au fichier: #import "iCarousel.h"
  • Une fois que vous avez ajouté iCarousel à votre projet, vous pouvez commencer à l'utiliser.
  • Assurez-vous de respecter les protocoles iCarouselDelegate et iCarouselDataSource.

Swift 3 exemple de code:

    override func viewDidLoad() {
      super.viewDidLoad()
      let carousel = iCarousel(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
      carousel.dataSource = self
      carousel.type = .coverFlow
      view.addSubview(carousel) 
    }

   func numberOfItems(in carousel: iCarousel) -> Int {
        return 10
    }

    func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
        let imageView: UIImageView

        if view != nil {
            imageView = view as! UIImageView
        } else {
            imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 128, height: 128))
        }

        imageView.image = UIImage(named: "example")

        return imageView
    }
0
Tech