I am a newbie to SwiftUI and I am experimenting with animations. The animation styling for my backside tabs work as anticipated once I press on every icon in addition to if I swipe on the display screen to change between the tabs. However once I attempt one thing comparable for my prime tabs, the slide animation for transitioning the chosen background coloration would not work once I swipe between the nested screens, it solely works once I press on the tab identify (button).
I’ve discovered a publish that is additionally having animation points utilizing GeometryReader nevertheless it would not appear to match my use case.
The one distinction between my prime and backside tabs is that the highest tabs use a GeometryReader, is that this a typical points? Are there any workarounds for this drawback? Any steering on the best way to clear up that is enormously appreciated.
BottomTabs.swift
import SwiftUI;
struct BottomTabs: View {
@Binding var tab: Int;
var physique: some View {
HStack {
Button(motion: {
withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
tab = 0;
}
}) {
VStack {
Picture(systemName: "baseball.fill").resizable()
.scaledToFit()
.body(width: 16, peak: 16)
.foregroundStyle(
LinearGradient(colours: [(tab == 0) ? .green : .white, (tab == 0) ? .purple : .white],
startPoint: .prime, endPoint: .backside)
).scaleEffect((tab == 0) ? 1.2 : 1)
.animation(.easeInOut(length: 0.3), worth: tab);
Circle()
.fill(.yellow)
.body(width: (tab == 0) ? 5 : 0, peak: (tab == 0) ? 5 : 0)
.padding(.prime, (tab == 0) ? 2 : 0)
.opacity((tab == 0) ? 1 : 0)
.animation(.easeInOut(length: 0.3), worth: tab);
}
}.padding(.horizontal, 10)
Button(motion: {
withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
tab = 1;
}
}) {
VStack {
Picture(systemName: "coronary heart.fill").resizable()
.scaledToFit()
.body(width: 16, peak: 16)
.foregroundStyle(
LinearGradient(colours: [(tab == 1) ? .green : .white, (tab == 1) ? .purple : .white],
startPoint: .prime, endPoint: .backside)
)
.scaleEffect((tab == 1) ? 1.2 : 1)
.animation(.easeInOut(length: 0.3), worth: tab);
Circle().fill(.yellow)
.body(width: (tab == 1) ? 5 : 0, peak: (tab == 1) ? 5 : 0)
.opacity((tab == 1) ? 1 : 0)
.padding(.prime, (tab == 1) ? 2 : 0)
.animation(.easeInOut(length: 0.3), worth: tab);
}
}.padding(.horizontal, 10)
Button(motion: {
withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
tab = 2;
}
}) {
VStack {
Picture(systemName: "basketball.fill").resizable()
.scaledToFit()
.body(width: 16, peak: 16)
.foregroundStyle(
LinearGradient(colours: [(tab == 2) ? .green : .white, (tab == 2) ? .purple : .white],
startPoint: .prime, endPoint: .backside)
)
.scaleEffect((tab == 2) ? 1.2 : 1)
.animation(.easeInOut(length: 0.3), worth: tab);
Circle().fill(.yellow)
.body(width: (tab == 2) ? 5 : 0, peak: (tab == 2) ? 5 : 0)
.opacity((tab == 2) ? 1 : 0)
.padding(.prime, (tab == 2) ? 2 : 0)
.animation(.easeInOut(length: 0.3), worth: tab);
}
}.padding(.horizontal, 10)
}.body(peak: 30)
.padding(.vertical, 20)
.padding(.horizontal, 30)
.background(
RoundedRectangle(cornerRadius: 20).fill(.black)
).padding(.backside, 10);
}
}
ContentView.swift
import SwiftUI;
struct ContentView: View {
@State personal var tab: Int = 0;
@State personal var tag: Int = 0;
var physique: some View {
ZStack(alignment: .backside) {
TabView(choice: $tab) {
NavigationStack {
ZStack(alignment: .prime) {
TabView(choice: $tag) {
ScrollView(showsIndicators: false) {
LazyVStack {
ForEach(0..<500, id: .self) { i in
Textual content("(i + 1). TRENDING HOME SCREEN");
}
}
}.tag(0)
ScrollView(showsIndicators: false) {
LazyVStack {
ForEach(0..<500, id: .self) { i in
Textual content("(i + 1). FOLLOWING HOME SCREEN");
}
}
}.tag(1)
ScrollView(showsIndicators: false) {
LazyVStack {
ForEach(0..<500, id: .self) { i in
Textual content("(i + 1). EVENTS HOME SCREEN");
}
}
}.tag(2)
}.tabViewStyle(PageTabViewStyle(indexDisplayMode: .by no means))
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Button(motion: {
}) {
Picture(systemName: "basketball.fill")
.resizable()
.scaledToFit()
.body(width: 50, peak: 50)
.foregroundColor(.black);
}
.padding(.prime, 50)
.padding(.main, 20);
}
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(motion: {
}) {
Picture(systemName: "baseball.fill")
.resizable()
.scaledToFit()
.body(width: 50, peak: 50)
.foregroundColor(.black);
}.padding(.prime, 50)
.padding(.trailing, 20);
}
}
.toolbarBackground(.hidden, for: .navigationBar)
TopTabs(tab: $tag).padding(.prime, 60)
}
.ignoresSafeArea()
}
.tag(0)
ScrollView(showsIndicators: false) {
LazyVStack {
ForEach(0..<1000, id: .self) { i in
Textual content("(i + 1). LIKES SCREEN");
}
}
}.tag(1)
ScrollView(showsIndicators: false) {
LazyVStack {
ForEach(0..<2000, id: .self) { i in
Textual content("(i + 1). SPORTS SCREEN");
}
}
}.tag(2)
}.tabViewStyle(PageTabViewStyle(indexDisplayMode: .by no means));
BottomTabs(tab: $tab);
}
.ignoresSafeArea()
}
}
#Preview {
ContentView();
}
TopTabs.swift
import SwiftUI
struct TopTabs: View {
@Binding var tab: Int
@Namespace personal var animation
var physique: some View {
GeometryReader { geometry in
let containerWidth = geometry.dimension.width * 0.9
let tabWidth = containerWidth / 3
ZStack(alignment: .main) {
RoundedRectangle(cornerRadius: 50)
.fill(Shade.yellow)
.body(width: tabWidth, peak: 40)
.matchedGeometryEffect(id: "tabBackground", in: animation)
.offset(x: CGFloat(tab) * tabWidth)
HStack(spacing: 0) {
Button(motion: {
withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
tab = 0
}
}) {
Textual content("Trending")
.font(Font.customized("Gilroy-Medium", dimension: 14))
.foregroundColor(tab == 0 ? .black : .white)
}
.body(width: tabWidth, peak: 40)
Button(motion: {
withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
tab = 1
}
}) {
Textual content("Following")
.font(Font.customized("Gilroy-Medium", dimension: 14))
.foregroundColor(tab == 1 ? .black : .white)
}
.body(width: tabWidth, peak: 40)
Button(motion: {
withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) {
tab = 2
}
}) {
Textual content("Occasions")
.font(Font.customized("Gilroy-Medium", dimension: 14))
.foregroundColor(tab == 2 ? .black : .white)
}
.body(width: tabWidth, peak: 40)
}
}
.body(width: containerWidth, peak: 40)
.background(
RoundedRectangle(cornerRadius: 50)
.fill(Shade.grey)
)
.masks(RoundedRectangle(cornerRadius: 50))
.padding(.prime, 20)
.body(maxWidth: .infinity)
}
.transition(AnyTransition.opacity.animation(.easeInOut(length: 0.2)))
}
}