Ios UITableView indexPath和最大可重用单元数

Ios UITableView indexPath和最大可重用单元数,ios,arrays,swift,uitableview,core-data,Ios,Arrays,Swift,Uitableview,Core Data,使用自定义可重用单元格的MyUITableView已完成,但仍存在两个问题: 我可以创建6个单元格,但当我添加第7个单元格时,应用程序崩溃,在打开可选值时意外发现为零,我真的无法理解。当我重新启动应用程序时,视图会立即再次崩溃 除了valueLabel.text之外,单元格的数据被正确重用当我在滚动了tableView的单元格上更新此值时,数据会后退一行(如图所示)。我认为这是一个indexPath问题,与数据源的错误编辑(更新?)有关,但是当我重新启动应用程序时,数据位于正确的单元格中。 我

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

  • 我可以创建6个单元格,但当我添加第7个单元格时,应用程序崩溃,在打开可选值时意外发现为零,我真的无法理解。当我重新启动应用程序时,
    视图
    会立即再次崩溃

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

  • 我在下面的代码中标记了这些事件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()! {
                    }
                }
            } 
            //...
        }
    }
    
    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()
        }
    }
    
    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)")
        }
    }
    
    }
    
    WalletTableViewController:(问题1)

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


    真正的问题在于更新单元格的逻辑。您使用一个名为
    updateCellValue()
    的函数,该函数用于强制“从外部”更新单元格。这是错误的。相反,您应该更新数据源,并让tableView通过调用
    self.tableView.reloadData()
    来获取这些更改。绝不要通过更新单元格来更新表视图,而要通过简单地更新数据源来更新表视图本身。

    您使用的UIKit框架与预期完全相反:您的代码在数据模型中循环,并希望更新表视图中的每个单元格。如果你有数千行,那么你会(至少)尝试更新成千上万的单元格。但是,正如@Larme已经提到的,这些细胞并不存在(因为它们是不可见的),并且您的大多数调用都是徒劳的

    要正确使用UIKit,您必须更改方法:您不主动更新,但如果表视图检测到它必须在一个单元格(或多个单元格,每个单元格一个接一个)中显示数据,则表视图将调用您,因为视图变得可见,用户滚动等。因此,如果表视图仅显示5个单元格,它只需要5个单元格的数据,而不需要整个数据库

    您需要做的是:

    • 删除
      func updateCellValue()
      。如果您认为必须更新视图,请在表视图中调用
      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`
    }
    
    func tableView(tableView:UITableView,cellForRowAt indexath:indexPath)->UITableViewCell{
    //……和以前一样
    cell.delegate=self
    cell.amountTextField.delegate=self
    updateCellValueLabel(单元格,atRow:indexPath.row)
    返回单元
    }
    func updateCellValueLabel(walletTableViewCell:walletTableViewCell,atRow行:Int){
    如果walletTableViewCell.amountLabel.text==“”{
    walletTableViewCell.amountLabel.text=“0.00000000”
    }
    var newCryptos:[CryptosMO]=[]
    var doubleAmount=0.0
    var cryptoPrice=“”
    如果CDHandler.fetchObject()!=nil{
    newCryptos=CDHandler.fetchObject()!
    }
    cryptoPrice=cryptos[row]。代码!
    guard let cryptoDoublePrice=CryptoInfo.cryptoPriceDic[cryptoPrice]else{return}
    让selectedAmount=newCryptos[行]
    guard let amount=selectedAmount.amount else{return}
    var currentAmountValue=selectedAmountValue
    双倍金额=双倍(金额)!
    let calculation=CryptoublePrice*doubleAmount
    currentAmountValue=字符串(计算)
    editObject(editObject:selectedAmount,amount:amount,amountValue:currentAmountValue)
    
    walletTableViewCell.valueLabel.text=String(calculation)/
    self.tableView.cellForRow(at:)
    如果单元格不可见,则返回零。由于您在那里用“!”展开,这就解释了崩溃的原因。您应该删除
    updateCellValue()
    UITableView
    自动调用
    tableView(\u,cellForRowAt:)
    当它需要时,您只需更新您的信息数组并调用
    tableView.reloadData()
    。当表格视图需要用于可见单元格时,它将自动使用该方法。这是一种被动方式,它是一种委托。@AlejandroIván我在哪里以及如何更新我的信息数组?@martin your
    cryptos
    数组。我指的是用于填充表格视图的数组。@Larme in
    tableView.cellForRow(在:)
    updateCellValue()
    ?非常感谢安德烈亚斯,这似乎是一个完美的答案!有几件事我还没有完全理解。我在本周的同一时间学习了
    表格视图
    核心数据
    ,对此我深表歉意。我正确地理解了你的第二点,但你能给另外两个增加一点精度吗?特别是
    交上IndexPath(或只交行),这样您就不需要调用tableView.IndexPath
    。再次感谢!它工作得很好!Andreas!还有两个问题:当通过委托调用
    updateCellValueLabel
    时,如何从另一个类传递
    IndexPath.row
    ?您建议我读什么给bette
    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)")
        }
    }
    
    }
    
    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`
    }