I'm new to swift and Viewbuilder and I am trying to use variables in the Viewbuilder correctly. The objective of this code is to reveal the answer to a question using an animation. The question is presented on a card and when the button on the card is tapped the answer is revealed. It all works well if I use one card however when I put multiple cards in a scrollView and tap the button, all of the answers are presented at the same time. I believe this is because I am using one show variable for all the cards (therefore it expands all the cards when at once). I need to have a show variable for each card but am struggling to implement this. How would I go about this?
The code for my View is:
struct Home: View {
// MARK: Animation Properties
@State var expandTheCard: Bool = false
@State var bottomLiquidView: AnimationView = AnimationView(name: "LiquidWave", bundle: .main)
@State var topLiquidView: AnimationView = AnimationView(name: "LiquidWave", bundle: .main)
// Avoiding Multitapping
@State var isfinished: Bool = false
var body: some View {
NavigationView {
ScrollView {
LazyVStack(spacing: 200) {
// MARK: Animated Liquid Transition Cards
LiquidCard(title: "What Is Hello In French?", subTitle: "", detail: "Hello In French Is...", description: "Bonjour!"){
if isfinished{return}
isfinished = true
// Animating Lottie View with little Delay
DispatchQueue.main.asyncAfter(deadline: .now() + (expandTheCard ? 0 : 0.2)) {
// So that it will finish soon...
bottomLiquidView.play(fromProgress: expandTheCard ? 0 : 0.45, toProgress: expandTheCard ? 0.6 : 0)
topLiquidView.play(fromProgress: expandTheCard ? 0 : 0.45, toProgress: expandTheCard ? 0.6 : 0){status in
isfinished = false
}
}
// Toggle Card
withAnimation(.interactiveSpring(response: 0.7, dampingFraction: 0.8, blendDuration: 0.8)){
expandTheCard.toggle()
}
}
.frame(maxHeight: .infinity)
}
}
}
}
The code for the ViewBuilder is:
@ViewBuilder
func LiquidCard(title: String,subTitle: String,detail: String,description: String,color: SwiftUI.Color = Color("Blue"),onExpand: @escaping ()->())->some View{
ZStack{
VStack(spacing: 20){
Text(title)
.font(.largeTitle.bold())
.foregroundColor(.white)
HStack(spacing: 10){
Text(subTitle)
.fontWeight(.semibold)
.foregroundColor(.white)
}
}
.padding()
.frame(maxWidth: .infinity)
.frame(height: expandTheCard ? 250 : 350)
.background{
GeometryReader{proxy in
let size = proxy.size
let scale = size.width / 1000
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
// To get Custom Color simply use Mask Technique
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
.mask {
ResizableLottieView(lottieView: $bottomLiquidView)
// Scaling it to current Size
.scaleEffect(x: scale, y: scale, anchor: .leading)
}
.rotationEffect(.init(degrees: 180))
.offset(y: expandTheCard ? size.height / 1.43 : 0)
}
}
// MARK: Expand Button
.overlay(alignment: .bottom) {
Button {
onExpand()
} label: {
Image(systemName: "chevron.down")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 30.0, height: 30.0)
.font(.title3.bold())
.foregroundColor(color)
.padding(30)
.background(.white,in: RoundedRectangle(cornerRadius: 20, style: .continuous))
// Shadows
.shadow(color: .black.opacity(0.15), radius: 5, x: 5, y: 5)
.shadow(color: .black.opacity(0.15), radius: 5, x: -5, y: -5)
}
.padding(.bottom,-25)
}
.zIndex(1)
// MARK: Expanded Card
VStack(spacing: 20){
Text(detail)
.font(.largeTitle.bold())
Text(description)
.font(.title3)
.lineLimit(3)
.padding(.horizontal)
.multilineTextAlignment(.center)
}
.foregroundColor(.white)
.padding(.vertical,40)
.padding(.horizontal)
.frame(maxWidth: .infinity)
.background{
GeometryReader{proxy in
let size = proxy.size
let scale = size.width / 1000
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
// To get Custom Color simply use Mask Technique
RoundedRectangle(cornerRadius: 35, style: .continuous)
.fill(color)
.mask {
ResizableLottieView(lottieView: $topLiquidView)
// Scaling it to current Size
.scaleEffect(x: scale, y: scale, anchor: .leading)
}
.offset(y: expandTheCard ? -size.height / 1.2 : -size.height / 1.4)
}
}
.zIndex(0)
.offset(y: expandTheCard ? 280 : 0)
}
.offset(y: expandTheCard ? -120 : 0)
}