I am struggling to grasp why .matchedGeometryEffect()
is behaving this manner:
I am attempting to animate a tab’s peak to completely different heights, primarily based on its content material. To take action I am utilizing .matchedGeometryEffect()
on the tab background as observe:
struct ContentView: View {
// MARK: - Properties
@State personal var selectedTab: Tab = .cost
@Namespace var namespace
// MARK: - Physique
var physique: some View {
ZStack(alignment: .backside) {
// Background coloration
Colour.gray6
.ignoresSafeArea()
// Tab
swap selectedTab {
case .dashboard:
DashboardTabView(namespace: namespace)
case .transactions:
TransactionsTabView(namespace: namespace)
case .cost:
PaymentTabView(namespace: namespace)
case .providers:
ServicesTabView(namespace: namespace)
case .settings:
SettingsTabView(namespace: namespace)
}
}
.safeAreaInset(edge: .backside, spacing: 0) {
// Tab bar
TabBar(selectedTab: $selectedTab)
}
}
}
struct DashboardTabView: View {
// MARK: - Properties
let namespace: Namespace.ID
// MARK: - Physique
var physique: some View {
TabViewContainer(namespace: namespace) {
Textual content("Dashboard Tab")
.foregroundStyle(.black)
}
}
}
struct PaymentTabView: View {
// MARK: - Properties
let namespace: Namespace.ID
// MARK: - Physique
var physique: some View {
TabViewContainer(namespace: namespace) {
HStack(spacing: 16) {
CTAButton(title: "Obtain", icon: "arrow.down.sq.") { }
.matchedGeometryEffect(id: "one", in: namespace)
CTAButton(title: "Pay", icon: "dollarsign.arrow.circlepath", coloration: Colour(hex: "#F1D302")) { }
.matchedGeometryEffect(id: "two", in: namespace)
}
.body(peak: 52)
.padding(.horizontal)
}
}
}
struct TabViewContainer<Content material: View>: View {
// MARK: - Init
init(
cornerRadii: RectangleCornerRadii = .init(topLeading: 21, bottomLeading: 0, bottomTrailing: 0, topTrailing: 21),
namespace: Namespace.ID,
@ViewBuilder content material: @escaping () -> Content material
) {
self.cornerRadii = cornerRadii
self.namespace = namespace
self.content material = content material
}
// MARK: - Properties
let namespace: Namespace.ID
@State personal var childHeight: CGFloat = 0
@ViewBuilder var content material: () -> Content material
personal let cornerRadii: RectangleCornerRadii
// MARK: - Physique
var physique: some View {
ZStack(alignment: .backside) {
// Background coloration
Colour.gray0
.matchedGeometryEffect(id: "tab_background", in: namespace)
.body(peak: childHeight)
.clipShape(UnevenRoundedRectangle(cornerRadii: cornerRadii, fashion: .steady))
// Content material
content material()
.padding(.vertical)
.overlay {
GeometryReader { proxy in
Colour.clear
.activity(id: proxy.dimension.peak) {
childHeight = max(proxy.dimension.peak, 0)
}
}
}
}
}
}
I can see the animation on the display nevertheless it seems to be like as an alternative of animating from the origin view to the vacation spot view immediately, it reset the peak to 0 first, inflicting the animation to interrupt.
Would anyone perceive why it is behaving this manner by any likelihood? Am I lacking one thing within the implementation?
This is what it seems to be like proper now. I slowed down the animation on Xcode simulator so you possibly can see it extra clearly:
Hyperlink to Xcode simulator demo
I am attempting to show some tabs primarily based on the selectedTab
worth within the ContentView
. These tabs (DashboardTabView
, PaymentTabView
, and so forth) all leverage the identical TabViewContainer
that comprises a Colour.gray0
view that makes use of .matchedGeometryEffect(id: "tab_background", in: namespace)
.
My expectation is that these tab will transition peak seamlessly from one tab to a different, and actually after I arduous code the values for the peak the animation is working propertly.
The issue arises after I use GeometryReader
to retrieve the peak of the content material()
youngster view. Then the animation breaks.