ios - UITableView indexPath 和最大可重用单元格数

标签 ios arrays swift uitableview core-data

我的带有自定义可重用单元格的 UITableView 已完成,但仍然存在两个问题:

  1. 我可以创建 6 个单元格,但是当我添加第 7 个单元格时,应用程序崩溃并显示 Unexpectedly found nil while unwrapping an Optional value 我真的无法理解。当我重新启动应用程序时, View 再次立即崩溃。

  2. 除了 valueLabel.text 之外,单元格的数据被正确重用。 当我在 tableView 滚动的单元格上更新此值时,数据回落一行(如图所示)。我相信这是一个与 data source 的错误编辑(更新?)相关的 indexPath 问题,但是当我重新启动应用程序时,数据位于正确的单元格中。

enter image description here

我在下面的代码中标记了这些事件 1 和 2。

创建数据:

func createCryptoArray(_ addedCrypto: String) {

        if addedCrypto == "Bitcoin BTC" {
            if CoreDataHandler.saveObject(name: "Bitcoin", code: "bitcoin", symbol: "BTC", placeholder: "BTC Amount", amount: "0.00000000", amountValue: "0.0") {
                for _ in CDHandler.fetchObject()! {
                }
            }
        }
        if addedCrypto == "Bitcoin Cash BCH" {
            if CoreDataHandler.saveObject(name: "Bitcoin Cash", code: "bitcoinCash", symbol: "BCH", placeholder: "BCH Amount", amount: "0.00000000", amountValue: "0.0") {
                for _ in CDHandler.fetchObject()! {
                }
            }
        } 
        //...
    }
}

WalletTableViewController:(问题 1)

class WalletTableViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var tableView: UITableView!

var cryptos : [CryptosMO] = []

var total : Double = 0.0
static var staticTotal : Double = 0.0

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.delegate = self
    tableView.dataSource = self

    if CDHandler.fetchObject() != nil {
        cryptos = CDHandler.fetchObject()!
        tableView.reloadData()
    }

    update()

    updateWalletValue()
    updateWalletLabel()
}

override func viewWillAppear(_ animated: Bool) {

    update()

    tableView.delegate = self
    tableView.dataSource = self

    if CDHandler.fetchObject() != nil {
        cryptos = CDHandler.fetchObject()!
        tableView.reloadData()
    }
}

func updateCellValue() {

    for section in 0...self.tableView.numberOfSections - 1 {
        if (self.tableView.numberOfRows(inSection: section) >= 1) {
            for row in 0...self.tableView.numberOfRows(inSection: section) - 1 {
                let indexP: IndexPath = IndexPath(row: row, section: section);
                self.updateCellValueLabel(self.tableView.cellForRow(at: indexP) as! WalletTableViewCell) // <-------Problem 1
            }
        }
    }
}

func update() {

    updateCellValue()
}
}

表格 View 函数:

extension WalletTableViewController: UITableViewDelegate, UITableViewDataSource, CryptoCellDelegate {

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

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

    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! WalletTableViewCell

    cell.cryptoNameLabel.text = cryptos[indexPath.row].name
    cell.cryptoCodeLabel.text = cryptos[indexPath.row].symbol
    cell.amountLabel.text = cryptos[indexPath.row].amount
    cell.amountTextField.placeholder = cryptos[indexPath.row].placeholder

    if cryptos[indexPath.row].amountValue == "0.0" {
        cell.cryptoValueLabel.text = ""
    }

    cell.delegate = self
    cell.amountTextField.delegate = self

    return cell
}

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

    if editingStyle == .delete {
        let selectedManagedObject = cryptos[indexPath.row]
        CDHandler.deleteObject(entity:"CryptosMO", deleteObject: selectedManagedObject)
        cryptos.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .fade)
        updateWalletValue()
        updateWalletLabel()
    }
}

用户操作、计算和数据更新:(使用 valueLabel.text's)

// TextFields amounts
//--------------------
func cellAmountEntered(_ walletTableViewCell: WalletTableViewCell) {

    if walletTableViewCell.amountTextField.text == "" {
        return
    }

    let str = walletTableViewCell.amountTextField.text
    let formatter = NumberFormatter()
    formatter.locale = Locale(identifier: "en_US")
    let dNumber = formatter.number(from: str!)
    let nDouble = dNumber!
    let eNumber = Double(truncating: nDouble)
    walletTableViewCell.amountLabel.text = String(format:"%.8f", eNumber)

    var editAmount = ""
    editAmount = String(format:"%.8f", eNumber)

    let indexPath = tableView.indexPath(for: walletTableViewCell)
    let selectedManagedObject = cryptos[(indexPath?.row)!]

    CDHandler.editObject(editObject: selectedManagedObject, amount: editAmount, amountValue: "0.0")

    walletTableViewCell.amountTextField.text = ""

}

// Value calculation & label update
//----------------------------------
func updateCellValueLabel(_ walletTableViewCell: WalletTableViewCell) {

    if walletTableViewCell.amountLabel.text == "" {
        walletTableViewCell.amountLabel.text = "0.00000000"
    }

    var newCryptos : [CryptosMO] = []
    var doubleAmount = 0.0

    var cryptoPrice = ""
    let indexPath = tableView.indexPath(for: walletTableViewCell)

    if CDHandler.fetchObject() != nil {
        newCryptos = CDHandler.fetchObject()!
    }

    cryptoPrice = cryptos[(indexPath?.row)!].code!
    guard let cryptoDoublePrice = CryptoInfo.cryptoPriceDic[cryptoPrice] else { return }

    let selectedAmount = newCryptos[(indexPath?.row)!]

    guard let amount = selectedAmount.amount else { return }
    var currentAmountValue = selectedAmount.amountValue

    doubleAmount = Double(amount)!

    let calculation = cryptoDoublePrice * doubleAmount
    currentAmountValue = String(calculation)

    CoreDataHandler.editObject(editObject: selectedAmount, amount: amount, amountValue: currentAmountValue)

    walletTableViewCell.valueLabel.text = String(calculation) // <-------- `valueLabel.text`
}

核心数据函数:

class CoreDataHandler: NSObject {

class func getContext() -> NSManagedObjectContext {
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    return appDelegate.persistentContainer.viewContext
}

class func saveObject(name:String, code:String, symbol:String, placeholder:String, amount:String, amountValue:String) -> Bool {
    let context = getContext()
    let entity = NSEntityDescription.entity(forEntityName: "CryptosMO", in: context)
    let managedObject = NSManagedObject(entity: entity!, insertInto: context)

    managedObject.setValue(name, forKey: "name")
    managedObject.setValue(code, forKey: "code")
    managedObject.setValue(symbol, forKey: "symbol")
    managedObject.setValue(placeholder, forKey: "placeholder")
    managedObject.setValue(amount, forKey: "amount")
    managedObject.setValue(amountValue, forKey: "amountValue")

    do {
        try context.save()
        return true
    } catch {
        return false
    }
}

class func fetchObject() -> [CryptosMO]? {
    let context = getContext()
    var cryptos: [CryptosMO]? = nil

    do {
        cryptos = try context.fetch(CryptosMO.fetchRequest()) as? [CryptosMO]
        return cryptos
    } catch {
        return cryptos
    }
}

class func deleteObject(entity: String, deleteObject: NSManagedObject) {
    let context = getContext()
    context.delete(deleteObject)

    do {
        try context.save()
    } catch let error as NSError {
        print("Could not save. \(error), \(error.userInfo)")
    }
}

class func editObject(editObject: NSManagedObject, amount: String, amountValue: String) {
    let context = getContext()
    let managedObject = editObject

    do {
        managedObject.setValue(amount, forKey: "amount")
        managedObject.setValue(amountValue, forKey: "amountValue")
        try context.save()
    } catch let error as NSError {
        print("Could not save. \(error), \(error.userInfo)")
    }
}

}

最佳答案

您使用的 UIKit 框架完全相反其预期方式:您的代码循环遍历您的数据模型并希望更新 TableView 中的每个单元格。假设您有数千行,那么您将(至少)尝试更新数以千计的单元格。但是,正如@Larme 已经提到的,这些单元格不存在(因为它们不可见),而且您的大部分调用都是徒劳的。

要正确使用 UIKit,你必须改变你的做法:你不会主动更新,而是如果表格 View 检测到它必须在一个单元格(或多个单元格,每个一个接一个),因为 View 可见,用户滚动等。所以如果 TableView 只显示 5 个单元格,它只要求 5 个单元格的数据,而不是整个数据库。

你需要做的是:

  • 删除 func updateCellValue()。如果您认为必须更新 View ,请在 TableView 中调用 reloadData()reloadRowsAtIndexPaths...。这将导致调用适当的数据源方法,例如...
  • tableView(cellForRowAt:) 的末尾,您使用出列的单元格调用 updateCellValueLabel,然后它将更新标签。
  • 您还应该修改 updateCellValueLabel 并提交 IndexPath(或仅行),因此您不需要调用 tableView.indexPath(for: walletTableViewCell) 任何更多。

这只是一个简单的操作提示;你也可能会考虑优化数据获取


补充关于第三点(见评论): 您可以通过以下方式修改您的代码:

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

    // ... same as before

    cell.delegate = self
    cell.amountTextField.delegate = self

    updateCellValueLabel(cell, atRow:indexPath.row)
    return cell
}

func updateCellValueLabel(_ walletTableViewCell: WalletTableViewCell, atRow row:Int) {

    if walletTableViewCell.amountLabel.text == "" {
        walletTableViewCell.amountLabel.text = "0.00000000"
    }

    var newCryptos : [CryptosMO] = []
    var doubleAmount = 0.0

    var cryptoPrice = ""

    if CDHandler.fetchObject() != nil {
        newCryptos = CDHandler.fetchObject()!
    }

    cryptoPrice = cryptos[row].code!
    guard let cryptoDoublePrice = CryptoInfo.cryptoPriceDic[cryptoPrice] else { return }

    let selectedAmount = newCryptos[row]

    guard let amount = selectedAmount.amount else { return }
    var currentAmountValue = selectedAmount.amountValue

    doubleAmount = Double(amount)!

    let calculation = cryptoDoublePrice * doubleAmount
    currentAmountValue = String(calculation)

    CoreDataHandler.editObject(editObject: selectedAmount, amount: amount, amountValue: currentAmountValue)

    walletTableViewCell.valueLabel.text = String(calculation) // <-------- `valueLabel.text`
}

因此 updateCellValueLabel 获得一个额外的 atRow 参数,以访问 cryptosnewCryptos 数组中的正确值.

关于ios - UITableView indexPath 和最大可重用单元格数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49174668/

相关文章:

ios - 如何删除某种类型的所有 subview

ios - Apple 告诉 iOS8 上扩展的 64 位要求是什么意思?

ios - 使用电子邮件地址检查 PayPal 帐户是否存在?

c++ - 遍历数组最有效的方法是什么? (c++)

ios - 访问另一个数组中的 JSON 数组

javascript - 检查数据库是否存在并在 SQLite IOS 中删除数据库

c++ - 有什么方法可以使模板函数应用于 C++ 中任意长度的数组?

arrays - 创建一个具有枚举元素大小的数组

objective-c - 如何使用 "SCNVector4Make()"?

swift - 如何让文字出现在图片的中央?