简体   繁体   中英

UITableView crashes when scrolling / index out of bounds for an array

Index out of bounds exception appears when I scroll UITableView,bug seems to appear in a function named checkTypes. The problem is in this line:

listOfAllCells[i].cellTextBox.text = "\(value)"

This is my ValuesTableViewController with a custom ValuesCell.

    //
//  ValuesTableViewController.swift
//  UnitConverter
//
//

import UIKit

protocol Initializable {
    init()
}
//Treba odraditi kalkulaciju unita za svaki treba popraviti bug koji se desi kod nasledjivanja

class ValuesTableViewController: UITableViewController, Initializable, UITextFieldDelegate {

    @IBOutlet var valuesTableView: UITableView!
    var valueName: String?
    var selectedValueObject: Properties?
    var listOfAllUnitObjects = [AnyObject]()
    var doubleTextValue : Double?
    var listOfAllCells = [ValuesCell]()
    var returningCalculatedValue : Double?
   // var testTextField : UITextField
    //"m²","km²","ha","dm²","cm²","mm²","sq mi", "ac", "sq yd","sq ft", "sq in"
   // var areaValues: [String: Area] = ["m²": Acre(), "km²": SquareKilometer(), "ha": Hectare(), "dm²": SquareDecimeter(), ""]
    //var listOfAllUnitsInClass = [Acre,Hectare,SquareCentimeter,SquareDecimeter,SquareFoot,SquareInch,]





    override func viewDidLoad() {
        super.viewDidLoad()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem
         valuesTableView.register(UINib(nibName: "ValuesCell", bundle: nil), forCellReuseIdentifier: "valueCell")


//        let myclass = stringClassFromString(valuesObject.textValue) as! Properties
//        let instance = myclass.init()
       // print("All units in selected Class : \(listOfAllUnitObjects)")
//       let testLista = testCalculation()
//        for object in testLista {
//            print("Ovo pokusavam da napravim : \(object)")
////        object as! Properties
////        let newType = type(of: object)
////        let createObject = createInstance(typeThing: newType)
//        /*
//        let acreTest = Acre()
//        print(acreTest.ConvertFromMainUnitToUnit(entryValue: 200))
//        print(testLista)
//         */

//        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source

//    override func numberOfSections(in tableView: UITableView) -> Int {
//        // #warning Incomplete implementation, return the number of sections
//        return 0
//    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        let valuesType = configureTable()
        let valuesObject = createInstance(typeThing: valuesType)

      return valuesObject.listOfMainLabels.count

    }
    func textFieldDidBeginEditing(_ textField: UITextField) {
        textField.text = ""
    }
    func textFieldDidEndEditing(_ textField: UITextField) {
        //Ovde nastaje problem jer uzima prethodno unetu vrednost umesto novu

        //if textField.text != "" {
            if let textInputValue = textField.text {
                if Double(textInputValue) != nil {
                    if let doubleTextValueInput = Double(textInputValue){
                        doubleTextValue = doubleTextValueInput
                        //vratiti vrednost textfielda nakon promene da bi se input iskoristio za racunanje
                        let valuesType = configureTable()
                        let valuesObject = createInstance(typeThing: valuesType)
                        if let returnCalculatedValue = returningCalculatedValue {
                            checkTypes(allCells: listOfAllCells, entryValues: returnCalculatedValue, valuesObject: valuesObject)
                            print("Prolazi unesena vrednost za novo racunanje je \(returnCalculatedValue)")
                            valuesTableView.reloadData()
                        }
                        self.view.endEditing(true)
                    }

                }
            }
        //}
    }
    func configureTable() -> Properties.Type {

        //Check to see if the sent string is equal to any of the existing objects
        //if it is equal then create that object and append all of its values to tableView


        //Napravi listu svih objekata pusti u petlju iteraciju vidi da li je string jednak
        //nekom od objekata.text ako jeste stopiraj je i kraj
        if let valueNameUnwrapped = valueName {
        let valueNamesTrimmed = valueNameUnwrapped.trimmingCharacters(in: .whitespaces)
//            print("This is 2nd step: \(valueNamesTrimmed)")
        let allValuesList = AllValues()
        for value in allValuesList.listValues {
            //print("Vrednosti \(value.textValue)")
            switch valueNamesTrimmed {
            case value.textValue:
//                print("Izabrano je \(value.textValue)")
                selectedValueObject = value
                if let selectedObject = selectedValueObject{
                    selectedValueObject?.textValue = value.textValue
                   // selectedValueObject?.colorValue = value.colorValue
                    selectedValueObject?.tagValue = value.tagValue
                    let typeOfObject = type(of: selectedObject)
                    return typeOfObject
                }
            default:
                if let selectedObject = selectedValueObject{
                    selectedValueObject?.textValue = value.textValue
                   // selectedValueObject?.colorValue = value.colorValue
                    selectedValueObject?.tagValue = value.tagValue
                    let typeOfObject = type(of: selectedObject)
                    return typeOfObject
                }
            }
        }
        }
        return type(of: selectedValueObject) as! Properties.Type
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "valueCell", for: indexPath) as! ValuesCell
        if indexPath.row == 0 {
            cell.cellView.backgroundColor = UIColor.white
        }
        let valuesType = configureTable()
        let valuesObject = createInstance(typeThing: valuesType)

        let currentClassName = valuesObject.listOfAllUnits[indexPath.row]
        let unitObject = stringClassFromString(currentClassName)

        //Napraviti da u izabranom Unitu Navigacija bude iste boje kao na prethodnom screenu
        cell.cellMainLbl.text = valuesObject.listOfMainLabels[indexPath.row]
        cell.cellSmallLbl.text = valuesObject.listofSmallLabels[indexPath.row]
//            cell.valueLabel.textColor = UIColor.init(red: CGFloat(rColorValue[indexPath.row]), green: CGFloat(gColorValue[indexPath.row]), blue: CGFloat(bColorValue[indexPath.row]), alpha: 1.0)
//            cell.leftColorView.backgroundColor = UIColor.init(red: CGFloat(rColorValue[indexPath.row]), green: CGFloat(gColorValue[indexPath.row]), blue: CGFloat(bColorValue[indexPath.row]), alpha: 1.0)
        cell.tag = indexPath.row
        listOfAllCells.append(cell)
        //listOfAllUnitObjects.append(unitObject)
        if let unitObjectUnwrapped = unitObject {
            listOfAllUnitObjects.append(unitObjectUnwrapped)
        }
//        for unit in valuesObject.listOfAllUnits {
//            let unitObject = stringClassFromString(unit)
//            if let unitObjectUnwrapped = unitObject {
//                listOfAllUnitObjects.append(unitObjectUnwrapped)
//            }
//
//
//        }

//        //cell.imageView?.image = UIImage(named: fruitName)
//        cell.rightValueImage.image = UIImage(named: "\(indexPath.row)")

        // Configure the cell...
        return cell
    }
    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let valuesType = configureTable()
        let valuesObject = createInstance(typeThing: valuesType)
        checkTypes(allCells: listOfAllCells,entryValues: 1, valuesObject: valuesObject)
        valuesTableView.tableFooterView = UIView()
    }
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let valuesType = configureTable()
        let valuesObject = createInstance(typeThing: valuesType)
        let cell = tableView.cellForRow(at: indexPath) as! ValuesCell
        if indexPath.row != 0 {
           //let changeFirstCellColor = getCellsData()
         //   changeFirstCellColor[0].cellView.backgroundColor = UIColor.init(red: 232, green: 232, blue: 232, alpha: 1)

        }
        cell.cellView.backgroundColor = UIColor.white
//        print("Ovo su glavne vrednosti za racunanje \(listOfAllUnitObjects)")
//        let finalList = listOfAllUnitObjects as? [Properties]
//        print("Ovde je bio pucao program: \(String(describing: finalList))")
//        let mainToUnit = finalList[indexPath.row].ConvertFromMainUnitToUnit(entryValue: 20)
//        let unitToMain = finalList[indexPath.row].ConvertFromUnitToMainUnit(entryValue: 20)
//        cell.cellTextBox.value(forKey: "\(mainToUnit)")
        cell.cellTextBox.delegate = self
        if let doubleValue = doubleTextValue{
            //Ovde nastaje problem jer samo vrati unetu vrednost
            let entryValueReturned = checkTypesForSelected(indexPath: indexPath, cell: cell, entryValues: doubleValue, valuesObject: valuesObject)
            returningCalculatedValue = entryValueReturned
            if let returnCalculatedValue = returningCalculatedValue {
                print("Vracam vrednost od rezultata checkTypes funkcije za uneti broj \(doubleValue) rezultat je : \(returnCalculatedValue)")
            }


        }
        self.view.endEditing(true)


    }
    func checkTypes<T>(allCells: [ValuesCell],entryValues: Double, valuesObject: T){
        if valuesObject is Area {


        let classValues: [Area.Type] = [SquareMeter.self, SquareKilometer.self, Hectare.self, SquareDecimeter.self,
                                        SquareCentimeter.self, SquareMillimeter.self, SquareMile.self, Acre.self,
                                        SquareYard.self, SquareFoot.self, SquareInch.self]

            for i in 0...(listOfAllCells.count - 1){
                let object = classValues[i].init()
                let value = object.ConvertFromMainUnitToUnit(entryValue: entryValues)
                print("Vracam vrednost druge metode za \(i) element je : \(value)")
                listOfAllCells[i].cellTextBox.text = "\(value)"
            }
        }

    }
    func checkTypesForSelected<T>(indexPath: IndexPath, cell: ValuesCell,entryValues: Double, valuesObject: T) -> Double{
        if valuesObject is Area {

            let classValues: [Area.Type] = [SquareMeter.self, SquareKilometer.self, Hectare.self, SquareDecimeter.self,
                                            SquareCentimeter.self, SquareMillimeter.self, SquareMile.self, Acre.self,
                                            SquareYard.self, SquareFoot.self, SquareInch.self]
            if listOfAllUnitObjects[indexPath.row] is SquareMeter.Type {
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareMeter.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            } else if listOfAllUnitObjects[indexPath.row] is SquareKilometer.Type {
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareKilometer.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is Hectare.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? Hectare.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is SquareDecimeter.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareDecimeter.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is SquareCentimeter.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareCentimeter.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is SquareMillimeter.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareMillimeter.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is SquareCentimeter.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareCentimeter.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is SquareMile.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareMile.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is Acre.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? Acre.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is SquareYard.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareYard.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is SquareFoot.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareFoot.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }else if listOfAllUnitObjects[indexPath.row] is SquareInch.Type{
                var testObject = classValues[indexPath.row].init()
                testObject = createInstance(typeThing: (listOfAllUnitObjects[indexPath.row] as? SquareInch.Type)!)
                print("Zavrseno RADI! \(testObject)")
                let value = testObject.ConvertFromUnitToMainUnit(entryValue: entryValues)
                cell.cellTextBox.text = "\(value)"
                return value
            }

        }
        return 0
    }
//    func calculateAllUnits(){
//        let allCells = getCellsData()
//        for cell in allCells {
//            cell.cellTextBox.text = ""
//        }
//    }
    //Pravi objekat od typa Klase
    func createInstance<T>(typeThing:T.Type) -> T where T:Initializable {
        return typeThing.init()
    }

    func stringClassFromString(_ className: String) -> AnyClass! {

        /// get namespace
        let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String;

        /// get 'anyClass' with classname and namespace
        let cls: AnyClass = NSClassFromString("\(namespace).\(className)")!;

        // return AnyClass!
        return cls;
    }
//    func getCellsData() -> [ValuesCell] {
//        var dataArray: [ValuesCell] = []
//        for section in 0 ..< self.valuesTableView.numberOfSections {
//            for row in 0 ..< self.valuesTableView.numberOfRows(inSection: section) {
//                let indexPath = NSIndexPath(row: row, section: section)
//                let cell = self.valuesTableView.cellForRow(at: indexPath as IndexPath) as! ValuesCell
//                dataArray.append(cell)
//            }
//        }
//        return dataArray
//    }

    //Neka prikaze ne keyboard sa slovima nego samo sa brojkama da ogranicimo korisnika
//    func textFieldDidBeginEditing(_ textField: UITextField) {
//
//    }
//    func textFieldDidEndEditing(_ textField: UITextField) {
//        <#code#>
//    }
//    func testCalculation() -> [AnyObject]{
//        let valuesType = configureTable()
//        let valuesObject = createInstance(typeThing: valuesType)
//        let list = valuesObject.listOfAllUnits
//        var listOfAllUnitValues = [AnyObject]()
//        print("Broj koliko ima elementa u listi : \(list.count)")
//        for i in 0...(list.count - 1) {
//            let object1 = stringClassFromString(list[i]) as AnyObject
//            listOfAllUnitValues.append(object1)
//        }
//        return listOfAllUnitValues
//
//    }
    //Napraviti Dictionary key value da bude labela iz celije, a vrednost da bude ta klasa, posle povezati
//    func makeAllUnitObjects() -> [Properties] {
//       let listOfAllCells = getCellsData()
//        for i in 0...listOfAllCells.count {
//            let test = listOfAllCells[i]
//        }
//    }
    /*
    // Override to support conditional editing of the table view.
    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return true
    }
    */

    /*
    // Override to support editing the table view.
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            // Delete the row from the data source
            tableView.deleteRows(at: [indexPath], with: .fade)
        } else if editingStyle == .insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }    
    }
    */

    /*
    // Override to support rearranging the table view.
    override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {

    }
    */

    /*
    // Override to support conditional rearranging of the table view.
    override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        // Return false if you do not want the item to be re-orderable.
        return true
    }
    */

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */

}

I managed to figure out what was the problem, because uitableview generates cells depending on how many cells u can see, my listOfAllCells was appending constantly. Fix was in cellForRowAt:

if valuesObject.listOfMainLabels.count - 1 >= listOfAllCells.count {
            listOfAllCells.append(cell)
}

As you can see 1 if statement fixed the issue thanks for responding to this thread, if anyone has any suggestions about my code I'm gladly to hear cause I just started developing I'm a beginner and every suggestion makes me better.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM