自实施SearchBar以来Xcode 9.4.1 TableView问题

时间:2019-02-23 17:48:53

标签: swift xcode tableview uisearchbar master-detail

我已使用带有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()
        }
    }

1 个答案:

答案 0 :(得分:0)

您似乎同时使用NSFetchedResultsController和通知来对行更改做出反应(观察者) NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in ... })。同样,您也不在乎内存循环的发生,内存循环很可能是随处可见的。难怪所有这些都会导致如此奇怪的行为。