3

The goal is to have a Shimmering loading animation. Like facebook and many other apps use.

If I put this in a TabView it works as expected, but if I have the code just in a NavigationView it jump around like in the video. Why does this happen?

If someone could guide me to a solution that would be great!

Video: https://i.sstatic.net/roHm2.jpg

Here is the code:

import SwiftUI

struct ShimmeringListView: View {
    
    public var body: some View {
        return VStack {
            ShimmeringCell()
            ShimmeringCell()
        }
    }
}

private struct ShimmeringCell: View {
    @State private var opacity: Double = Constants.minOpacity
    
    public var body: some View {
        cardShimmerAnimation
    }
    
    var cardShimmerAnimation: some View {
        VStack {
            ZStack {
                backgroundRectangle
                shimmerRectangles
            }
        }
        .padding(.bottom, 20)
        .padding(.top, 16)
    }
    
    var backgroundRectangle: some View {
        Rectangle()
            .fill(
                LinearGradient(
                    gradient: Gradient(stops: [
                        Gradient.Stop(color: .lightGray, location: 0.476),
                        Gradient.Stop(color: .white, location: 0.524)
                    ]),
                    startPoint: .top,
                    endPoint: .bottom))
            .frame(height: 269)
            .cornerRadius(Constants.cornerRadius)
            .padding(.horizontal, 15)
    }
    
    var shimmerRectangles: some View {
        VStack {
            oneRectangle
                .frame(height: 28)
                .padding(.trailing, 73)
                .padding(.bottom, 2)
            oneRectangle
                .frame(height: 20)
                .padding(.trailing, 188)
                .padding(.bottom, 2)
            oneRectangle
                .frame(height: 20)
                .padding(.trailing, 130)
                .padding(.bottom, 20)
            oneRectangle
                .frame(height: 20)
                .padding(.trailing, 188)
                .padding(.vertical, 8)
            oneRectangle
                .frame(height: 20)
                .padding(.trailing, 116)
            Divider()
                .padding(.horizontal, 32)
                .padding(.vertical, 8)
            oneRectangle
                .frame(height: 20)
                .padding(.trailing, 52)
        }
    }
    
    var oneRectangle: some View {
        let baseAnimation = Animation.easeInOut(duration: Constants.duration)
        let repeatedAnimation = baseAnimation.repeatForever(autoreverses: true)
        
        return RoundedRectangle(cornerRadius: Constants.cornerRadius)
            .fill(Color.loadingColor)
            .opacity(opacity)
            .transition(.opacity)
            .padding(.leading, 31)
            .animation(repeatedAnimation)
            .onAppear {
                self.opacity = Constants.maxOpacity
            }
    }
}

private struct Constants {
    static let duration: Double = 0.9
    static let minOpacity: Double = 0.25
    static let maxOpacity: Double = 1.0
    static let cornerRadius: CGFloat = 6.0
}
1
  • Cool animation free from apple! Commented Nov 3, 2021 at 21:40

1 Answer 1

10

You just need two small changes (see comments inlined in the code):

return RoundedRectangle(cornerRadius: Constants.cornerRadius)
    .fill(Color.loadingColor)
    .opacity(opacity)
    .transition(.opacity)
    .padding(.leading, 31)
    .animation(repeatedAnimation, value: opacity) //<-- Add value: parameter
    .onAppear {
        DispatchQueue.main.async { //<-- wrap in DispatchQueue.main.async
            self.opacity = Constants.maxOpacity
        }
    }

In terms of why: For the animation modifier, use of the animation without the value parameter is deprecated and we should expect to use value:. For the DispatchQueue, my best guess is that the NavigationView has an implicit animation attached and we need to start this animation on the next run loop to make sure that it's separate.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.