Le développement d'écrans de bienvenue (onboarding) ou de guides interactifs pour une application iOS peut rapidement devenir complexe lorsqu'il s'agit de gérer les transitions entre pages et les animations synchronisées. La bibliothèque Presentation offre une abstraction élégante au-dessus de UIPageViewController pour simplifier ce processus grâce à un système de positionnement relatif et une gestion intuitive des animations.
Architecture et positionnement par pourcentage
L'un des conecpts phares de Presentation est l'utilisation de coordonnées exprimées en pourcentages. Cela permet de s'affranchir des calculs de frames fixes et de garantir une mise en page adaptative selon la taille de l'écran.
La classe Position
La classe Position définit l'emplacement d'un élément en utilisant des ratios allant de 0.0 à 1.0. Voici différentes manières d'instancier un emplacement :
// Alignement depuis le coin supérieur gauche
let coinSuperieurGris = Position(left: 0.25, top: 0.35)
// Alignement depuis le coin inférieur gauche
let baseGauche = Position(left: 0.25, bottom: 0.70)
// Alignement depuis le coin supérieur droit
let zoneDroiteHaut = Position(right: 0.15, top: 0.35)
// Alignement depuis le coin inférieur droit
let zoneDroiteBas = Position(right: 0.15, bottom: 0.70)
Le conteneur Content
La classe Content fait le lien entre une vue standard (UIView) et sa Position. Elle s'occupe de générer les contraintes Auto Layout nécessaires en arrière-plan.
let monLabel = UILabel()
monLabel.text = "Bienvenue à bord"
monLabel.sizeToFit()
// Élément centré sur son point de position
let texteCentre = Content(view: monLabel, position: Position(left: 0.5, top: 0.5))
// Élément dont l'origine (coin haut-gauche) est fixée à la position
let texteOrigine = Content(view: monLabel, position: Position(left: 0.2, top: 0.2), centered: false)
Système d'animation des transitions
La bibliothèque permet de définir des animations qui se déclenchent lors du défilement entre les pages, offrant un contrôle précis sur le mouvement des éléments.
Utilisation de TransitionAnimation
let logoView = Content(view: monImageView, position: Position(left: 0.1, top: 0.4))
let ciblePosition = Position(left: 0.9, top: 0.4)
let glissementAnimation = TransitionAnimation(
content: logoView,
destination: ciblePosition,
duration: 1.5, // Durée de l'effet
damping: 0.75, // Coefficient d'amortissement (ressort)
reflective: true, // Animation réversible au retour
initial: false // Jouer immédiatement ou non
)
Comparatif des types d'animations disponibles
| Type d'effet | Description | Usage recommandé |
|---|---|---|
| Translation | Déplacement d'un point A vers un point B | Mouvement d'éléments entre deux pages |
| Mise à l'échelle | Modification de la taille (zoom) | Focus sur un élément spécifique |
| Opacité (Fade) | Variation de la transparence | Apparition ou disparition fluide |
| Rotation | Pivotement de la vue | Indicateurs de chargement ou effets visuels |
Mise en œuvre pratique
Pour intégrer Presentation, vous pouvez utiliser CocoaPods (pod 'Presentation') ou Carthage (github "hyperoslo/Presentation").
Exemple de contrôleur de tutoriel
import Presentation
class OnboardingViewController: PresentationController {
override func viewDidLoad() {
super.viewDidLoad()
setupGuide()
}
private func setupGuide() {
let pageA = construireEcran(titre: "Étape 1", sousTitre: "Découvrez notre interface.")
let pageB = construireEcran(titre: "Étape 2", sousTitre: "Configurez vos préférences.")
// Ajout des pages au contrôleur principal
add([pageA, pageB])
}
private func construireEcran(titre: String, sousTitre: String) -> SlideController {
let titreLabel = UILabel()
titreLabel.text = titre
titreLabel.font = .boldSystemFont(ofSize: 26)
titreLabel.sizeToFit()
let descLabel = UILabel()
descLabel.text = sousTitre
descLabel.numberOfLines = 0
descLabel.textAlignment = .center
descLabel.frame = CGRect(x: 0, y: 0, width: 280, height: 80)
let titreContent = Content(view: titreLabel, position: Position(left: 0.5, top: 0.25))
let descContent = Content(view: descLabel, position: Position(left: 0.5, top: 0.45))
let slide = SlideController(contents: [titreContent, descContent])
// Animation personnalisée pour le titre
let animTitre = TransitionAnimation(
content: titreContent,
destination: Position(left: 0.5, top: 0.35),
duration: 1.2
)
slide.add(animations: [animTitre])
return slide
}
}
Gestion avancée : L'effet Parallaxe
Le parallaxe donne une impression de profondeur en faisant bouger les éléments de l'arrière-plan à des vitesses différentes de celles du premier plan.
private func appliquerEffetParallaxe() {
let decors = [
(image: "nuages", x: -0.3, y: 0.15, vitesse: 0.2),
(image: "montagnes", x: 0.0, y: 0.4, vitesse: 0.05),
(image: "foret", x: 0.0, y: 0.7, vitesse: -0.1)
]
var listeContenus = [Content]()
for decor in decors {
let vueImage = UIImageView(image: UIImage(named: decor.image))
let positionInitiale = Position(left: decor.x, top: decor.y)
let contenu = Content(view: vueImage, position: positionInitiale, centered: false)
listeContenus.append(contenu)
}
addToBackground(listeContenus)
// Application du décalage selon les pages
for indexPage in 1...3 {
for (indexElement, decor) in decors.enumerated() {
let decalageX = decor.x + (CGFloat(indexPage) * decor.vitesse)
let cible = Position(left: decalageX, top: decor.y)
addAnimation(
TransitionAnimation(content: listeContenus[indexElement], destination: cible),
forPage: indexPage
)
}
}
}
Optimisation et bonnes pratiques
| Axe d'optimisation | Action recommandée | Bénéfice |
|---|---|---|
| Mémoire | Réutilisation des instances de Content |
Réduction de l'empreinte RAM |
| Fluidité | Limiter le nombre de vues transparentes superposées | Maintien de 60 FPS lors du scroll |
| Architecture | Utiliser des méthodes "Factory" pour les éléments récurrents | Code plus propre et maintenable |
En combinant le positionnement relatif et les animations réactives, Presentation permet de transformer un simple tutoriel statique en une expérience utilisateur immersive et dynamique, tout en conservant une base de code structurée et facile à faire évoluer.