【问题标题】:Deleting item from tableview from filtered data crashing从过滤数据崩溃中删除表视图中的项目
【发布时间】:2019-08-02 04:25:45
【问题描述】:

我正在尝试从我的表格视图中删除提醒,但是当它来自通过搜索栏过滤时,常规删除在没有发生过滤器时非常有效,但是当过滤器位于搜索栏中时,它会使应用程序崩溃.

这是代码;

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    var updatedArray = [Reminder]()

    if editingStyle == .delete {
        if filtered == false {
            reminders.remove(at: indexPath.row)
            tableReminders.deleteRows(at: [indexPath], with: .fade)
            convertAndSaveInDDPath(array: reminders)
        }
        if filtered == true {
            updatedArray = reminders.filter{ $0.reminderName != filterData[indexPath.row].reminderName}
            print(updatedArray)
            reminders = updatedArray
            tableReminders.deleteRows(at: [indexPath], with: .fade)
            tableReminders.reloadData()
            //convertAndSaveInDDPath(array: reminders)

        }

    }
}

任何帮助将不胜感激,谢谢。

编辑(新代码):

  public struct Reminder {
    var reminderName : String
    var reminderPriority : String
    var reminderDate : Date
    var reminderStatus : String
    var reminderSavedTime : Date
}

var reminders : [Reminder] = []

var filtered : Bool = false



public  func getFilePath(fileName:String) -> String {
    let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
    let url = NSURL(fileURLWithPath: path)
    let filePath = url.appendingPathComponent(fileName)?.path
    return filePath!
}

public func convertAndSaveInDDPath (array:[Reminder]) {
    let objCArray = NSMutableArray()
    for obj in array {

        // we have to do something like this as we can't store struct objects directly in NSMutableArray
        let dict = NSDictionary(objects: [obj.reminderName ,obj.reminderPriority, obj.reminderDate, obj.reminderStatus, obj.reminderSavedTime ], forKeys: ["reminderName" as NSCopying,"reminderPriority" as NSCopying, "reminderDate" as NSCopying, "reminderStatus" as NSCopying, "reminderSavedTime" as NSCopying])
        objCArray.add(dict)
    }

    // this line will save the array in document directory path.
    objCArray.write(toFile: getFilePath(fileName: "remindersArray"), atomically: true)
}

public func getArray() -> [Reminder]? {
    var remindersArray = [Reminder]()
    if let _ = FileManager.default.contents(atPath: getFilePath(fileName: "remindersArray")) {
        let array = NSArray(contentsOfFile: getFilePath(fileName: "remindersArray"))
        for (_,userObj) in array!.enumerated() {
            let reminderDict = userObj as! NSDictionary
            let reminder = Reminder(reminderName: (reminderDict.value(forKey: "reminderName") as? String)!, reminderPriority: (reminderDict.value(forKey: "reminderPriority") as? String)!, reminderDate: (reminderDict.value(forKey: "reminderDate") as? Date)!, reminderStatus: (reminderDict.value(forKey: "reminderStatus") as? String)!, reminderSavedTime: (reminderDict.value(forKey: "reminderSavedTime") as? Date)!)
            remindersArray.append(reminder)

        }
        return remindersArray
    }
    return nil
}

class ViewController: UIViewController, UITableViewDataSource, UITextFieldDelegate, UITableViewDelegate, UISearchBarDelegate {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            // Reminder Section
            return filteredReminder.count
    }

    @IBOutlet var searchBar: UISearchBar!
    var filterData = [Reminder]()
    var originalReminder = [Reminder]()  // original data array.
    var filteredReminder = [Reminder]()  // data that used to show in tableview.


    var sortedAZState : Bool = false
    var sortedTimeState : Bool = false
    var sortedPrioState : Bool = false

    @IBOutlet weak var tableReminders: UITableView!
    @IBOutlet weak var timeSortBtn: UIButton!
    @IBOutlet weak var sortBtn: UIButton!
    @IBOutlet weak var prioritySortBtn: UIButton!

    @IBAction func BtnSort(_ sender: Any) {
        sortList(sender: sortBtn) // sorts by a-z through the sort function
    }

    @IBAction func btnSortTime(_ sender: Any) {
        sortList(sender: timeSortBtn)
    }

    @IBAction func btnSortPriority(_ sender: Any) {
        sortList(sender: prioritySortBtn)
    }


    func sortList(sender: UIButton) { // should probably be called sort and not filter


        if sender.tag == 1 && sortedAZState == false {
            reminders.sort() { $0.reminderName < $1.reminderName } // sort the reminder by name
            tableReminders.reloadData(); // notify the table view the data has changed
            print("sender.tag 1")
            sortedAZState = true
        }

        else if sender.tag == 1 && sortedAZState == true {
            reminders.sort() { $0.reminderName > $1.reminderName } // sort the reminder by name
            tableReminders.reloadData(); // notify the table view the data has changed
            print("sender.tag 1")
            sortedAZState = false
        }

        else if sender.tag ==  2 && sortedTimeState == false {
            reminders.sort { $0.reminderSavedTime.compare($1.reminderSavedTime) == .orderedAscending }
            tableReminders.reloadData();
            print("sender.tag 2")
            sortedTimeState = true
        }

        else if sender.tag ==  2 && sortedTimeState == true {
            reminders.sort { $0.reminderSavedTime.compare($1.reminderSavedTime) == .orderedDescending }
            tableReminders.reloadData();
            print("sender.tag 2")
            sortedTimeState = false
        }

        else if sender.tag == 3 && sortedPrioState == false {
            reminders.sort() { $0.reminderPriority.count < $1.reminderPriority.count } // sort the reminder by priority
            tableReminders.reloadData(); // notify the table view the data has changed
            print("sender.tag 3")
            sortedPrioState = true
        }

        else if sender.tag == 3 && sortedPrioState == true {
            reminders.sort() { $0.reminderPriority.count > $1.reminderPriority.count } // sort the reminder by priority
            tableReminders.reloadData(); // notify the table view the data has changed
            print("sender.tag 3")
            sortedPrioState = false
        }

    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        // Create an object of the dynamic cell "plainCell"
        let cell = tableView.dequeueReusableCell(withIdentifier: "ReminderTableViewCell", for: indexPath) as! ReminderTableViewCell
        // Depending on the section, fill the textLabel with the relevant text
        // Reminder Section


        cell.reminderLabel.text = filteredReminder[indexPath.row].reminderName

        if filteredReminder[indexPath.row].reminderPriority == "!" {
            let yourImage: UIImage = UIImage(named: "lowpriority")!
            cell.priorityImage.image = yourImage
        }

        else if filteredReminder[indexPath.row].reminderPriority == "!!" {
            let yourImage: UIImage = UIImage(named: "mediumpriority")!
            cell.priorityImage.image = yourImage
        }

        else if filteredReminder[indexPath.row].reminderPriority == "!!!" {
            let yourImage: UIImage = UIImage(named: "highpriority")!
            cell.priorityImage.image = yourImage
        }

        /* I DON'T KNOW WHAT THIS COMPLETION FOR, HOPE IT IS WORKING A/C TO YOUR NEED.  */

        //            cell.completeButtonAction = { [unowned self] in
        //                let reminderCall = reminders[indexPath.row].reminderName
        //                let alert = UIAlertController(title: "Complete!", message: "You have completed \(reminderCall).", preferredStyle: .alert)
        //                let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        //                alert.addAction(okAction)
        //
        //                reminders.remove(at: indexPath.row)
        //                self.tableReminders.deleteRows(at: [indexPath], with: .fade)
        //                convertAndSaveInDDPath(array: reminders)
        //
        //                print("reminder deleted")
        //
        //                self.present(alert, animated: true, completion: nil)
        //            }
        //


        return cell

    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
        print(reminders[indexPath.row].reminderName)

        let selectedReminder = reminders[indexPath.row].reminderName
        let destinationVC = EditReminderViewController()
        destinationVC.reminderPassed = selectedReminder

        performSegue(withIdentifier: "editSegue", sender: indexPath)
    }

     func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == .delete {

            filteredReminder.remove(at: indexPath.row)
            tableReminders.deleteRows(at: [indexPath], with: .fade)

            print(filteredReminder)
            convertAndSaveInDDPath(array: filteredReminder) // UNCOMMENT THIS

        }
    }

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar)  {
        searchBar.resignFirstResponder()
    }

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {

      // var decapValue = searchBar.text?.lowercased()

       // print(decapValue!)
       // print(LinearSearch(searchText: decapValue!, array: reminders))

        if searchBar.text != "" {
            self.filteredReminder = originalReminder.filter({ reminder -> Bool in
                return reminder.reminderName.lowercased().contains(searchText.lowercased())
            })
        }
        else {
            self.filteredReminder = self.originalReminder
        }
        tableReminders.reloadData()

       // print(filterData)



    }
    /*
    func LinearSearch(searchText: String, array: [Reminder]) -> Bool { // search function to return a true or a false bool (contains two parameneters, search value and array
        for i in reminders {                                      // cycles through each element in the array
            if i.reminderName.lowercased().contains(searchText)  {                               // if element = search (return true)
            filterData.append(i)
            print(filterData)
                return true
            }
        }


        return false                                            // returns false if no element comes back to equal the searchValue
    }
*/

    @IBAction func btnAdd(_ sender: Any) {
        performSegue(withIdentifier: "addSegue", sender: (Any).self)
    }

    override func viewDidLoad() {
        super.viewDidLoad()         // example cell
      // reminders.append(Reminder(reminderName: "HOMEWORK", reminderPriority: "LOW", reminderDate: "4324", reminderStatus: "INCOMPLETE"))

        tableReminders.dataSource = self
        tableReminders.delegate = self

        searchBar.delegate = self

        tableReminders.reloadData()

        // print file path of array saved
        // print(getFilePath(fileName: "remindersArray"))

       let reminderRetrievedArray = getArray()
         reminders = reminderRetrievedArray!
        originalReminder = reminders
        gfilteredReminder = reminders

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }



}

我不知道你要的是哪个代码,所以这里都有。但是是的,你是对的,它一次删除一个,但它正在保存一个空数组以供下次打开时加载。

【问题讨论】:

  • if filtered == true 条件下,此行是错误的 -> tableReminders.deleteRows(at: [indexPath], with: .fade)。删除它并尝试。
  • @dahiya_boy 当我这样做时,它不会崩溃,是的,它会从数组中删除它,但它会保留在 tableview 中,直到我再次触摸搜索栏,如果这有任何意义,我想要桌子查看不显示已删除的值,甚至在我触摸搜索栏之前
  • 请记住,当您执行deleteRowinsertRow 时,您还需要对数据源执行相同的操作。在您的filtered == true 中,您正在创建更改对象引用的新数据源。从开发人员的角度来看,您会看到值,但机器检查地址已更改。在filtered == false 中你做得很完美,这就是为什么它可以工作而true 不是。
  • @dahiya_boy 我将如何在过滤部分执行此操作?
  • 这取决于您的功能。它如何为用户工作?什么是用户交互?

标签: ios swift tableview searchbar


【解决方案1】:

以下更改可让您的代码完美、干净、易于理解且有效。如果您有任何疑问,请写评论,如果答案有任何错误,请更新答案。


  1. 您不需要维护标记过滤。我假设你有两个数组

    var originalReminder = [Reminder]()  // original data array.
    var filteredReminder = [Reminder]()  // data that used to show in tableview.
    
  2. ViewDidLoad 中,在originalReminder 中设置正确的数据,如果您想在表格中显示所有原始数据,请在filteredReminder 中也分配相同的数据。

  3. 现在我们将使用 1 个数组管理 tableview,即filteredReminder,如果搜索栏文本为空,那么我们会将原始数组分配给过滤后的数组。所以你的searchbar textDidChange 看起来像这样。

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    
        if searchBar.text != "" {
            self.filteredReminder = originalReminder.filter({ reminder -> Bool in
                return reminder.reminderName.lowercased().contains(searchText.lowercased())
            })
                    }
        else {
            self.filteredReminder = self.originalReminder
        }
        tableReminders.reloadData()
    }
    
  4. 您可以从cellForRow 中删除冗余代码,如下所示

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return filteredReminder.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // Create an object of the dynamic cell "plainCell"
        let cell = tableView.dequeueReusableCell(withIdentifier: "ReminderTableViewCell", for: indexPath) as! ReminderTableViewCell
        // Depending on the section, fill the textLabel with the relevant text
        // Reminder Section
    
    
        cell.reminderLabel.text = filteredReminder[indexPath.row].reminderName
    
        if filteredReminder[indexPath.row].reminderPriority == "!" {
            let yourImage: UIImage = UIImage(named: "lowpriority")!
            cell.priorityImage.image = yourImage
        }
    
        else if filteredReminder[indexPath.row].reminderPriority == "!!" {
            let yourImage: UIImage = UIImage(named: "mediumpriority")!
            cell.priorityImage.image = yourImage
        }
    
        else if filteredReminder[indexPath.row].reminderPriority == "!!!" {
            let yourImage: UIImage = UIImage(named: "highpriority")!
            cell.priorityImage.image = yourImage
        }
    
        /* I DON'T KNOW WHAT THIS COMPLETION FOR, HOPE IT IS WORKING A/C TO YOUR NEED.  */
    
        //            cell.completeButtonAction = { [unowned self] in
        //                let reminderCall = reminders[indexPath.row].reminderName
        //                let alert = UIAlertController(title: "Complete!", message: "You have completed \(reminderCall).", preferredStyle: .alert)
        //                let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        //                alert.addAction(okAction)
        //
        //                reminders.remove(at: indexPath.row)
        //                self.tableReminders.deleteRows(at: [indexPath], with: .fade)
        //                convertAndSaveInDDPath(array: reminders)
        //
        //                print("reminder deleted")
        //
        //                self.present(alert, animated: true, completion: nil)
        //            }
        //
    
    
        return cell
    
    }
    
  5. 要删除单元格,现在只需要这样做。

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    
        if editingStyle == .delete {
    
                filteredReminder.remove(at: indexPath.row)
                tableReminders.deleteRows(at: [indexPath], with: .fade)
               //                convertAndSaveInDDPath(array: filteredReminder) // UNCOMMENT THIS
    
        }
    }
    

【讨论】:

  • 这已经完全解决了我想要的问题,但是现在当我现在删除一个提醒时,它会将过滤后的数组设置为空并保存为空,因此当新程序打开时它正在加载一个空数组.
  • so when the new program opens it is loading an empty array.我没听懂你在说什么??
  • 是的,它加载了一个空数组,因为我相信当它在执行搜索后删除时,该数组被过滤为空,因此它保存了一个空数组。
  • @sav 一次只会删除一个值。所以可能还有另一个问题。从问题中删除旧代码并上传新代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-31
  • 2013-04-22
相关资源
最近更新 更多