代码之家  ›  专栏  ›  技术社区  ›  Wizzardzz

UITableView索引XPath和最大可重用单元格数

  •  0
  • Wizzardzz  · 技术社区  · 8 年前

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

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

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

    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()
    }
    }
    

    TableView功能:

    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`
    }
    

    }

    CoreData功能:

    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)")
        }
    }
    
    }
    
    2 回复  |  直到 8 年前
        1
  •  1
  •   Andreas Oetjen    8 年前

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

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

    您需要做的是:

    • 去除 func updateCellValue() 。如果您认为必须更新视图,请致电 reloadData() reloadRowsAtIndexPaths... 在表格视图中。这将导致调用适当的数据源方法,如。。。
    • 在…的结尾 tableView(cellForRowAt:) ,您呼叫 updateCellValueLabel 使用出列的单元格,然后将更新标签。
    • 您还应该修改 更新CellValueLabel 然后交上 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`
    }
    

    所以 更新CellValueLabel 获得额外的 atRow 参数,以访问 cryptos newCryptos 阵列。

        2
  •  0
  •   Tom E    8 年前

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