Lka*_*abo 5 sqlite xcode core-data swiftui
更新 #4
错误是这样的:在 SwiftUI 视图中显示 List 或 ForEach 后,如果您通过更改列出的项目数来更改该视图,则 UI 在尝试计算已更改/需要更改的行数时会锁定。 .
我在 Apple 开发论坛中看到其他人遇到过这个错误。他们的临时解决方案是“将数组设置为空白”,从而在修改列出的数据集之前彻底清除列表大约 100 毫秒。这将充分避免用户使用数据数组迭代 List 或 ForEach 时的锁定。
问题是,对于 CoreData,按照本文中的描述使用,似乎没有任何方法可以清除推送的字母之间的列表(获取请求)。
在更新 #3 中,有一个 GitHub 项目显示了此问题的示例数据。
对解决方法的任何输入表示赞赏。
更新 #3
不好.. 正如这篇文章中所述,我能够从使用 CoreData 更改为本地 SQLite 数据库文件。我的结果是搜索和使用 CoreData 一样慢。我不知道这里发生了什么。但也许它与 SwiftUI 输出的渲染结果有关?无论哪种方式,搜索和显示大量数据似乎都是不可能的。
根据 J. Doe 的要求,我在 GitHub 上发布了一个示例项目,该项目演示了这个问题。这个项目可以在这里找到
我希望有人能看到我做错了什么。我很难相信这只是 iOS 的一个限制。
原帖
有任何想法吗?
我觉得我缺少一些基本的东西。我的获取请求(下面的代码)非常慢。我试图向 CoreData 模型添加一个索引,但有负面改进(来自下面的 J. Doe 的建议)。我想也许我需要以某种方式向 fetch 请求添加一个 fetchBatchSize 声明(想通了这一点 - 请参阅下面的更新 #2 - 没有帮助),但是使用 SwiftUI 中的属性包装器 @FetchRequest,似乎没有办法做这个。
下面的代码正在处理大约 5,000 条记录的测试数据集。在搜索中,每次更改输入(输入每个字母)时,搜索都会再次运行,这会拖累系统停止(CPU 100+% 和内存使用量增加)。
在以前的应用程序中,我完成了类似的任务,但这些应用程序使用 SQLite 数据文件并用 ObjC 编写。在这些情况下,事情真的很快,超过了这个测试数据集的 3 倍。
如果有人能指出我正确的方向来加速我的 CoreData 获取,我将不胜感激。如果我不需要,我不想回到 SQLite 文件。
非常感谢!
使用 SwiftUI,这是我的代码:
struct SearchView: View {
@Binding var searchTerm:String
var titleBar:String
var fetch: FetchRequest<MyData>
var records: FetchedResults<MyData>{fetch.wrappedValue}
init(searchTerm:Binding<String>, titleBar:String) {
self._searchTerm = searchTerm
self.titleBar = titleBar
self.fetch = FetchRequest(entity: MyData.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ MyData.header, ascending: true)], predicate: NSCompoundPredicate(type: .and, subpredicates: [ NSCompoundPredicate(type: .or, subpredicates: [NSPredicate(format: "%K CONTAINS[cd] %@", #keyPath(MyData.title),searchTerm.wrappedValue), NSPredicate(format: "%K CONTAINS[cd] %@", #keyPath(MyData.details),searchTerm.wrappedValue)]), NSPredicate(format: "%K == %@", #keyPath(MyData.titleBar),titleBar)])) //fetch request contains logic for module and search data - need to fix sort order later
}
var body: some View {
List{
Section(header: SearchBar(text: $searchTerm)) {
ForEach(records, id: \.self) { fetchedData in
VStack {
NavigationLink(destination: DetailView(titleBar: fetchedData.title!, statuteData: fetchedData.details!, isFavorite: fetchedData.isFavorite)) {
HStack {
Text(fetchedData.header!)
.font(.subheadline)
VStack(alignment: .leading) {
Text(fetchedData.title!)
}
.scaledToFit()
Spacer()
if fetchedData.isFavorite {
Image(systemName: "star.fill")
.imageScale(.small)
.foregroundColor(.yellow)
}
}
}
}
}
}.navigationBarTitle(Text("Search"))
}
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢您的帮助。
更新:
在编辑之前,我报告了存储数据的另一个问题,但是,该问题已通过以下帖子解决:
更新#2:
我最初的问题是问如何在我的 fetch 中添加批量限制以查看是否有帮助。我能够在不使用 FetchRequest 包装器的情况下重写获取,使用 NSFetchRequest 并添加了批处理限制。它对这种情况没有任何帮助..
再次感谢
我也遇到了同样的问题,即list从核心数据中获取结果时滚动速度非常慢。与我之前使用 UIKit 的解决方案相比,使用 swiftUI 的速度很慢(相同的数据,相同的获取)。除了必须使用 fetchOffset 和 fetchLimit 之外,我发现一个主要的性能问题是由于使用NavigationLink. 没有 NavigationLink 时,性能非常好,但使用 NavigationLink 时,性能就不那么好了。
在寻找解决方案时,我发现了Anupam Chugh 撰写的这篇博文 ,他写了有关此问题的文章,并提供了我在下面复制的解决方案。我很感激他。这是他的解决方案,不是我的。
\n关键点是,当NavigationLink在 中使用时list,即使用户尚未导航到该视图,也会立即加载目标视图。为了克服这个问题,我们必须让目的地视图变得懒惰。
就我而言,我在主视图中选择了一种食物,然后在详细视图中显示了所选食物的 180 多个属性的列表......
\n使用以下代码创建一个新文件
\nimport SwiftUI\n\n/// Creates a lazy view from view.\n///\n/// Helpfull for use of `NavigationLink` within `list`, where destination views are loaded immediately even when the user hasn\xe2\x80\x99t navigated to that view.\n/// Embedding the destination view in LazyView makes the destination view lazy and speeds up performance significantly for long lists.\n///\n/// ```Swift\n/// NavigationLink(destination: LazyView(Text("Detail Screen"))){\n/// Text("Tap me to see detail screen!")\n/// }\n/// ```\n///\n/// Source: [Blog post by Anupam Chugh on Medium]( https://medium.com/better-programming/swiftui-navigation-links-and-the-common-pitfalls-faced-505cbfd8029b). Thank you!!!!\nstruct LazyView<Content: View>: View {\n let build: () -> Content\n init(_ build: @autoclosure @escaping () -> Content) {\n self.build = build\n }\n var body: Content {\n build()\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n并像这样使用它:
\nNavigationLink(destination: LazyView(Text("Detail Screen"))){\n Text("Tap me to see detail screen!")\n}\nRun Code Online (Sandbox Code Playgroud)\n我希望这对其他人也有帮助。
\n这是解决方法,我可以补充一下,这是完全不可接受的。它使应用程序在技术上可以运行,但速度太慢,感觉就像在 8086 上加载 Windows 10 一样。荒谬。
此外,苹果反馈仍然没有答复,甚至没有确认。尽管他们表示无法帮助我,但我的代码级支持请求已被扣除。不开心..
不管怎样,如果你想构建一个感觉就像在泥土中挖掘的应用程序,使用 CoreData 并能够搜索该数据,这就是你的解决方法。
第一:创建数据的可哈希模型,或者至少是您需要搜索和/或显示的数据部分:
struct MyDataModel: Hashable {
let title: String
let name: String
let myData: String
}
Run Code Online (Sandbox Code Playgroud)
第二:创建一个 ObservableObject 类,该类发布一个变量,其中包含刚刚创建的数据模型类的数组:
class MyData:ObservableObject {
@Published var searchDataArray = [MyDataModel]()
}
Run Code Online (Sandbox Code Playgroud)
第三:确保将环境变量推送到您计划使用的视图:(此示例位于我的 SceneDelegate.swift 文件中
let myData = MyData()
Run Code Online (Sandbox Code Playgroud)
并附加到.environmentObject(myData)您需要它的任何视图中。
第四:从您的视图访问环境变量:@EnvironmentObject var myData: MyData并将获取结果加载到已发布的数据数组中,我使用此函数来完成任务:
func arrayFiller(){
if self.myData.searchDataArray.count > 0 {
self.myData.searchDataArray.removeAll()
}
for item in self.fetchRequest {
self.myData.searchDataArray.append(MyDataModel(title: item.title!, name: item.name!, myData: item:myData!))
}
}
Run Code Online (Sandbox Code Playgroud)
最后,从要搜索的视图中,您可以迭代已发布的环境变量,并且可以在搜索条件更改之间延迟清除数组,以避免出现错误。
ForEach(self.myData.searchDataArray, id: \.self) { fetchedItem in
Text(fetchedItem.name)
}
Run Code Online (Sandbox Code Playgroud)
然后,我使用 an.onReceive来观察我的 searchTerm 变量的更改,擦除已发布的数组,等待 10 毫秒并使用与我的搜索词匹配的数据重新填充数组。
它真的很慢而且很可怕。它有效,但我认为我无法在这种混乱的情况下进行任何接近生产的工作。
| 归档时间: |
|
| 查看次数: |
1803 次 |
| 最近记录: |