使用 NSFetchRequest 和 NSPredicate 时,SwiftUI 列表更新缓慢

Posted

技术标签:

【中文标题】使用 NSFetchRequest 和 NSPredicate 时,SwiftUI 列表更新缓慢【英文标题】:SwiftUI List is slow to update when using NSFetchRequest and NSPredicate 【发布时间】:2019-12-20 14:41:43 【问题描述】:

我的列表过滤在当前设置下很慢:

struct TechList: View 

@FetchRequest
var devices: FetchedResults<HearingDevice>


@State private var selectedDevice: String?
@ObservedObject var model = Model()

init(predicate: NSPredicate?) 
    let request: NSFetchRequest<HearingDevice> = HearingDevice.fetchRequest()
    request.sortDescriptors = []
    if let predicate = predicate 
        request.predicate = predicate
    
    _devices = FetchRequest<HearingDevice>(fetchRequest: request)


var body: some View 
    List
        ForEach(devices, id: \.self)  device in
            VStack(alignment: .center) 
                HStack
                    Text(device.model ?? "Unknown" + " ")
                        .font(.system(size: 17))
                        .fontWeight(.medium)
                        .foregroundColor(self.selectedDevice == device.model ? Color.white:Color.init(hex: "47535B"))
                        .multilineTextAlignment(.leading)
                        .padding(.leading)
                    Spacer()
                
            
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 60)
            .background(self.selectedDevice == device.model ? Color.init(hex: "666699"):Color.init(hex: "F6F6F6"))
            .cornerRadius(7.0)
            .onTapGesture 
                self.model.deviceModel = device.model!
                withAnimation(.spring())
                    self.selectedDevice = device.model!
                
            
        
    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        .padding(.top, 170.0)
        .padding(.bottom, 23.0)
        .padding(.horizontal, 10.0)
     
 

我正在使用 NSPredicate 过滤我的获取请求。当用户在字段中输入文本时,列表会更新以反映输入文本。然而,过滤列表非常慢,我的数据集中只有不到 300 条记录,那么为什么过滤需要这么长时间呢?我还将在下面发布我的文本字段代码,看看是否有人能识别出这种慢速过滤可能发生的位置。我想提一下,我还尝试使用 @Published 变量作为输入谓词来跟踪用户输入何时更改,而不是使用 onEditingChanged,这并没有提高性能。

TextField("Search for Device by name", text: $searchInput, onEditingChanged: _ in
        self.predicate = NSPredicate(format: "model contains %@", "\(self.searchInput)")

        print("THE PREDICATE: \(String(describing: self.predicate))")

        if self.searchInput == ""
            self.predicate = nil
        

    )

【问题讨论】:

获取限制当然是一件好事,但这是重新获取每个输入字符的陷阱,我认为这不是故意的。我建议将它与Combine (ups) 结合使用并使用.debounce 运算符,这样只有在用户停止输入时才会重新获取。 This topic 应该会有所帮助。 【参考方案1】:

回答我自己的问题,我发现了一种可以通过使用 fetchLimit 来实现优化的方法

 init(predicate: NSPredicate?) 
    let request: NSFetchRequest<HearingDevice> = HearingDevice.fetchRequest()
    request.sortDescriptors = []
    if let predicate = predicate 
        request.predicate = predicate
    else
        request.fetchLimit = 50 <------
    
    _devices = FetchRequest<HearingDevice>(fetchRequest: request)

这样在加载默认的未过滤数据集时,可以防止加载大量项目造成的滞后。

【讨论】:

【参考方案2】:

问题不在于您的 NSFetchRequest。问题与 SwiftUI 相关。

你只需要添加:

.id(UUID()) 加入您的列表。

例如:

List(items, id: \.self) 
    Text("Item \($0)")
.id(UUID())

请看https://www.hackingwithswift.com/articles/210/how-to-fix-slow-list-updates-in-swiftui

这样您就可以了解为什么会发生这种情况

【讨论】:

以上是关于使用 NSFetchRequest 和 NSPredicate 时,SwiftUI 列表更新缓慢的主要内容,如果未能解决你的问题,请参考以下文章

NSFetchRequest 和 predicateWithBlock

嵌套 NSFetchRequest?

MonoTouch 支持 NSFetchRequest 和 NSPredicate

具有不同记录和 UIPickerView 的 NSFetchRequest

根据实体类对 NSFetchRequest 进行排序

使用 NSFetchRequest 按日期和单元格按时区字母顺序对部分进行排序