代码之家  ›  专栏  ›  技术社区  ›  Umair Khan

SwiftUI重新绘制数据更改的整个视图-仅需要重新绘制特定零件

  •  0
  • Umair Khan  · 技术社区  · 1 年前

    我正在进行一个SwiftUI项目,在该项目中,我使用list和ForEach显示了一个推荐列表。该列表是MyReferralsList视图的一部分,我希望优化UI渲染,以避免在数据属性更改时重新绘制整个视图。

    我已经尝试为所有相关的数据类型以及视图本身实现Equatable协议。我还尝试在ForEach中使用id参数,但并没有如预期的那样工作。

    ScrollingViewForBtns正在从API获取按钮标题,但我不会从这里调用那个API。我只是在此视图中更改myReferralsListViewModel.data。数据数组是已发布的对象,数据仅由ReferralListSubView使用。

    以下是我迄今为止所做的尝试:

    所有相关数据类型和视图均符合Equatable协议。 将id参数与ForEach一起使用以唯一标识每个元素,还将id用于包含ForEach循环的子视图。 尽管做出了这些努力,但每次我获取新数据并更新myReferralsListViewModel.data时,整个MyReferralsList视图都会被重新绘制,包括ScrollingViewForBtns和referralsListView。

    以下是MyReferralsList视图的简化代码:

    struct MyReferralsList: View, Equatable {
        
        
        @ObservedObject var myReferralsListViewModel: MyReferralsListVM
        @State private var selectedStatus: Int = 0
        @State private var dataChanged: Bool = false
        private var organizationId: Int = 0
        
        init(organizationId: Int, statusId: Int? = 0){
            self.organizationId = organizationId
            self.myReferralsListViewModel = MyReferralsListVM(referralsService:ReferralsService(networking: Networking.shared), organizationId: organizationId)
        }
        
        
        
        var body: some View {
            
            ZStack(alignment: .top) {
                Color.white
                
                VStack(alignment: .leading, spacing: 16.0) {
                    /// Buttons to filter the referrals by stauts
                    Group{
                        ScrollingViewForBtns(selectedButtonIndex: $selectedStatus,statusBtnTapped: { id in
                            
                            /**
                             - if selected index is 0 means "All" referrals are selected so we fetch all referrals passing only the organizationID
                             - Else we pass the id for the selected status and fetch the referrals for the selected Status
                             */
                            if selectedStatus == 0 {
                                
                                myReferralsListViewModel.fetchData(id: organizationId)
                            }else{
                                myReferralsListViewModel.fetchReferralForStatus(statusId: id)
                            }
                        })
                    }
                    ReferralListSubView(data: myReferralsListViewModel.data).id(dataChanged)
                    Spacer()
                }
                .padding(.vertical, 14)
                .padding(.horizontal, 15)
            }
            .foregroundColor(Color("textGray"))
            .onChange(of: myReferralsListViewModel.data) { _ in
                dataChanged.toggle()
            }
            
            
        }
        
        static func == (lhs: MyReferralsList, rhs: MyReferralsList) -> Bool {
            
            print("Comparing MyReferralsList instances...")
            print("lhs.selectedStatus: \(lhs.selectedStatus), rhs.selectedStatus: \(rhs.selectedStatus)")
            print("lhs.organizationId: \(lhs.organizationId), rhs.organizationId: \(rhs.organizationId)")
            
            // Compare data property
            print("lhs.myReferralsListViewModel.data: \(lhs.myReferralsListViewModel.data)")
            print("rhs.myReferralsListViewModel.data: \(rhs.myReferralsListViewModel.data)")
            let dataEqual = lhs.myReferralsListViewModel.data == rhs.myReferralsListViewModel.data
            print("Data is equal: \(dataEqual)")
            
            return lhs.selectedStatus == rhs.selectedStatus &&
            lhs.organizationId == rhs.organizationId &&
            lhs.myReferralsListViewModel.data == rhs.myReferralsListViewModel.data
        }
     }
    

    ReferralListSubView的代码:

    struct ReferralListSubView: View {
        let data: [Referral]
        var body: some View {
         
            
            List{
                ForEach(data, id:\.id) { referral in
                    
                NavigationLink(destination: ReferralDetailView(referral: referral)) {
                       ReferralsCells(referral: referral)
                    
                        
                        
                }
                    .frame(height: 81.0)
                    .padding(.horizontal)
                    .background(Color.white)
                    .cornerRadius(6)
                    .shadow(color: Color.black.opacity(0.2), radius: 2, x: 0, y: 2)
                    .listRowBackground(Color.clear)
                }
                
                   
            }
           
            .listRowSeparator(.hidden)
            .listStyle(PlainListStyle()) // Set the list style to PlainListStyle()
            .background(Color.white) //
            
            
            
            
        }
    }
    

    滚动ViewForBtns的代码:

    struct ScrollingViewForBtns: View {
        @ObservedObject var referralsStatusVM = ReferralsStatusViewModel(referralService: ReferralsService(networking: Networking.shared))
        @Binding  var selectedButtonIndex: Int
        
      
        
        var statusBtnTapped : (Int) -> Void
        var body: some View {
            ScrollView(.horizontal, showsIndicators: false){
                HStack(spacing: 16){
                    
                    ForEach(referralsStatusVM.buttons.indices, id:\.self) { index in
                        StatusButton(btnText: referralsStatusVM.buttons[index].name, btnAction: {
                            
                            selectedButtonIndex = index // This will help to change selected button color
                            statusBtnTapped(referralsStatusVM.buttons[index].id) // Passing the selected status id to the parent view
                            // this handles the API from parent view
                        }, isSelected: Binding(
                            get: { selectedButtonIndex == index },
                            set: { _ in }))
                    }
                }
                .padding(.vertical,10)
                .padding(.horizontal,2)
             
            }
        }
    }
    

    当数据数组发生变化时,我只需要更新ReferralListSubView。

    有人能帮我确定我可能缺少什么吗?或者建议其他方法来优化渲染,防止每次数据更改时都重新绘制整个视图?

    提前感谢您的帮助!

    0 回复  |  直到 1 年前
        1
  •  0
  •   Umair Khan    1 年前

    因此,以下是它可能在未来对某些人有所帮助的解决方案。

    在ScrollingViewForBtns中,我使用@ObservedObject,每当我点击ScrollingView ForBtns的按钮时,视图都会重新创建,因为它会更改所选按钮的颜色,因此每当我选择新按钮时,该视图都会被重新创建,@Observed Object会被触发。

    为了解决这个问题,我使用了@StateObject,因为@StateObject将确保在视图的生存期内只创建一次视图模型。