Hello
I am using matched Geometry Effect to make animations and transitions, the problem is that when I press to start the animation, the object being animated, in this case Text, is duplicated during the transition, and then when I press again to get it back to its original position, no animation takes place, how can I fix it.
Here is the code:
struct ContentView: View {
@StateObject var numberViewModel = NumberViewModel()
@Namespace var animation
var body: some View {
GeometryReader { geo in
NavigationView{
ZStack {
ScrollView{
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())]) {
ForEach(numbers){ number in
NumberView(numberViewModel: numberViewModel, animation: animation, number: number)
.onTapGesture {
withAnimation(.easeInOut(duration: 1)){
numberViewModel.selected = number
numberViewModel.tapped = true
}
}
}
}
}
if numberViewModel.tapped{
NumberTappedView(animation: animation, numberViewModel: numberViewModel)
.position(
x: geo.frame(in:.global).midX,
y: geo.frame(in:.global).midY
)
.onTapGesture {
withAnimation(.easeInOut(duration: 1)){
numberViewModel.selected = Number(number: 0)
numberViewModel.tapped = false
}
}
}
}
}
}
}
}
struct NumberView: View {
@ObservedObject var numberViewModel: NumberViewModel
var animation: Namespace.ID
var number: Number
var body: some View{
GroupBox{
if !(numberViewModel.selected.number == number.number){
Text("\(number.number)")
.font(.largeTitle)
.frame(width: 100, height: 100, alignment: .center)
.matchedGeometryEffect(id: number.number, in: animation)
}
}
}
}
struct Number: Identifiable {
var id = UUID()
var number: Int
}
var numbers: [Number] = [
Number(number: 1),
Number(number: 2)
]
struct NumberTappedView: View {
var animation: Namespace.ID
@ObservedObject var numberViewModel: NumberViewModel
var body: some View{
GroupBox{
Text("\(numberViewModel.selected.number)")
.font(.largeTitle)
.frame(width: 200, height: 200, alignment: .center)
.matchedGeometryEffect(id: numberViewModel.selected.number, in: animation)
}
}
}
class NumberViewModel: ObservableObject {
@Published var selected: Number = Number(number: 0)
@Published var tapped: Bool = false
}
Thank You!
I have not played with matchedGeometryEffect yet, so read the followings as as far as I tried things. There may be other better ways.
Text, is duplicated during the transition
You have two Texts, and with using matchedGeometryEffect, they both are animated.
Applying matchedGeometryEffect after frame is specified, the positions of the two in animation are different.
Please try moving matchedGeometryEffect before frame.
when I press again to get it back to its original position, no animation takes place
Seems some sort of symmetry is needed to trigger animation when you set numberViewModel.tapped to false.
Please try something like this:
struct NumberView: View {
@ObservedObject var numberViewModel: NumberViewModel
var animation: Namespace.ID
var number: Number
var body: some View{
GroupBox{
if numberViewModel.selected.number != number.number {
Text("\(number.number)")
.font(.largeTitle)
.matchedGeometryEffect(id: number.number, in: animation) //<-
.frame(width: 100, height: 100, alignment: .center)
}
}
}
}
struct NumberTappedView: View {
var animation: Namespace.ID
@ObservedObject var numberViewModel: NumberViewModel
var body: some View{
GroupBox {
if numberViewModel.tapped { //<-
Text("\(numberViewModel.selected.number)")
.font(.largeTitle)
.matchedGeometryEffect(id: numberViewModel.selected.number, in: animation) //<-
.frame(width: 200, height: 200, alignment: .center)
} //<-
}
}
}
Or you would prefer this version of NumberTappedView:
struct NumberTappedView3: View {
var animation: Namespace.ID
@ObservedObject var numberViewModel: NumberViewModel
var body: some View{
GroupBox {
if numberViewModel.tapped {
ZStack {
ForEach(numbers) { number in
if numberViewModel.selected.number == number.number {
Text("\(numberViewModel.selected.number)")
.font(.largeTitle)
.matchedGeometryEffect(id: numberViewModel.selected.number, in: animation)
.frame(width: 200, height: 200, alignment: .center)
}
}
}
}
}
}
}