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.
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
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))
}
}
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. En changeant l'état de sélection, l'apparence a également changé. Maintenant, ajoutons une animation à partir d'ici.
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)
})
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))
}
}
}
L'animation des héros est passionnante, alors j'aimerais essayer diverses autres choses.