我正在使用Swift 4构建单个视图iOS 11应用程序,其UITableViewController
也被定义为delegate
的{{1}}。
NSFetchedResultsController
如果我使用class MyTVC: UITableViewController, NSFetchedResultsControllerDeleagate {
var container:NSPersistentContainer? =
(UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
var frc : NSFetchedResultsController<Student>?
override func viewDidLoad() {
container?.performBackgroundTask { context in
// adds 100 dummy records in background
for i in 1...100 {
let student = Student(context: context)
student.name = "student \(i)"
}
try? context.save() // this works because count is printed below
if let count = try? context.count(for: Student.fetchRequest()) {
print("Number of students in core data: \(count)") // prints 100
}
} // end of background inserting.
// now defining frc:
if let context = container?.viewContext {
let request:NSFetchRequest<Student> = Student.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
frc = NSFetchedResultsController<Student> (
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil )
try? frc?.performFetch() // this works and I get no errors
tableView.reloadData()
frc.delegate = self
} // end of frc definition
}
}
添加一行Student
,viewContext
将触发所需的方法以在frc
中显示它。但是,未示出100个虚拟行。事实上,如果我尝试在插入完成后告诉tableview重新加载,我的应用程序开始表现得很奇怪而且变得越来越错误,并且没有做它应该做的事情(即:不删除行,不编辑等)
但如果我重新启动我的应用程序,而不调用虚拟插入,我可以看到从上一次运行中插入的100行。
唯一的问题是我无法从后台线程调用tableView
,所以我尝试这样做:
tableView.reloadData()
然后我尝试调用// after printing the count, I did this:
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData() // causes UI to behave weirdly
}
在正确的线程中重新加载表视图
viewContext.perform
如何告诉我的tableview以线程安全的方式重新加载并显示100个虚拟行?
答案 0 :(得分:1)
override func viewDidLoad() {
super.viewDidLoad()
//Always need your delegate for the UI to be set before calling the UI's delegate functions.
frc.delegate = self
//First we can grab any already stored values.
goFetch()
//This chunk just saves. I would consider putting it into a separate function such as "goSave()" and then call that from an event handler.
container?.performBackgroundTask { context in
//We are in a different queue than the main queue, hence "backgroundTask".
for i in 1...100 {
let student = Student(context: context)
student.name = "student \(i)"
}
try? context.save() // this works because count is printed below
if let count = try? context.count(for: Student.fetchRequest()) {
print("Number of students in core data: \(count)") // prints 100
}
//Now that we are done saving its ok to fetch again.
goFetch()
}
//goFetch(); Your other code was running here would start executing before the backgroundTask is done. bad idea.
//The reason it works if you restart the app because that data you didn't let finish saving is persisted
//So the second time Even though its saving another 100 in another queue there were still at least 100 records to fetch at time of fetch.
}
func goFetch() {
if let context = container?.viewContext {
let request:NSFetchRequest<Student> = Student.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
frc = NSFetchedResultsController<Student> (
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil )
try? frc?.performFetch()
//Now that records are both stored and fetched its safe for our delegate to access the data on the main thread.
//To me it would make sense to do a tableView reload everytime data is fetched so I placed this inside o `goFetch()`
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
}
答案 1 :(得分:1)
经过大量关于NSFetchedResultsController和NSPersistentContainer的阅读,最后在SO上找到了重要的信息,我想我有一个有效的例子。
我的代码略有不同,因为我使用了一个项目。无论如何,这就是我所做的:
在我的视图控制器中,我有一个容器的属性
import React, { Component } from 'react'
import ReactHighcharts from 'react-highcharts';
class Graph extends Component {
constructor() {
super();
this.state = {
config: {
chart: {
type: 'xrange'
},
title: {
text: 'Highcharts X-range'
},
xAxis: {
type: 'datetime'
},
yAxis: {
title: {
text: ''
},
categories: ['Prototyping', 'Development', 'Testing'],
reversed: true
},
series: [{
name: 'Project 1',
// pointPadding: 0,
// groupPadding: 0,
pointWidth: 20,
data: [{
x: Date.UTC(2014, 10, 21),
x2: Date.UTC(2014, 11, 2),
y: 0,
partialFill: 0.25
}, {
x: Date.UTC(2014, 11, 2),
x2: Date.UTC(2014, 11, 5),
y: 1
}, {
x: Date.UTC(2014, 11, 8),
x2: Date.UTC(2014, 11, 9),
y: 2
}, {
x: Date.UTC(2014, 11, 9),
x2: Date.UTC(2014, 11, 19),
y: 1
}, {
x: Date.UTC(2014, 11, 10),
x2: Date.UTC(2014, 11, 23),
y: 2
}],
dataLabels: {
enabled: true
}
}]
}
}
}
render() {
const config = this.state.config;
return (
<ReactHighcharts config={config}></ReactHighcharts>
)
}
}
export default Graph
在viewDidLoad中,我加载了持久存储并创建了我的100条记录。
private var persistentContainer = NSPersistentContainer(name: coreDataModelName)
下面是createFakeNotes(),其中我使用单独的上下文在后台线程中插入元素,这段代码几乎取自Apple's Core Data programming guide,但为了使UI更新,我需要将automaticMergesChangesFromParent设置为true,我在this SO answer
中找到了我还首先删除旧笔记以使测试更容易。
persistentContainer.loadPersistentStores { persistentStoreDescription, error in
if let error = error {
print("Unable to add Persistent Store [\(error)][\(error.localizedDescription)]")
} else {
self.createFakeNotes() // Here 100 elements get created
DispatchQueue.main.async {
self.setupView() // other stuff, not relevant
self.fetchNotes() // fetch using fetch result controller
self.tableView.reloadData()
}
}
}
}
答案 2 :(得分:0)
您应该通过从viewwillappear调用数据来获取数据,然后尝试重新加载您的tableview。
override func viewWillAppear(_ animated: Bool) {
getdata()
tableView.reloadData()
}
func getdata() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do{
persons = try context.fetch(Person.fetchRequest())
}
catch {
print("fetching failed")
}
}