我的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
}
}
答案 0 :(得分:2)
您的代码太复杂了。当您使用类作为数据源时,多余的数组是多余的。
删除checkedItems
和uncheckedItems
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)
}
}
}