[Need help!] Performance Issue: Laggy Scroll in SwiftUI App with ScrollView

I'm using ScrollView to display my course list, and each card in the list, named 'CourseCardView' as shown in the code. When I test it on both the simulator and a physical device, I notice that scrolling is not smooth and feels laggy. I'm not sure how to address this issue. Here is my code:

//
//  CourseListView.swift
//  LexueSwiftUI
//
//  Created by bobh on 2023/9/3.
//

import SwiftUI

struct CourseCardView: View {
    let cardHeight: CGFloat = 150
    let cardCornelRadius: CGFloat = 10
    let cardHorizontalPadding: CGFloat = 10
    
    @State var courseName = "course name"
    @State var courseCategory = "category name"
    @State var progress = 66
    var body: some View {
        ZStack {
            Image("default_course_bg2")
                .resizable()
                .blur(radius: 5, opaque: true)
                .cornerRadius(cardCornelRadius)
                .padding(.horizontal, cardHorizontalPadding)
                .frame(height: cardHeight)
            Color.white
                .cornerRadius(cardCornelRadius)
                .padding(.horizontal, cardHorizontalPadding)
                .frame(height: cardHeight)
                .opacity(0.1)
            
            VStack(alignment: .leading, spacing: 2) {
                Spacer()
                Text(courseName)
                    .bold()
                    .font(.title)
                    .foregroundColor(.white)
                    .lineLimit(1)
                    .shadow(color: .black.opacity(0.5), radius: 5, x: 0, y: 2)
                    .padding(.leading, 10)
                
                Text(courseCategory)
                    .bold()
                    .font(.headline)
                    .foregroundColor(.white)
                    .lineLimit(1)
                    .shadow(color: .black.opacity(0.5), radius: 5, x: 0, y: 2)
                    .padding(.leading, 10)
                    .padding(.bottom, 5)
                ProgressView(value: Double(progress) / 100.0)
                    .padding(.horizontal, 10)
                    .padding(.bottom, 10)
                    .accentColor(.white)
                    .shadow(color: .black.opacity(0.3), radius: 5, x: 0, y: 2)
            }
            .frame(height: cardHeight)
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding(.horizontal, cardHorizontalPadding)
            VStack {
                HStack {
                    Spacer()
                    Button(action: {
                        
                    }) {
                        Image(systemName: "star")
                            .foregroundColor(.white)
                            .font(.system(size: 24).weight(.regular))
                            .shadow(color: .black.opacity(0.3), radius: 5, x: 0, y: 2)
                    }
                    .padding(.trailing, 10)
                    .padding(.top, 10)
                }
                Spacer()
            }
            .frame(height: cardHeight)
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding(.horizontal, cardHorizontalPadding)
        }
        .shadow(color: .black.opacity(0.3), radius: 5, x: 0, y: 2)
    }
}

private struct ListView: View {
    @Binding var courses: [CourseShortInfo]
    @Binding var isRefreshing: Bool
    
    @Environment(\.refresh) private var refreshAction
    @ViewBuilder
    var refreshToolbar: some View {
        if let doRefresh = refreshAction {
            if isRefreshing {
                ProgressView()
            } else {
                Button(action: {
                    Task{
                        await doRefresh()
                    }
                }) {
                    Image(systemName: "arrow.clockwise")
                }
            }
        }
    }
    var body: some View {
        VStack {
            ScrollView(.vertical) {
                LazyVStack(spacing: 20){
                    ForEach(courses) { item in
                        CourseCardView(courseName: item.shortname!, courseCategory: item.coursecategory!, progress: item.progress!)
                        .listRowSeparator(.hidden)
                    }
                }
            }
            .toolbar {
                refreshToolbar
            }
        }
    }
}

struct CourseListView: View {
    @State private var courseList = GlobalVariables.shared.courseList
    @State var isRefreshing: Bool = false
    @State var searchText: String = ""
    func testRefresh() async {
        Task {
            isRefreshing = true
            Thread.sleep(forTimeInterval: 1.5)
            withAnimation {
                isRefreshing = false
            }
        }
    }
    var body: some View {
        NavigationView {
            VStack {
                ListView(courses: $courseList, isRefreshing: $isRefreshing)
                    .refreshable {
                        print("refresh")
                        await testRefresh()
                    }
            }
            .searchable(text: $searchText, prompt: "Search the course")
            .navigationTitle("Course")
            .navigationBarTitleDisplayMode(.large)
        }
    }
}

struct CourseListView_Previews: PreviewProvider {
    static var previews: some View {
        CourseListView()
    }
}

The global variable code:

import Foundation
import SwiftUI

class GlobalVariables {
    static let shared = GlobalVariables()
    @Published var isLogin = true
    @Published var courseList: [CourseShortInfo] = [
        CourseShortInfo(id: 11201, shortname: "数据结构与C++程序设计", progress: 66, coursecategory: "自动化学院"),
        CourseShortInfo(id: 11202, shortname: "数值分析", progress: 20, coursecategory: "数学学院"),
        CourseShortInfo(id: 11203, shortname: "数据结构与C++程序设计", progress: 66, coursecategory: "自动化学院"),
        CourseShortInfo(id: 11204, shortname: "数值分析", progress: 20, coursecategory: "数学学院"),
        CourseShortInfo(id: 11205, shortname: "数据结构与C++程序设计", progress: 66, coursecategory: "自动化学院"),
        CourseShortInfo(id: 11206, shortname: "数值分析", progress: 20, coursecategory: "数学学院"),
        CourseShortInfo(id: 11207, shortname: "数据结构与C++程序设计", progress: 66, coursecategory: "自动化学院"),
        CourseShortInfo(id: 11208, shortname: "数值分析", progress: 20, coursecategory: "数学学院"),
        CourseShortInfo(id: 11209, shortname: "数据结构与C++程序设计", progress: 66, coursecategory: "自动化学院"),
        CourseShortInfo(id: 11210, shortname: "数值分析", progress: 20, coursecategory: "数学学院")
    ]
    
    var debugMode = true

    private init() {
        
    }
}

Replies

I suspect it might be due to the excessive use of shadow effects on each of my cards?