[SWIFT] Développement d'applications iOS: application Timer (10. Créer une animation)

スクリーンショット 2020-10-28 11.35.01.png

article

Les points pour créer une application de minuterie sont publiés dans plusieurs articles. Dans cet article, je vais vous montrer comment créer une animation pour rendre le compte à rebours visuellement agréable.

environnement

Dépôt Git

Vous pouvez voir l'exemple de code à partir de l'URL du référentiel Git suivante. https://github.com/msnsk/Qiita_Timer.git

procédure

  1. Pensez au type d'animation que vous souhaitez
  2. Créez une vue animée
  3. Préparez une figure à animer
  4. Spécifiez la couleur de la figure et changez-la constamment
  5. Appliquer une animation qui déplace la forme
  6. Placez la vue animée sur la vue principale

1. Pensez au type d'animation que vous souhaitez

En regardant le MainView pour le moment, il y a un léger écart entre le TimerView / PickerView et le ProgressBarView, donc j'aimerais animer la forme dans cet espace vide pour le rendre visuellement amusant.

Comme un mouvement concret, préparez deux petits cercles, placez-les à une position qui touche l'intérieur de la barre de progression à 12 heures, et prenez quelques secondes pour faire le tour de la barre de progression. Je vais y arriver. Les deux cercles se déplacent respectivement dans le sens horaire et antihoraire. Et après un tour, il se chevauchera à nouveau exactement à 12 heures.

Définissez également la transparence des deux cercles sur 0,5 afin que les couleurs se mélangent et que la transparence devienne 1 lorsqu'ils se chevauchent. Les couleurs des deux cercles, comme la barre de progression, changent constamment.

2. Créez une vue animée

Créez un nouveau fichier avec le nom AnimationView. Préparez une instance de la classe TimeManager dans les propriétés de la structure Animation. Préfixez-le avec le wrapper de propriété @EnvironmentObject car il fait toujours référence à la valeur de cette propriété.

De plus, lors de l'organisation d'une figure dans une animation, la position est spécifiée en fonction de la taille de l'écran, préparez donc à l'avance la propriété de taille d'écran (ici, seule la largeur est utilisée, mais l'appareil est tourné sur le côté. Si vous souhaitez ajuster la mise en page lorsque vous le faites, vous avez également besoin de la hauteur).

AnimationView.swift


import SwiftUI

struct AnimationView: View {
    @EnvironmentObject var timeManager: TimeManager
    
    let screenWidth = UIScreen.main.bounds.width
    let screenHeight = UIScreen.main.bounds.height
    
    var body: some View {

    }
}

3. Préparez une figure à animer

L'animation que nous allons créer déplacera deux cercles.

Ajoutez deux composants Circle () à l'intérieur du corps {}. Superposez-les les uns sur les autres avec ZStack {}. Peu importe lequel est en haut.

Dans le modificateur .frame (), spécifiez une taille de 20. C'est un cercle assez petit.

Le modificateur .offset () décale la position de placement verticalement vers le haut. Maintenant, déplacez-le vers le haut dans la direction verticale (y) en fonction de la propriété screenWidth qui stocke la largeur de l'écran préparé précédemment. Si vous le déplacez vers le haut, ce sera une valeur négative. Au contraire, si vous souhaitez le décaler vers le bas, ce sera une valeur positive. La longueur du décalage correspond à la largeur de l'écran x 0,38. Cela placera un cercle au-dessus du centre de l'écran, à peu près à l'intérieur de la barre de progression.

AnimationView.swift


struct AnimationView: View {
    @EnvironmentObject var timeManager: TimeManager
    
    let screenWidth = UIScreen.main.bounds.width
    let screenHeight = UIScreen.main.bounds.height
    
    var body: some View {
        ZStack{
            Circle()
                .frame(width: 20, height: 20)
                .offset(y: -self.screenWidth * 0.38)
            
            Circle()
                .frame(width: 20, height: 20)
                .offset(y: -self.screenWidth * 0.38)
        }
    }
}

4. Spécifiez la couleur de la figure et changez-la constamment

Ensuite, préparez une propriété qui spécifie la teinte des deux cercles. Les valeurs initiales sont respectivement de 0,5 et 0,3.

    @State var costomHueA: Double = 0.5
    @State var costomHueB: Double = 0.3

Dans le modificateur .foregroundColor, utilisez les valeurs des propriétés de teinte ci-dessus pour colorer le cercle en spécifiant la teinte, la saturation, la luminosité et l'opacité. Tout d'abord, placez les propriétés ci-dessus dans l'argument hue. Et comme l'opacité rend les cercles 0,5 et translucides, nous spécifions une valeur maximale de 1 pour la saturation et la luminosité.

//Pour le premier cercle
.foregroundColor(Color(hue: self.costomHueA, saturation: 1.0, brightness: 1.0, opacity: 0.5))
//Pour le deuxième cercle
.foregroundColor(Color(hue: self.costomHueB, saturation: 1.0, brightness: 1.0, opacity: 0.5))

Et, afin de changer constamment la couleur, il est nécessaire de changer constamment la valeur de costomHueA / B de la propriété. Nous l'avons fait lorsque nous avons créé ProgressBarView, mais nous pouvons le faire avec le modificateur onReceive en déclenchant la propriété timer de la classe TimeManager (la méthode de publication de la classe Timer).

Créez un modificateur onReceive pour le ZStack {} qui contient les deux composants Circle dans le corps {}.

Ensuite, en même temps que la méthode Timer.publish est activée toutes les 0,05 secondes, la valeur de costomHueA / B est également augmentée de +0,05. Étant donné que l'argument hue de la structure Color qui crée la couleur prend uniquement une valeur Double de 0 à 1, ajoutez-le avec une instruction if de sorte que lorsque costomHueA / B devienne respectivement 1,0, il revienne à 0,0.

.onReceive(timeManager.timer) { _ in
    self.costomHueA += 0.005
    if self.costomHueA >= 1.0 {
        self.costomHueA = 0.0
    }
    self.costomHueB += 0.005
    if self.costomHueB >= 1.0 {
        self.costomHueB = 0.0
    }
}

À ce stade, tout le code ressemble à ceci:

AnimationView.swift


struct AnimationView: View {
    @EnvironmentObject var timeManager: TimeManager
    @State var costomHueA: Double = 0.5
    @State var costomHueB: Double = 0.3

    let screenWidth = UIScreen.main.bounds.width
    let screenHeight = UIScreen.main.bounds.height
    
    var body: some View {
        ZStack{
            Circle()
                .frame(width: 20, height: 20)
                .offset(y: -self.screenWidth * 0.38)
                .foregroundColor(Color(hue: self.costomHueA, saturation: 1.0, brightness: 1.0, opacity: 0.5))
         
            Circle()
                .frame(width: 20, height: 20)
                .offset(y: -self.screenWidth * 0.38)
                .foregroundColor(Color(hue: self.costomHueB, saturation: 1.0, brightness: 1.0, opacity: 0.5))
        }
        .onReceive(timeManager.timer) { _ in
            self.costomHueA += 0.005
            if self.costomHueA >= 1.0 {
                self.costomHueA = 0.0
            }
            self.costomHueB += 0.005
            if self.costomHueB >= 1.0 {
                self.costomHueB = 0.0
            }
        }
    }
}

5. Appliquer une animation qui déplace la forme

Nous ajouterons des modificateurs liés à l'animation aux deux cercles.

faites pivoter le cercle avec le modificateur rotationEffect. En fonction de l'angle de cette rotation, la position du cercle décalé par le modificateur de décalage ajouté précédemment changera également du même angle autour du centre de l'écran.

Par conséquent, si vous souhaitez déplacer deux cercles le long d'une barre de progression circulaire, spécifiez les angles des deux motifs au début et à la fin de l'animation entre () de l'argument rotationEffect degrés et déplacez les deux à intervalles réguliers. La commutation provoque une modification de la valeur de l'angle entre avant et après la commutation, et la modification est reproduite dans l'animation. Le résultat est une animation de deux cercles se déplaçant d'avant en arrière.

Ici, nous voulons faire une animation qui se déplace autour de 360 °, spécifiez donc 0 ° et 360 °. Préparez la propriété dans le sens des aiguilles d'une montre en tant que type booléen comme déclencheur pour changer l'angle. Laissez la valeur initiale vraie.

@State var clockwise = true

Si la propriété dans le sens des aiguilles d'une montre est vraie, la rotation du cercle est de 0 °, et si elle est fausse, la rotation est de 360 °. D'autre part, l'autre composant Circle est inversé de sorte que si le sens horaire est vrai, il est de 360 ° et s'il est faux, il est de 0 °. Cela provoque la rotation des deux cercles dans des directions opposées.

//Pour le premier cercle
.rotationEffect(.degrees(clockwise ? 0 : 360))
//Pour le deuxième cercle
.rotationEffect(.degrees(clockwise ? 360 : 0))

Ajoutez un modificateur d'animation sous le modificateur rotationEffect. Cela appliquera l'animation aux modificateurs au-dessus du modificateur d'animation.

Il existe plusieurs types d'animation, mais nous voulons la faire bouger lentement au début, rapidement au milieu et lentement à la fin, nous appliquons donc ici easInOut comme argument au modificateur d'animation. Mettez ensuite 5 dans l'argument duration de easyInOut. Cela signifie exécuter une seule animation pendant 5 secondes.

.animation(.easeInOut(duration: 5))

Cependant, l'intervalle d'activation de la propriété timer est assez court, 0,05 seconde par heure, créez donc une autre propriété count de type Double. Il est toujours référencé uniquement dans cette structure AnimationView, il est donc préfixé avec le wrapper de propriété @State.

@State var count: Double = 0

Dans le modificateur onReceive créé à l'étape 4, assurez-vous d'abord que la valeur dans le sens des aiguilles d'une montre est basculée lorsque la propriété count est 0. Autrement dit, assurez-vous que l'animation démarre lorsque count est égal à 0.

.onReceive(timeManager.timer) { _ in
    if self.count <= 0 {
        self.clockwise.toggle()
    }

    //(Code omis lié à costomHue)
}

En outre, écrivez du code qui modifie la valeur de la propriété count au fil du temps.

Comme la méthode Timer.publish se déclenche toutes les 0,05 secondes, elle incrémente également la valeur de la propriété count de 0,05 seconde. Ensuite, lorsque la valeur de la propriété count atteint 5, la valeur de la propriété count doit être à nouveau renvoyée à 0.

.onReceive(timeManager.timer) { _ in
    if self.count <= 0 {
        self.clockwise.toggle()
    }
    if self.count < 5.00 {
        self.count += 0.05
    } else {
        self.count = 0
    }
    //(Code omis lié à costomHue)
}

Cela fera basculer la propriété dans le sens horaire toutes les 5 secondes et déclenchera l'animation. Le temps requis pour chaque animation est également réglé sur 5 secondes, donc l'animation sera répétée toutes les 5 secondes sans interruption.

Maintenant, tout le code ressemble à ceci: Ceci termine l'AnimationView.

AnimationView.swift


struct AnimationView: View {
    @EnvironmentObject var timeManager: TimeManager
    @State var costomHueA: Double = 0.5
    @State var costomHueB: Double = 0.3
    @State var clockwise = true

    let screenWidth = UIScreen.main.bounds.width
    let screenHeight = UIScreen.main.bounds.height
    
    var body: some View {
        ZStack{
            Circle()
                .frame(width: 20, height: 20)
                .offset(y: -self.screenWidth * 0.38)
                .foregroundColor(Color(hue: self.costomHueA, saturation: 1.0, brightness: 1.0, opacity: 0.5))
                .rotationEffect(.degrees(clockwise ? 0 : 360))
                .animation(.easeInOut(duration: 5))
            
            Circle()
                .frame(width: 20, height: 20)
                .offset(y: -self.screenWidth * 0.38)
                .foregroundColor(Color(hue: self.costomHueB, saturation: 1.0, brightness: 1.0, opacity: 0.5))
                .rotationEffect(.degrees(clockwise ? 360 : 0))
                .animation(.easeInOut(duration: 5))
        }
        .onReceive(timeManager.timer) { _ in
            if self.count <= 0 {
                self.clockwise.toggle()
            }
            if self.count < 5.00 {
                self.count += 0.05
            } else {
                self.count = 0
            }
            
            self.costomHueA += 0.005
            if self.costomHueA >= 1.0 {
                self.costomHueA = 0.0
            }
            self.costomHueB += 0.005
            if self.costomHueB >= 1.0 {
                self.costomHueB = 0.0
            }
        }
    }
}

Vérifiez l'AnimationView sur le canevas. Cela devrait ressembler à l'image gif ci-dessous. 5p2wx-8k3l6.gif

6. Placez la vue animée sur la vue principale

Plaçons maintenant l'AnimationView terminée dans MainView.

La condition d'affichage de l'animation est définie uniquement lorsque l'état de la minuterie est différent de .stopped (.running ou .pause).

En outre, il existe un interrupteur à bascule marche / arrêt pour l'animation d'effet dans les éléments de réglage du SettingView créé précédemment. Par conséquent, l'animation est affichée uniquement lorsque isEffectAnimationOn de la classe TimeManager liée à ce commutateur à bascule est true.

Décrivez avec une instruction if dans les deux conditions ci-dessus.

if timeManager.isEffectAnimationOn && timeManager.timerStatus != .stopped {
    AnimationView()
}

Aussi, je veux placer l'AnimationView dans le code sur le calque, après la barre de progression supérieure, donc je l'écrirai au début de ZStack {}.

Enfin, le code de MainView ressemble à ceci:

MainView


struct MainView: View {
    @EnvironmentObject var timeManager: TimeManager
    
    var body: some View {
        ZStack {
            if timeManager.isEffectAnimationOn && timeManager.timerStatus != .stopped {
                AnimationView()
            }
            
            if timeManager.isProgressBarOn {
                ProgressBarView()
            }
            
            //(Autres vues omises)
        }
        .onReceive(timeManager.timer) { _ in
            //(réduction)
        }
    }
}

Vérifiez MainView dans Canvas. Cela devrait ressembler à l'image ci-dessous. スクリーンショット 2020-10-28 11.44.42.png

enfin

Cette série "Développement d'applications iOS: application de minuterie" est complétée avec les 10 articles jusqu'à présent.

Je n'ai spécifié aucun bouton, texte ou couleur d'arrière-plan dans l'application, il peut donc manquer de couleur. Si vous souhaitez consulter les articles de cette série, essayez de la personnaliser vous-même.

Recommended Posts

Développement d'applications iOS: application Timer (10. Créer une animation)
Développement d'applications iOS: application Timer (2. affichage de la minuterie)
Développement d'applications iOS: application Timer (résumé)
Développement d'applications iOS: application Timer (1. réglage de l'heure de la minuterie)
Développement d'applications iOS: application Timer (3. bouton Démarrer / Arrêter, bouton Réinitialiser)
Développement d'applications iOS: application Timer (7. Mise en œuvre de la sélection du son d'alarme)
Développement d'applications iOS: application Timer (5. Implémentation d'alarme et de vibration)
Développement d'applications iOS: application Timer (9. Personnalisez la couleur de la barre de progression)
Feuille de route des compétences de développement d'applications iOS (introduction)
L'ingénieur iOS démarre le développement Android
Développement d'applications ROS sur Android
Développement ATDD sur iOS (édition de base)