我正在进行一个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) {
Group{
ScrollingViewForBtns(selectedButtonIndex: $selectedStatus,statusBtnTapped: { id in
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)")
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())
.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)
}, isSelected: Binding(
get: { selectedButtonIndex == index },
set: { _ in }))
}
}
.padding(.vertical,10)
.padding(.horizontal,2)
}
}
}
当数据数组发生变化时,我只需要更新ReferralListSubView。
有人能帮我确定我可能缺少什么吗?或者建议其他方法来优化渲染,防止每次数据更改时都重新绘制整个视图?
提前感谢您的帮助!