我已使用带有Core Data的主从模板在Xcode 9.4.1中创建了一个项目。在实现searchBar之前,该应用程序运行良好。自实现searchBar以来,项目的索引已关闭。例如,如果我在表格视图中选择顶部项目,则底部项目将显示在详细信息视图中。 searchBar函数起作用。我知道我需要以某种方式实现我的filteredData函数来完成,但是不知道在哪里以及如何进行修复。我已经注释了显示表视图部分的代码,直到可以修复为止。
我的核心数据的实体名称是“重复者”
由于字符数限制,我在MasterViewController中复制了我认为是相关的代码:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var repeaters: [Repeaters] = []
var selectedIndex: Int!
var filteredData: [Repeaters] = []
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let object = fetchedResultsController.object(at: indexPath)
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
// MARK: - Table View
// Returns number of section in the Table View
override func numberOfSections(in tableView: UITableView) -> Int {
//return fetchedResultsController.sections?.count ?? 0
return 1
}
// // Sets Section Titles in Table View
// override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// if let sections = fetchedResultsController.sections {
// let currentSection = sections[section]
// return String(currentSection.name)
// }
// return nil
// }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//let sectionInfo = fetchedResultsController.sections![section]
//return sectionInfo.numberOfObjects
return filteredData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellTableIdentifier, for: indexPath) as! CustomCell
//let repeater = fetchedResultsController.object(at: indexPath)
//let repeater = cell.textLabel?.text = filteredData[indexPath.row].name
//cell.call = repeater.call!
//cell.city = repeater.location!
cell.call = filteredData[indexPath.row].call!
cell.city = filteredData[indexPath.row].location!
cell.callFontColor = defaultCallFontColor
cell.paramFontColor = defaultParamFontColor
return cell
}
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let editAction = UITableViewRowAction(style: .normal, title: "Edit") { (rowAction, indexPath) in
// Create an Alert with a textFields
let alertController = UIAlertController(title: "Update Repeater",
message: "",
preferredStyle: UIAlertControllerStyle.alert)
let defaultAction = UIAlertAction(
title: "Update",
style: UIAlertActionStyle.default,
handler: {(alertAction: UIAlertAction!) in
let object = self.fetchedResultsController.object(at: indexPath)
let state: String = (alertController.textFields![1]).text!
let county: String = (alertController.textFields![2]).text!
let location: String = (alertController.textFields![3]).text!
let input_Freq: String = (alertController.textFields![4]).text!
let output_Freq: String = (alertController.textFields![5]).text!
let uplink_Tone: String = (alertController.textFields![6]).text!
let downlink_Tone: String = (alertController.textFields![7]).text!
let offset: String = (alertController.textFields![8]).text!
let use: String = (alertController.textFields![9]).text!
object.setValue(state, forKey: "state")
object.setValue(county, forKey: "county")
object.setValue(location, forKey: "location")
object.setValue(input_Freq, forKey: "input_Freq")
object.setValue(output_Freq, forKey: "output_Freq")
object.setValue(uplink_Tone, forKey: "uplink_Tone")
object.setValue(downlink_Tone, forKey: "downlink_Tone")
object.setValue(offset, forKey: "offset")
object.setValue(use, forKey: "use")
let context = self.fetchedResultsController.managedObjectContext
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
self.tableView.reloadData()
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="Call Sign"
textField.keyboardType=UIKeyboardType.emailAddress
textField.isUserInteractionEnabled = false
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="State"
textField.keyboardType=UIKeyboardType.emailAddress
// Add Observer to sense when value has changed
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "State is a required field!"
}
}
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="County"
textField.keyboardType=UIKeyboardType.emailAddress
// Add Observer to sense when value has changed
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "County is a required field!"
}
}
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="City"
textField.keyboardType=UIKeyboardType.emailAddress
// Add Observer to sense when value has changed
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "City is a required field!"
}
}
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="Input Frequency"
textField.keyboardType=UIKeyboardType.emailAddress
// Add Observer to sense when value has changed
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "Input Frequency is a required field!"
}
}
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="Output Frequency"
textField.keyboardType=UIKeyboardType.emailAddress
// Add Observer to sense when value has changed
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "Output Frequency is a required field!"
}
}
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="Uplink Tone"
textField.keyboardType=UIKeyboardType.emailAddress
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "Uplink Tone is a required field!"
}
}
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="Downlink Tone"
textField.keyboardType=UIKeyboardType.emailAddress
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "Downlink Tone is a required field!"
}
}
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="Offset"
textField.keyboardType=UIKeyboardType.emailAddress
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "Offset is a required field!"
}
}
})
alertController.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder="Use"
textField.keyboardType=UIKeyboardType.emailAddress
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
defaultAction.isEnabled = textField.hasText
if textField.hasText {
alertController.view.tintColor = UIColor.black
alertController.message = ""
} else {
alertController.view.tintColor = UIColor.red
alertController.message = "Use is a required field!"
}
}
})
let cancelAction = UIAlertAction(
title: "Cancel",
style: UIAlertActionStyle.cancel,
handler:nil)
alertController.addAction(defaultAction)
alertController.addAction(cancelAction)
let object = self.fetchedResultsController.object(at: indexPath)
(alertController.textFields![0]).text = object.call
(alertController.textFields![1]).text = object.state
(alertController.textFields![2]).text = object.county
(alertController.textFields![3]).text = object.location
(alertController.textFields![4]).text = object.input_Freq
(alertController.textFields![5]).text = object.output_Freq
(alertController.textFields![6]).text = object.uplink_Tone
(alertController.textFields![7]).text = object.downlink_Tone
(alertController.textFields![8]).text = object.offset
(alertController.textFields![9]).text = object.use
self.present(alertController, animated: true, completion: nil)
}
editAction.backgroundColor = .blue
let deleteAction = UITableViewRowAction(style: .normal, title: "Delete") { (rowAction, indexPath) in
let context = self.fetchedResultsController.managedObjectContext
let alertController = UIAlertController(title: "Delete",
message: "Delete Confirmation",
preferredStyle: UIAlertControllerStyle.alert)
let firetAction = UIAlertAction(title: "OK",
style: UIAlertActionStyle.default,
handler: {(alertAction: UIAlertAction!) in
context.delete(self.fetchedResultsController.object(at: indexPath))
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
})
let cancelAction = UIAlertAction(title: "Cancel",
style: UIAlertActionStyle.cancel,
handler: {(alertAction: UIAlertAction!) in
print()
})
alertController.addAction(firetAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
deleteAction.backgroundColor = .red
return [editAction,deleteAction]
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let context = fetchedResultsController.managedObjectContext
// Delete object from Table View
context.delete(fetchedResultsController.object(at: indexPath))
// If not errors, delete from Core Data Entity
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
// Segeue for navigation to Detail view, required with use of Custom Cell
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//self.performSegue(withIdentifier: "showDetail", sender:tableView)
selectedIndex = indexPath.row
print(selectedIndex)
self.performSegue(withIdentifier: "showDetail", sender:tableView)
}
func configureCell(_ cell: UITableViewCell, withEvent repeater: Repeaters) {
//cell.textLabel!.text = repeater.call!.description
}
// MARK: - Fetched results controller
var fetchedResultsController: NSFetchedResultsController<Repeaters> {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest: NSFetchRequest<Repeaters> = Repeaters.fetchRequest()
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "call", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: "state", cacheName: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController<Repeaters>? = nil
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
// Used for the insertion and deletion of Section Headers
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type {
case .insert:
tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade)
case .delete:
tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
default:
return
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
tableView.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
tableView.deleteRows(at: [indexPath!], with: .fade)
case .update:
configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! Repeaters)
case .move:
configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! Repeaters)
tableView.moveRow(at: indexPath!, to: newIndexPath!)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
func fetchData() {
do {
repeaters = try context.fetch(Repeaters.fetchRequest())
filteredData = repeaters
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Couldn't Fetch Data")
}
}
func createSearchBar() {
let searchBar = UISearchBar()
searchBar.showsCancelButton = false
searchBar.placeholder = "Search"
searchBar.delegate = self
self.navigationItem.titleView = searchBar
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
filteredData = repeaters
} else {
filteredData = repeaters.filter { ($0.call?.uppercased().contains(searchText.uppercased()))! }
print(filteredData)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
答案 0 :(得分:0)
您似乎同时使用NSFetchedResultsController
和通知来对行更改做出反应(观察者)
NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in ... }
)。同样,您也不在乎内存循环的发生,内存循环很可能是随处可见的。难怪所有这些都会导致如此奇怪的行为。