通过单元格文本字符串值删除特定的数组元素[swift 4]

时间:2018-11-30 14:15:36

标签: ios arrays swift xcode

我的UITableView有两个数组。一个保留数组项,另一个保留数组项的值,以防它们带有复选标记。我现在遇到问题,因为我的两个数组没有相同的IndexPath。我需要一些东西来通过其字符串值删除selectedChecklist数组中的项目。我该怎么办?

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        checklist.remove(at: indexPath.row)
        selectedChecklist.removeAll { $0 == String(cell.textLabel) }
        myTableView.reloadData()
    }
}

打印选中的清单

  

[“ Test”,“ Test2”,“ Test3”,“ Asdf”,“ Test2”,“ Test2”,“ Test”]

这是我整个数组的代码。我正在努力实现答案:

import UIKit

class ChecklistViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource{

var dataHolder = [ListItem]()

var newChecklistItemString: String?
var alertInputTextField: UITextField?

@IBOutlet weak var myTableView: UITableView!

let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)

var checkedItems: [ListItem] {
    return dataHolder.filter { return $0.isChecked }
}
var uncheckedItems: [ListItem]  {
    return dataHolder.filter { return !$0.isChecked }
}

public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return (dataHolder.count)
}

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

    let cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "cell")
    cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 18.0)
    cell.textLabel?.text = dataHolder[indexPath.row].title

    return cell
}

// checkmarks when tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if (tableView.cellForRow(at: indexPath)?.accessoryType != .checkmark) {
        tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
    }else {
        tableView.cellForRow(at: indexPath)?.accessoryType = .none
    }

    tableView.deselectRow(at: indexPath, animated: true)
    saveDefaults()
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        checkedItems[indexPath.row].isChecked = false
        myTableView.reloadData()
    }
}


override func viewDidAppear(_ animated: Bool) {
    myTableView.reloadData()
}

override func viewDidLoad() {
    super.viewDidLoad()

    addSlideMenuButton()

    loadDefaults()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

@IBAction func addNewObject(_ sender: Any) {

    let alert = UIAlertController(title: "New Item", message: nil, preferredStyle: .alert)
    alert.addTextField { (alertInputTextField) in
        alertInputTextField.autocapitalizationType = .sentences
    }

    alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
        self.dismiss(animated: true, completion: nil)
    }))

    alert.addAction(UIAlertAction(title: "Add", style: .default, handler: { (action) in

        let textf = alert.textFields![0] as UITextField

        let indexPath = IndexPath(row: self.dataHolder.count, section: 0)
        self.dataHolder.append(ListItem(title: textf.text!, isChecked: false))
        self.saveDefaults()
        self.myTableView.insertRows(at: [indexPath], with: .automatic)

    }))

    self.present(alert, animated: true, completion: nil)

}

func loadDefaults()
{
    self.dataHolder = UserDefaults.standard.array(forKey: "dataHolder") as? [ListItem] ?? []

}

func saveDefaults()
{
    UserDefaults.standard.set(self.dataHolder, forKey: "dataHolder")

}
}

class ListItem {

var title: String
var isChecked: Bool

init(title: String, isChecked: Bool) {
    self.title = title
    self.isChecked = isChecked
}
}

3 个答案:

答案 0 :(得分:2)

您的代码太复杂了。当您使用类作为数据源时,多余的数组是多余的。

  • 删除checkedItemsuncheckedItems

    var checkedItems: [ListItem] { return dataHolder.filter { return $0.isChecked } } var uncheckedItems: [ListItem] { return dataHolder.filter { return !$0.isChecked } }

  • cellForRow中,根据isChecked设置选中标记并重复使用单元格!

    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 18.0) // better set this in Interface Builder
        let data = dataHolder[indexPath.row]
        cell.textLabel?.text = data.title
        cell.accessoryType = data.isChecked ? .checkmark : .none
        return cell
    }
    
  • didSelectRowAt
  • 切换模型中的isChecked并仅更新特定行

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
        dataHolder[indexPath.row].isChecked.toggle()
        tableView.reloadRows(at: [indexPath], with: .none)
        tableView.deselectRow(at: indexPath, animated: true)
        saveDefaults()
    }
    
  • tableView:commit:forRowAt:中删除给定indexPath处的行

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            dataHolder.remove(at: indexPath.row)
            myTableView.deleteRows(at: [indexPath], with: .fade)
            saveDefaults()
        }
    }
    
  • 并且您无法将自定义类的数组保存到UserDefaults。我建议使用结构和Codable

    struct ListItem : Codable {
        var title: String
        var isChecked: Bool
    }
    
    func loadDefaults()
    {
        guard let data = UserDefaults.standard.data(forKey: "dataHolder") else {
           self.dataHolder = []
           return
        }
        do {
            self.dataHolder = try JSONDecoder().decode([ListItem].self, for: data)
        } catch {
            print(error)
            self.dataHolder = []
        }  
    }
    
    func saveDefaults()
    {
        do {
            let data = try JSONEncoder().encode(self.dataHolder)
            UserDefaults.standard.set(data, forKey: "dataHolder")
        } catch {
            print(error)
        }
    }
    

答案 1 :(得分:1)

避免使用2个数组“持久化”模型。相反,您可以使用tuples生成单个数组:

var myArray: [(String, Bool)] = [("Test", false), ("Test1", false), ("Test2", false)]

从这里开始,问题得到了简化,并且您不会再遇到索引路径问题

答案 2 :(得分:1)

修改

我已更改代码以支持将[ListItem]保存到UserDefaults-由 Leo Dabus带来的评论,我还更改了几行内容,这些内容受 vadian 的代码似乎具有出色的编码风格。

class ChecklistViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource{

    var dataHolder: [ListItem] = DefaultsHelper.savedItems

    @IBOutlet weak var myTableView: UITableView!


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataHolder.count
    }

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

        let cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "cell")

        cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 18.0)

        let currentListItem = dataHolder[indexPath.row]

        cell.textLabel?.text = currentListItem.title
        cell.accessoryType = currentListItem.isChecked ? .checkmark : .none

        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        dataHolder[indexPath.row].isChecked.toggle()
        DefaultsHelper.saveItems(items: dataHolder)

        tableView.reloadRows(at: [indexPath], with: .none)
        tableView.deselectRow(at: indexPath, animated: true)
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == .delete {

            dataHolder.remove(at: indexPath.row)
            DefaultsHelper.saveItems(items: dataHolder)

            myTableView.reloadData()
            myTableView.deleteRows(at: [indexPath], with: .automatic)
        }
    }


    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        myTableView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // be sure you've set your tableView's dataSource and delegate to this class (It's fine if you've handled this on the storyboard side)

        addSlideMenuButton()
    }

    @IBAction func addNewObject(_ sender: Any) {

        let alert = UIAlertController(title: "New Item", message: nil, preferredStyle: .alert)
        alert.addTextField { (alertInputTextField) in
            alertInputTextField.autocapitalizationType = .sentences
        }

        alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
            self.dismiss(animated: true, completion: nil)
        }))

        alert.addAction(UIAlertAction(title: "Add", style: .default, handler: { (action) in

            let textf = alert.textFields![0] as UITextField

            let indexPath = IndexPath(row: self.dataHolder.count, section: 0)

            let itemToInsert = ListItem(title: textf.text!, isChecked: false)

            // self.dataHolder.append(itemToInsert)

            // thought you would want this, it will add your notes in reverse chronological order
            self.dataHolder.insert(itemToInsert, at: 0)
            DefaultsHelper.saveItems(items: self.dataHolder)

            self.myTableView.insertRows(at: [indexPath], with: .automatic)
        }))

        self.present(alert, animated: true, completion: nil)

    }
}

模型类:

// implementing NSObject and NSCoding to let us save this item in UserDefaults
class ListItem: NSObject, NSCoding{

    var title: String
    var isChecked: Bool

    init(title: String, isChecked: Bool) {
        self.title = title
        self.isChecked = isChecked
    }

    // This code lets us save our custom object in UserDefaults

    required convenience init(coder aDecoder: NSCoder) {
        let title = aDecoder.decodeObject(forKey: "title") as? String ?? ""
        let isChecked = aDecoder.decodeBool(forKey: "isChecked")
        self.init(title: title, isChecked: isChecked)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(title, forKey: "title")
        aCoder.encode(isChecked, forKey: "isChecked")
    }
}

class DefaultsHelper{

    private static let userDefaults = UserDefaults.standard
    private static let dataKey = "dataHolder"

    static var savedItems: [ListItem] {
        guard let savedData = userDefaults.data(forKey: dataKey) else { return [] }

        do{
            let decodedData = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(savedData)
            return decodedData as? [ListItem] ?? []
        }catch{
            print("could not fetch items- you may handle this", error)
        }

        return []
    }

    static func saveItems(items: [ListItem]){
        do{
            let encodedData = try NSKeyedArchiver.archivedData(withRootObject: items, requiringSecureCoding: false)
            userDefaults.set(encodedData, forKey: dataKey)
        }catch{
            print("could not save items- you may handle this", error)
        }
    }
}