[SWIFT] Animation utilisant matchedGeometryEffect et @Namespace

API ajoutée dans Swift UI 2.0

Parmi les nouvelles fonctionnalités ajoutées dans SwiftUI 2.0, il y avait une API qui facilite la création d'animations Hero, alors je l'ai touchée pendant un moment.

Formulaire rempli pour le moment

Créons une interface utilisateur semblable à celle d'un contrôle de segment comme celle-ci.

Github est là. https://github.com/hoshi005/matched-geometry-animation

0001.gif

Environnement de développement

Faire un bouton

Créez une vue des boutons utilisés pour la sélection. Avant cela, j'ai défini enum de manière appropriée. Environ 4 sont sélectionnés parmi les symboles SF.

enum ButtonType: String, CaseIterable {
    case share = "square.and.arrow.up"
    case trash = "trash"
    case folder = "folder"
    case person = "person"
}

La vue du bouton est créée comme ceci. Pour AccentColor, définissez votre couleur préférée dans Assets comme il convient.

struct CustomButton: View {
    //Propriétés qui représentent l'état sélectionné.
    @Binding var selected: ButtonType
    //Votre propre type de bouton.
    let type: ButtonType
    
    var body: some View {
        ZStack {
            //Dessinez un cercle sur l'arrière-plan si sélectionné.
            if selected == type {
                Circle()
                    .fill(Color.accentColor) //La couleur d'accent doit être définie dans les actifs.
            }
            
            Button(action: {
                selected = type //Appuyez sur le bouton pour basculer la sélection sur vous-même.
            }, label: {
                //Afficher l'image de l'énumération.
                Image(systemName: type.rawValue)
                    .resizable()
                    .renderingMode(.original)
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 44, height: 44)
            })
        }
        .frame(width: 80, height: 80)
    }
}

Vérifions l'apparence selon qu'elle est sélectionnée ou non. L'aperçu ressemble à ceci.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            CustomButton(selected: .constant(.share), type: .share) //État sélectionné.
            CustomButton(selected: .constant(.trash), type: .share) //État non sélectionné.
        }
        .previewLayout(.fixed(width: 100, height: 100))
    }
}

スクリーンショット 2020-10-18 13.00.39.png

Disposer les boutons sur l'écran

Organisons les boutons sur l'écran

struct ContentView: View {
    
    @State private var selected = ButtonType.share //Valeur initiale de l'état sélectionné.
    
    var body: some View {
        HStack {
            //Activez enum avec foreach et disposez les boutons personnalisés côte à côte.
            ForEach(ButtonType.allCases, id: \.self) { type in
                CustomButton(selected: $selected, type: type)
            }
        }
    }
}

L'aperçu ressemble à ceci

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ContentView()
            Group {
                CustomButton(selected: .constant(.share), type: .share)
                CustomButton(selected: .constant(.trash), type: .share)
            }
            .previewLayout(.fixed(width: 100, height: 100))
        }
    }
}

Déplaçons-le. 0002.gif En changeant l'état de sélection, l'apparence a également changé. Maintenant, ajoutons une animation à partir d'ici.

Animer

Tout d'abord, nous modifierons certains comportements lorsque vous appuyez sur le bouton afin que le changement d'état lorsque le bouton est sélectionné soit accompagné d'une animation.


//Extrait.
            
Button(action: {
    //Passer le traitement au moment de l'appui sur le bouton à la fermeture de la méthode withAnimation.
    withAnimation {
        selected = type
    }
}, label: {
    //Afficher l'image de l'énumération.
    Image(systemName: type.rawValue)
        .resizable()
        .renderingMode(.original)
        .aspectRatio(contentMode: .fit)
        .frame(width: 44, height: 44)
})

Définir .matchedGeometryEffect

Spécifiez .matchedGeometryEffect pour la vue que vous souhaitez animer. C'est comme regrouper les animations que vous souhaitez synchroniser en leur attribuant un identifiant et un espace de noms.

Commencez par déclarer l'espace de noms.

struct CustomButton: View {
    //réduction

    var namespace: Namespace.ID //Ajouter un espace de noms.
    
    //réduction
}

Spécifiez ensuite .matchedGeometryEffect pour la vue d'arrière-plan que vous souhaitez animer.


//Dessinez un cercle sur l'arrière-plan si sélectionné.
if selected == type {
    Circle()
        .fill(Color.accentColor) //La couleur d'accent doit être définie dans les actifs.
        //L'identifiant peut être n'importe quoi tant qu'il correspond aux groupes avec lesquels vous souhaitez synchroniser l'animation..
        .matchedGeometryEffect(id: "CustomButton", in: namespace)
}

Ensuite, apportez des modifications au côté de vue appelant

struct ContentView: View {
    
    @State private var selected = ButtonType.share

    //Déclarer un espace de noms à l'aide du wrapper de propriété @Namespace.
    @Namespace var namespace

    var body: some View {
        HStack {
            ForEach(ButtonType.allCases, id: \.self) { type in
                //Modifier pour donner un espace de noms à l'argument.
                CustomButton(selected: $selected, type: type, namespace: namespace)
            }
        }
    }
}

C'est ça! C'est très simple à faire!

L'aperçu fonctionnera si vous le modifiez comme ceci

struct ContentView_Previews: PreviewProvider {
    @Namespace static var namespace //N'oubliez pas la statique.
    static var previews: some View {
        Group {
            ContentView()
            Group {
                CustomButton(selected: .constant(.share), type: .share, namespace: namespace)
                CustomButton(selected: .constant(.trash), type: .share, namespace: namespace)
            }
            .previewLayout(.fixed(width: 100, height: 100))
        }
    }
}

Sommaire

L'animation des héros est passionnante, alors j'aimerais essayer diverses autres choses.

Recommended Posts

Animation utilisant matchedGeometryEffect et @Namespace
Signature et validation à l'aide de java.security.Provider
Hachage et authentification de mot de passe à l'aide de JBcrypt
Surveillance de l'infrastructure à l'aide de Graphite et StatsD