[SWIFT] Développement d'applications iOS: application Timer (8. Implémentation de la barre de progression)

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

Contenu

Les points pour créer une application de minuterie sont publiés dans plusieurs articles. Dans cet article, j'écrirai sur la création d'une barre de progression circulaire qui montre le temps restant du compte à rebours.

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. Créez une vue de la barre de progression
  2. Créez un cercle pour l'arrière-plan de la barre de progression
  3. Créez un cercle pour la barre de progression
  4. Liez la longueur de la barre de progression au temps écoulé
  5. Placez une barre de progression sur MainView
  6. Lissez le mouvement de la barre de progression

1. Créez une vue de la barre de progression

Créez un nouveau fichier avec le nom ProgressBar.swift. Cette vue fait également référence à la valeur de propriété de la classe TimeManager. Créez donc une instance de la classe TimeManager avec le wrapper de propriété @EnvironmentObject avant var.

ProgressBar.swift


import SwiftUI

struct ProgressBarView: View {
    @EnvironmentObject var timeManager: TimeManager

    var body: some View {
        Text("Hello, World!")
    }
}

2. Créez un cercle pour l'arrière-plan de la barre de progression

La barre de progression nécessite deux cercles, l'un qui se raccourcit avec le temps et l'autre qui est l'arrière-plan. Les cercles d'arrière-plan sont de la même taille et ne changent pas de longueur avec le temps. Tout d'abord, nous allons créer à partir du cercle d'arrière-plan.

Placez le cercle à l'intérieur du corps {}. SwiftUI fournit un composant graphique appelé Circle (), que vous pouvez utiliser.

ProgressBar.swift


struct ProgressBarView: View {
    @EnvironmentObject var timeManager: TimeManager

    var body: some View {
        Circle()
    }
}

Une figure se compose d'une ligne de contour et d'une face. Pour créer un cercle creux, n'affichez pas la face, affichez uniquement la ligne de contour et ajustez l'épaisseur, la longueur et la couleur de la ligne de contour.

Ajoutez un modificateur Cercle () pour obtenir la forme souhaitée.

Dans le modificateur .stroke, mettez Color () dans l'argument, puis définissez l'argument sur .darkGray pour en faire une couleur de fond grise.

Dans le modificateur .stroke, définissez l'argument de style lineWidth sur 20 pour spécifier l'épaisseur de la barre de progression.

Utilisez le modificateur .scaledToFit pour ajuster la taille du cercle afin de remplir la taille de l'écran et le modificateur .padding pour ajuster les marges avec les bords de l'écran.

ProgressBar.swift


struct ProgressBarView: View {
    @EnvironmentObject var timeManager: TimeManager

    var body: some View {
        Circle()
                .stroke(Color(.darkGray), style: StrokeStyle(lineWidth: 20))
                .scaledToFit()
                .padding(25)
    }
}

3. Créez un cercle pour la barre de progression

Le cercle de la barre de progression que vous allez créer chevauchera le cercle d'arrière-plan créé à l'étape 2, donc si vous ajoutez un autre composant Circle () pour la barre de progression, entourez les deux cercles avec ZStack {}. Je vais.

ProgressBar.swift


struct ProgressBarView: View {
    @EnvironmentObject var timeManager: TimeManager

    var body: some View {
        ZStack {
            //Cercle pour le fond
            Circle()
                .stroke(Color(.darkGray), style: StrokeStyle(lineWidth: 20))
                .scaledToFit()
                .padding(15)
            
            //Cercle pour la barre de progression
            Circle()
        }
    }
}

Comme le cercle d'arrière-plan, le cercle de la barre de progression sera mis en forme en ajoutant des modificateurs.

Dans le modificateur .stroke, j'ai mis Color () dans l'argument et spécifié .cyan comme couleur de la barre de progression pour le moment.

Dans le modificateur .stroke, mettez une spécification détaillée dans StrokeStyle de l'argument style. Spécifiez la largeur à 20 avec lineWidth, spécifiez .round avec lineCap pour arrondir les coins des extrémités de la ligne et spécifiez .round avec lineJoin pour dépasser la fin de la ligne de la moitié de la longueur de la ligne. Faites le tour.

Ajoutez le modificateur .rotationEffect et prenez Angle (degrés: -90) comme argument. Cela vous permet de changer la position de départ du contour de cercle par défaut de la direction 3 heures à la direction 12 heures.

D'autres sont identiques au cercle d'arrière-plan.

ProgressBar.swift


struct ProgressBarView: View {
    @EnvironmentObject var timeManager: TimeManager

    var body: some View {
        ZStack {
            //Cercle pour le fond
            Circle()
                .stroke(Color(.darkGray), style: StrokeStyle(lineWidth: 20))
                .scaledToFit()
                .padding(15)
            
            //Cercle pour la barre de progression
            Circle()
                .stroke(Color(.cyan), style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .bevel))
                .scaledToFit()
                //Réglez la position de départ de la ligne de contour dans le sens 12 heures
                .rotationEffect(Angle(degrees: -90))
                .padding(15)
        }
    }
}

4. Liez la longueur de la barre de progression au temps écoulé

Ajoutez plus de modificateurs au cercle de la barre de progression afin que la barre de progression se raccourcisse à mesure que le compte à rebours s'écoule.

Ajoutez un modificateur .trim. Cela vous permet de couper la barre de progression à la longueur requise.

La position de départ est toujours fixée à 12 heures, donc mettez 0 dans l'argument from. Comme la barre de progression doit se raccourcir avec le temps, la position finale doit toujours être liée au temps restant. En outre, la valeur des arguments from et to doit être comprise entre 0 et 1.

Voici un peu d'arithmétique. La classe TimeManager fournit une propriété maxValue qui stocke la durée maximale définie dans Picker et une durée de propriété qui stocke le temps restant. En utilisant ces deux valeurs, la formule qui a une valeur maximale de 1 au début du compte à rebours et une valeur minimale de 0 à la fin du compte à rebours est duration / maxValue.

Comme le type de données de la valeur de l'argument à doit être CGFloat, la valeur à mettre finalement dans l'argument à est la suivante.

CGFloat(self.timeManager.duration / self.timeManager.maxValue)

Donc, le code ressemble à ceci:

ProgressBar.swift


struct ProgressBarView: View {
    @EnvironmentObject var timeManager: TimeManager

    var body: some View {
        ZStack {
            //Cercle pour le fond
            Circle()
                //(Modificateur omis)
            
            //Cercle pour la barre de progression
            Circle()
                .trim(from: 0, to: CGFloat(self.timeManager.duration / self.timeManager.maxValue))
                .stroke(Color(.cyan), style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
                .scaledToFit()
                //Réglez la position de départ de la ligne de contour dans le sens 12 heures
                .rotationEffect(Angle(degrees: -90))
                .padding(15)
        }
    }
}

Vérifiez ProgressBarView dans Canvas. Vous trouverez ci-dessous le code de l'aperçu.

struct ProgressBarView_Previews: PreviewProvider {
    static var previews: some View {
        ProgressBarView()
            .environmentObject(TimeManager())
            .previewLayout(.sizeThatFits)
    }
}

Cela devrait ressembler à l'image ci-dessous. スクリーンショット 2020-10-28 11.22.51.png

5. Placez une barre de progression sur MainView

Ajoutez une instance de ProgressBarView en haut du ZStack le plus à l'extérieur {} à l'intérieur du corps {} de MainView. Le haut du code ZStack est l'arrière de la hiérarchie des couches de composants de l'interface utilisateur. En tant qu'image, l'affichage du temps restant et le sélecteur de réglage de l'heure sont au premier plan sur l'écran.

De plus, comme un interrupteur à bascule pour afficher / masquer la barre de progression est d'abord préparé dans l'élément SettingView de l'écran de réglage, la barre de progression s'affiche uniquement lorsque la propriété isProgressBaron de la classe TimeManager liée à ce paramètre est true. Décrivez-le avec une instruction if pour qu'il s'affiche.

MainView.swift


struct MainView: View {
    @EnvironmentObject var timeManager: TimeManager
    
    var body: some View {
        ZStack {
            if timeManager.isProgressBarOn {
                ProgressBarView()
            }
            
            if timeManager.timerStatus == .stopped {
                PickerView()
            } else {
                TimerView()
            }
            
            VStack {
                Spacer()
                ZStack {
                    ButtonsView()
                        .padding(.bottom)

                    SettingButtonView()
                        .padding(.bottom)
                        .sheet(isPresented: $timeManager.isSetting) {
                            SettingView()
                                .environmentObject(self.timeManager)
                        }
                }
            }
        }
        .onReceive(timeManager.timer) { _ in
            //(Omission de description dans onReceive)
        }
    }
}

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

6. Lissez le mouvement de la barre de progression

J'ai pu implémenter la barre de progression, mais lorsque j'ai vérifié le mouvement réel de MainView avec Xcode Canvas et Simulator, plus le temps de réglage de la minuterie était court, plus la barre de progression devenait courte chaque seconde (c'était saccadé). Tu peux voir. Ce n'est pas un échec en tant que barre de progression, mais la plus lisse visuellement donne une impression de sophistication, donc je vais y remédier un peu.

Il y a deux causes à ce mouvement saccadé, nous allons donc corriger chacune d'elles.

Le premier est la propriété timer de la classe TimeManager. Cette propriété contient la méthode de publication de la classe Timer, mais la valeur de son argument tous est 1. Cela signifie qu'il sera activé toutes les secondes. Modifiez cette valeur à environ 0,05. À la suite de la vérification, il y aura une erreur entre le passage réel du temps et la mise à jour du temps restant de l'application de minuterie d'environ 0,01, donc je pense qu'environ 0,05 est la limite.

Mettez à jour dans la classe TimeManager comme suit:

TimeManager.swift


class TimeManager: ObservableObject {
    //(Autres propriétés omises)

    //La méthode de publication de la classe Timer qui est activée toutes les secondes
    var timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect()

    //(Méthode omise)

La seconde est la description dans le modificateur onReceive de MainView. Ce modificateur déclenche la propriété timer de la classe TimeManager que nous avons modifiée précédemment pour exécuter le code à l'intérieur de la fermeture {}. Puisque nous avons mis à jour le déclencheur pendant 0,05 seconde, la mise à jour de la propriété duration (temps restant) de la classe TimeManager décrite dans la fermeture {} du modificateur onReceive doit également être déduite de 0,05 seconde.

Mettez à jour dans MainView comme suit.

MainView.swift


struct MainView: View {
    @EnvironmentObject var timeManager: TimeManager
    
    var body: some View {
        ZStack {
            //(réduction)
        }
        //Exécute le code dans la fermeture déclenchée par une minuterie qui s'active à chaque heure spécifiée (1 seconde)
        .onReceive(timeManager.timer) { _ in
            //L'état de la minuterie est.Ne rien faire sauf courir
            guard self.timeManager.timerStatus == .running else { return }
            //Si le temps restant est supérieur à 0
            if self.timeManager.duration > 0 {
                //Du temps restant-0.05
                self.timeManager.duration -= 0.05 //Mettez à jour ici!
                //Lorsque le temps restant est égal ou inférieur à 0
            } else {
                //État de la minuterie.Changer pour arrêté
                self.timeManager.timerStatus = .stopped
                //Sonner une alarme
                AudioServicesPlayAlertSoundWithCompletion(self.timeManager.soundID, nil)
                //Activer la vibration
                AudioServicesPlayAlertSoundWithCompletion(SystemSoundID(kSystemSoundID_Vibrate)) {}
            }
        }
    }
}

Désormais, par exemple, même si vous réglez le minuteur sur 5 secondes, la barre de progression se déplacera de manière relativement fluide. La prochaine fois, j'afficherai plus joliment la couleur de la barre de progression, ce qui est un petit élément supplémentaire.

Recommended Posts

Développement d'applications iOS: application Timer (8. Implémentation de la barre de progression)
Développement d'applications iOS: application Timer (9. Personnalisez la couleur de la barre de progression)
Développement d'applications iOS: application Timer (4. Implémentation du compte à rebours)
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 (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 (10. Créer une animation)
Développement d'applications iOS: application Timer (3. bouton Démarrer / Arrêter, bouton Réinitialiser)
Journal complet de développement d'applications IOS pour l'auto-apprentissage
Feuille de route des compétences de développement d'applications iOS (introduction)
Mémo d'implémentation SKStoreReviewController dans l'interface utilisateur Swift d'iOS14