我有三个视图控制器(两个UIViewControllers和1个UITableViewController)。我想在tableView上显示所有数据,并在两个单独的视图控制器上添加/更新数据。
两个UIViewControllers每个都有三个textField(用于名称,电子邮件和电话号码)和一个按钮,用于保存/更新CoreData中和tableViewController上的数据。
表格视图中的一个部分由三行/两行组成(数字文本字段可以为空)。在某节中刷行时,用户可以删除整个节或编辑该节中的数据。
我创建了“人”实体和三个属性(“名称”,“电子邮件”,“数字”,所有均为String数据类型)。
但是我在网上遇到以下错误
let objectUpdate = test[0] as! NSManagedObject
错误:致命错误:索引超出范围
import UIKit
import CoreData
class RootTableViewController: UITableViewController {
//Array to display the data in table:
var array_of_person_data_array : [PersonData] = []
//Variable with index of selected row/section:
var index = 0
override func viewDidLoad() {
super.viewDidLoad()
let rightBarButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(nextScreen))
navigationItem.rightBarButtonItem = rightBarButton
tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "reuseIdentifier")
}
override func viewWillAppear(_ animated: Bool) {
retrieveData()
tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return array_of_person_data_array.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if array_of_person_data_array[section].number == nil || array_of_person_data_array[section].number == ""
{return 2}
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! TableViewCell
if indexPath.row == 0
{cell.personLabel.text = array_of_person_data_array[indexPath.section].name}
else if indexPath.row == 1
{cell.personLabel.text = array_of_person_data_array[indexPath.section].email}
else
{cell.personLabel.text = array_of_person_data_array[indexPath.section].number}
return cell
}
//Row actions when swiped:
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
index = indexPath.section
//Cancel row action:
let cancelRowAction = UITableViewRowAction(style: .normal, title: "Cancel", handler: {(action : UITableViewRowAction,indexPath : IndexPath) in
})
//Update row action:
let updateRowAction = UITableViewRowAction(style: .default, title: "Update", handler: {(action: UITableViewRowAction, indexPath: IndexPath) in
let sbObj = UIStoryboard(name: "Main", bundle: nil)
let svcObj = sbObj.instantiateViewController(withIdentifier: "UpdateViewControllerSB") as! UpdateViewController
svcObj.index = self.index
svcObj.personDataObject = self.array_of_person_data_array[indexPath.section]
self.navigationController?.pushViewController(svcObj, animated: true)
})
//Delete row action:
let deleteRowAction = UITableViewRowAction(style: .destructive, title: "Delete", handler: {(alert : UITableViewRowAction, indexPath : IndexPath) in
//Delete controller:
let deleteController = UIAlertController(title: "Delete", message: nil, preferredStyle: .actionSheet)
//Delete action:
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: {(UIAlertAction)in
self.deleteData()
})
deleteController.addAction(deleteAction)
//Cancel action:
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
deleteController.addAction(cancelAction)
//Present the controller:
self.present(deleteController,animated: true,completion: nil)
})
return [cancelRowAction,updateRowAction,deleteRowAction]
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 1000, height: 1000))
return view
}
@objc func nextScreen()
{
let sbObj = UIStoryboard(name: "Main", bundle: nil)
let svcObj = sbObj.instantiateViewController(withIdentifier: "AddViewControllerSB") as! AddViewController
self.navigationController?.pushViewController(svcObj, animated: true)
}
//Function to retrieve data from core data:
func retrieveData() {
//As we know that container is set up in the AppDelegates so we need to refer that container.
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
//We need to create a context from this container
let managedContext = appDelegate.persistentContainer.viewContext
//Prepare the request of type NSFetchRequest for the entity
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
// fetchRequest.fetchLimit = 1
// fetchRequest.predicate = NSPredicate(format: "username = %@", "Ankur")
// fetchRequest.sortDescriptors = [NSSortDescriptor.init(key: "email", ascending: false)]
//
do {
let result = try managedContext.fetch(fetchRequest)
for data in result as! [NSManagedObject] {
array_of_person_data_array.append(PersonData(personName: data.value(forKey: "name") as! String, personEmail: data.value(forKey: "email") as! String, personNumber: data.value(forKey: "number") as? String))
}
} catch {
print("Failed")
}
}
//Function to delete data from Core Data:
func deleteData(){
//As we know that container is set up in the AppDelegates so we need to refer that container.
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
//We need to create a context from this container
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "name = %@", array_of_person_data_array[index].name!)
fetchRequest.predicate = NSPredicate(format: "email = %@", array_of_person_data_array[index].email!)
fetchRequest.predicate = NSPredicate(format: "number = %@", array_of_person_data_array[index].number!)
do
{
let test = try managedContext.fetch(fetchRequest)
let objectToDelete = test[0] as! NSManagedObject
managedContext.delete(objectToDelete)
array_of_person_data_array.remove(at: index)
do{
try managedContext.save()
}
catch
{
print(error)
}
}
catch
{
print(error)
}
tableView.reloadData()
}
}
添加视图控制器:
import UIKit
import CoreData
class AddViewController: UIViewController {
@IBOutlet weak var nameTF: UITextField!
@IBOutlet weak var emailTF: UITextField!
@IBOutlet weak var numberTF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func addButtonAction(_ sender: Any) {
createData()
navigationController?.popToRootViewController(animated: true)
}
func createData(){
//As we know that container is set up in the AppDelegates so we need to refer that container.
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
//We need to create a context from this container
let managedContext = appDelegate.persistentContainer.viewContext
//Now let’s create an entity and new user records.
let userEntity = NSEntityDescription.entity(forEntityName: "Person", in: managedContext)!
//Get data ready to be set into CORE DATA:
let user = NSManagedObject(entity: userEntity, insertInto: managedContext)
user.setValue(nameTF.text, forKeyPath: "name")
user.setValue(emailTF.text, forKey: "email")
user.setValue(numberTF.text, forKey: "number")
//Save the set data to CORE DATA:
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}
更新视图控制器:
class UpdateViewController: UIViewController {
@IBOutlet weak var nameTF: UITextField!
@IBOutlet weak var emailTF: UITextField!
@IBOutlet weak var numberTF: UITextField!
var index : Int?
var personDataObject=PersonData(personName: "sample", personEmail: "sample@sample", personNumber: "xxxx-xxx-xxx")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func updateButtonAction(_ sender: Any) {
self.updateData()
navigationController?.popToRootViewController(animated: true)
}
//Update the data in CoreData:
func updateData(){
//As we know that container is set up in the AppDelegates so we need to refer that container.
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
//We need to create a context from this container
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "name = %@", "Ankur1")
fetchRequest.predicate = NSPredicate(format: "email = %@", "Ankur1")
fetchRequest.predicate = NSPredicate(format: "number = %@", "Ankur1")
do
{
let test = try managedContext.fetch(fetchRequest)
let objectUpdate = test[0] as! NSManagedObject
objectUpdate.setValue(nameTF.text, forKey: "name")
objectUpdate.setValue(emailTF.text, forKey: "email")
if let no = numberTF.text
{objectUpdate.setValue(no, forKey: "number")}
do{
try managedContext.save()
}
catch
{
print(error)
}
}
catch
{
print(error)
}
}
}
PersonData类定义为:
class PersonData
{
var name : String?
var email: String?
var number : String?
init(personName : String, personEmail : String, personNumber : String?) {
name = personName
email = personEmail
number = personNumber
}
}
如何更新现有数据或将新数据添加到CoreData并在Table View Controller上显示新数据?
最佳答案
您应该查看NSFetchedResultsController:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/nsfetchedresultscontroller.html
本质上,您想在CoreData上下文的第二个屏幕上创建/更新/删除值,保存这些更改,然后在主屏幕上设置获取的结果控制器(通过遵循上述文档)以自动侦听这些更改并将它们反映在表格中
这是一个好习惯,因为您不必担心自己更新视图并保持状态同步-视图更新时视图自动检索数据,这是自动完成的。
关于ios - 如何在第二屏幕上更新存储在核心数据中的值,并在第一屏幕上显示更新的值?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57482742/