skip to Main Content

I have a LottieAnimationView along with some other components inside ScrollView, animation is only supposed to play once.

@State var messageBannerVisisbility: Bool = false

var body: some View {
    VStack(alignment: .center) {
        TrackableScrollView {
            VStack(alignment: .center) {
                headerView(components: header)
                contentView(components: body)
            }
        } onScrollingStarted: {
            hideMessageBanner()
        } onScrollingFinished: {
            showMessageBanner()
        }
        .animation(nil)
        
        footerView(footer: content.footer)
        
    }
    .onAppear {
        showMessageBanner()
    }
}

@ViewBuilder private func footerView(footer: SignupPageV2Footer) -> some View {
    VStack(alignment: .leading, spacing: 0) {
        if let message = footer.message, messageBannerVisisbility {
            footerMessageView(from: message)
        }
        
        HStack(alignment: .center, spacing: 8) {
            Label(footer.signupAction.info.stripHTMLTags)
                .textColor(.secondary)
                .frame(width: 115, alignment: .leading)
            
            LozengeButton(title: footer.signupAction.label, isLoading: $viewModel.isPaymentInProgress) {
                viewModel.startPayment()
            }
            .accessibility(identifier: "subscribe_button")
        }
        .padding([.horizontal, .top], 16)
        .padding(.bottom, 8)
        .background(Color.white)
    }
    .background(Color.white.edgesIgnoringSafeArea(.bottom).elevation(.LightBackground.small))
}

@ViewBuilder private func footerMessageView(from message: SignupPageV2FooterMessage) -> some View {
    message.build { deeplink in
        viewModel.handleDeeplink(deeplink)
    } processEvent: { event in
        viewModel.handleEvent(event)
    }
    .transition(.move(edge: .bottom).combined(with: .opacity))
}

private func showMessageBanner() {
    if messageBannerVisisbility == true { return }
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
        withAnimation(.easeIn(duration: 0.3)) {
            messageBannerVisisbility = true
        }
    }
}

private func hideMessageBanner() {
    if messageBannerVisisbility == false { return }
    withAnimation(.easeIn(duration: 0.3)) {
        messageBannerVisisbility = false
    }
}

TrackableScrollView is my custom implementation to have scroll Start and End callbacks to show/hide footerMessageView, which is inside footerView, while scrolling.

The issue I am facing is, whenever I scroll LottieAnimationView seems to be resetting and therefore it plays the animation again everytime I scroll.

How do I just update footerView so that animation is only played once after the screen loads?

2

Answers


  1. I have seen similar problems and I have been able to solve them by using an id on the view that is supposed to animate only once.
    The best way is to add the id as something that is unique to that instance of the view, normally the id would be based on the data you give the view.

    .id(item.id)
    

    This will prevent the view from being re-drawn when there are changes in other components.

    Login or Signup to reply.
  2. Try this:

    (I cannot check this code will work or not)

    struct SomeView1: View {
        @State var messageBannerVisisbility: Bool = false
    
        var body: some View {
            VStack(alignment: .center) {
                ScrollViewTest(messageBannerVisisbility: messageBannerVisisbility)
            
                footerView(footer: content.footer)
            }
            .onAppear {
                showMessageBanner()
            }
        }
    }
    
    struct ScrollViewTest: View {
        @Binding var messageBannerVisisbility: Bool
        
        var body: some View {
            TrackableScrollView {
                VStack(alignment: .center) {
                    headerView(components: header)
                    contentView(components: body)
                }
            } onScrollingStarted: {
                withAnimation(.easeIn(duration: 0.3)) {
                    messageBannerVisisbility = true
                }
            } onScrollingFinished: {
                withAnimation(.easeIn(duration: 0.3)) {
                    messageBannerVisisbility = false
                }
            }
            .animation(nil)
        }
    }
    
    @ViewBuilder private func footerView(footer: SignupPageV2Footer) -> some View {
        VStack(alignment: .leading, spacing: 0) {
            if let message = footer.message, messageBannerVisisbility {
                footerMessageView(from: message)
            }
            
            HStack(alignment: .center, spacing: 8) {
                Label(footer.signupAction.info.stripHTMLTags)
                    .textColor(.secondary)
                    .frame(width: 115, alignment: .leading)
                
                LozengeButton(title: footer.signupAction.label, isLoading: $viewModel.isPaymentInProgress) {
                    viewModel.startPayment()
                }
                .accessibility(identifier: "subscribe_button")
            }
            .padding([.horizontal, .top], 16)
            .padding(.bottom, 8)
            .background(Color.white)
        }
        .background(Color.white.edgesIgnoringSafeArea(.bottom).elevation(.LightBackground.small))
    }
    
    @ViewBuilder
    private func footerMessageView(from message: SignupPageV2FooterMessage) -> some View {
        message.build { deeplink in
            viewModel.handleDeeplink(deeplink)
        } processEvent: { event in
            viewModel.handleEvent(event)
        }
        .transition(.move(edge: .bottom).combined(with: .opacity))
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search