Hello all!
In my project, I have created a swipeable carousel view that changes a published property (currentHoleIdx) in my ViewModel that I use to programmatically change a TabView.
I want the TabView to animate when the index changes, and it does, but only with the deprecated .animated(.easeInOut). I have tried .animation(.easeInOut, value:model.currentHoleIdx) and also tried withAnimation when I change the currentHoleIdx property. I will do my best to post the code to show what I have done. Thanks in advance for any help!
TabView with animation
VStack {
ZStack(alignment: .top) {
// Mini Scorecard
MiniScorecard(scorecard:model.scorecards[model.currentScorecardIdx!], teams: model.scorecards[model.currentScorecardIdx!].teams)
.coordinateSpace(name: "mini")
// .opacity(showScore ? 1 : 0)
.background(Color("card"))
.background(
GeometryReader { geo in
Color.clear.onAppear {
showScoreOffset = geo.size.height
// print(geo.size.height)
}
}
)
// Scorecard TabView
TabView(selection: $model.currentHoleIdx,
content: {
ForEach(0..<model.scorecards[model.currentScorecardIdx!].holes.count) {idx in
let scorecard = model.scorecards[model.currentScorecardIdx!]
if scorecard.formatType == "individual" {
if scorecard.gameID == "Standard" {
IndivStandardScoreScrollview(idx:idx)
.tag(idx)
.animation(.easeInOut, value: model.currentHoleIdx) // // does not work does not work with idx or model.currentHoleIdx
}
} else {
if scorecard.gameID == "Standard" {
TeamStandardScoreScrollview(holeIdx:idx)
.tag(idx)
.animation(.easeInOut, value: idx) // does not work does not work with idx or model.currentHoleIdx
}
}
}
Color.clear
.tag(model.scorecards[model.currentScorecardIdx!].holes.count)
}).tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.background(Color("card"))
.offset(y:showScore ? showScoreOffset : 0)
.animation(.easeInOut, value: showScore)
.animation(.easeInOut, value: scorecardOffset)
// .animation(.easeInOut, value: model.currentHoleIdx) // does not work
}.frame(width:UIScreen.main.bounds.width - 30)
}
.offset(y: scorecardOffset)
.offset(y: model.currentHoleIdx < model.scorecards[model.currentScorecardIdx!].holes.count ? 0 : scorecardHeight)
.padding(.top)
.padding(.horizontal, 15)
.animation(.easeInOut, value: scorecardOffset)
.animation(.easeInOut, value: model.currentHoleIdx) // does not work!!!
// .animation(.easeInOut) // Only thing that works! when model.currentHoleIdx changes from drag gesture
.overlay(
GeometryReader { geo in
Color.clear.onAppear {
scorecardHeight = geo.size.height
}
}
)
Swipeable carousel drag gesture - withAnimation when interacting with model.currentHoleIdx
return HStack(alignment: .center, spacing: spacing) {
items
}
.offset(x: CGFloat(calcOffset), y: 0)
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .named("canvas")).updating($screenDrag) { dragValue, gestureState, transaction in
// self.model.screenDrag = Float(currentState.translation.width)
// gestureState == screenDrag
gestureState = dragValue.translation
}.onEnded { value in
// screenDrag = 0 - Don't need this, bc GestureState automatically resets back to initial value
// Swipe to next hole
if (value.translation.width < -15) && self.model.currentHoleIdx < Int(numberOfItems) - 1 {
// calculate currentholeidx based on swipe width and cardwidth
let calculatedHoleIdx = self.model.currentHoleIdx - Int(floor(value.translation.width/(cardWidth + spacing + 10)))
withAnimation { // withAnimation Here
self.model.currentHoleIdx = calculatedHoleIdx > Int(numberOfItems - 1) ? Int(numberOfItems - 1) : calculatedHoleIdx
}
let impactMed = UIImpactFeedbackGenerator(style: .medium)
impactMed.impactOccurred()
}
// Swipe to previous hole
if (value.translation.width > 15) && self.model.currentHoleIdx > 0 {
let calculatedHoleIdx = self.model.currentHoleIdx - Int(ceil((value.translation.width/(cardWidth + spacing + 10))))
withAnimation { // withAnimation Here
self.model.currentHoleIdx = calculatedHoleIdx < 0 ? 0 : calculatedHoleIdx
}
self.model.currentHoleIdx = calculatedHoleIdx < 0 ? 0 : calculatedHoleIdx
let impactMed = UIImpactFeedbackGenerator(style: .medium)
impactMed.impactOccurred()
}
I feel like i've tried every possible iteration of .animation and withAnimation. The only one that works is using the deprecated .animation() modifier. Hope to hear from someone soon. thanks again!
Can you show enough code to run and test? Hard to say something with just fragments of code.
Ahh, I'm not sure how to replicate the issue without sharing the whole code base.
I will add that the animation does work with a simple Text View but not with the IndivStandardScoreScrollview(idx:idx) or TeamStandardScoreScrollview(holeIdx:idx), which are Scrollviews. Does that have anything to do with it? Thanks for trying OOPer