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 Text
s, 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)
}
}
}
}
}
}
}