SwiftUI MatchedGeometryEffect not working when I move backward in position

80 Views Asked by At

I have the matchedGeo working moving forward but for some reason, it doesn't work if I select a text moving backward. I have included a screenshot gif so you can see the issue.


import SwiftUI

enum CapsuleNavTitles: String, CaseIterable {
    case Discussion = "Discussion"
    case About = "About"
    case Memebers =  "Members"
}

struct CapsuleNavAnimationComponent: View {
    
    @State var currentNav: CapsuleNavTitles = .Discussion
    @Namespace var animation
    
    var body: some View {
        CapsuleNav()
    }
    
    @ViewBuilder
    func CapsuleNav() -> some View {
        HStack() {
            ForEach(CapsuleNavTitles.allCases, id: \.rawValue) { nav in
                Text(nav.rawValue)
                    .padding(EdgeInsets(top: 9, leading: 17, bottom: 9, trailing: 17))
                    .background(currentNav == nav ? Capsule()
                        .fill(Color.buttonColor)
                        .matchedGeometryEffect(id: "CapsuleNavTitles", in: animation) : nil)
                    .foregroundColor(currentNav == nav ? .white : .black)
                    .onTapGesture {
                        withAnimation(.easeInOut) {
                            currentNav = nav
                        }
                    }
            }
        }
        .background(Capsule().fill(Color.mainColor))
    }
}

Gif of the issues of my matchedGeo

1

There are 1 best solutions below

0
On

There might be a more reliable way to use matchedGeometryEffect. I would suggest, the Capsule that you are applying in the background should be matched to one of the nav items. The item that has been selected is treated as the source, the Capsule itself is never the source.

The version below has the following changes:

  • A matchedGeometryEffeect modifier has been added to each of the nav items, using the enum as id.
  • The nav item matching the selection is identified as the source.
  • The Capsule is now an overlay over the background.
  • The Capsule adopts the current selection as its id for the matchedGeometryEffect.
  • The Capsule always has isSource = false.

Here you go:

func CapsuleNav() -> some View {
    HStack {
        ForEach(CapsuleNavTitles.allCases, id: \.rawValue) { nav in
            Text(nav.rawValue)
                .padding(EdgeInsets(top: 9, leading: 17, bottom: 9, trailing: 17))
                .foregroundColor(currentNav == nav ? .white : .black)
                .matchedGeometryEffect(id: nav, in: animation, isSource: currentNav == nav)
                .onTapGesture {
                    withAnimation(.easeInOut) {
                        currentNav = nav
                    }
                }
        }
    }
    .background(
        Capsule().fill(Color.mainColor)
            .overlay {
                Capsule()
                    .fill(Color.buttonColor)
                    .matchedGeometryEffect(id: currentNav, in: animation, isSource: false)
            }
    )
}