MachTurtle
MachTurtle

Reputation: 220

Remove objects from an array using UISwitch

A little context - I have one ViewController called SelectedListItemsViewController that has a TableView populated by an array of Realm Model Objects called selectedListItems. There's an Add bar button item that navigates to another ViewController called AllListItemsViewController that has a TableView populated by an array of Realm Model Objects called allListItems, with each cell containing a UISwitch.

Both of the aforementioned arrays are based off the same class, which has a boolean property called isSelected. In my code, I currently have it set up so that when I toggle the UISwitch in the cell in the AllListItemsViewController, if it turns 'on' the isSelected property at that indexPath.row of allListItems is changed to true and the object is appended to the selectedListItems array. This part seems to work fine.

Toggling the switch to 'off' causes the isSelected property of that indexPath.row of allListItems to change to false and the object is removed at its index from selectedListItems. This works in most cases, but there are times where toggling the UISwitch of cells 'off' in the wrong order (if they were turned 'on' at index 0, then 1, then 2, and then I try to turn index 1 'off') then it crashes the simulation, presumably due to the index being out of range.

I'm very amateur when it comes to programming, so I'm sure the following code is sloppy and I doubt I'm achieving my objective in the best/most efficient way.

// The following arrays are for use with UISegmentedControl (other three removed for brevity)
// This is an array of 'Appearance' list items created from allListItems
let appearanceArray = allListItems.filter{
    $0.category.rangeOfString("Appearance") != nil
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath:
    NSIndexPath) -> UITableViewCell {
    let cellIdentifier = "Cell"
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! AllListItemsTableViewCell

    // This is a string value for the cell at indexPath.row
    var listItemInCell = ""

    // This is an array of all listItems in allListItems, and used with indexOf to check boolean status of isSelected inside of allListItems array
    let arrayAll = allListItems.map{($0.listItem)}

    // This is an array of all listItems in selectedListItems, and used to track remove indexes
    let arraySelected = selectedListItems.map{($0.listItem)}


    switch(segmentedControl.selectedSegmentIndex)
    {

    case 0: // Case 0 of 4, the others removed for brevity of this question

        listItemInCell = appearanceArray[indexPath.row].listItem

        // Index of listItemInCell inside of the array of allListItems' listItems
        let indexOf = arrayAll.indexOf(listItemInCell)!

        cell.listItemLabel.text = listItemInCell

        // Tracks UISwitch activity
        cell.callback = { (tableViewCell, switchState) in
            if self.tableView.indexPathForCell(tableViewCell) != nil {

                // do something with index path and switch state
                if switchState == true {
                    allListItems[indexOf].isSelected = true
                    selectedListItems.append(self.appearanceArray[indexPath.row])
                } else {
                    allListItems[indexOf].isSelected = false
                    let indexInSelected = arraySelected.indexOf(listItemInCell)!
                    selectedListItems.removeAtIndex(indexInSelected)
                }
            }
        }

        if appearanceArray[indexPath.row].isSelected {
            cell.toggleIsSelected.on = true
        } else {
            cell.toggleIsSelected.on = false
        }

        break

Is there a better way of going about this that doesn't have edge cases that cause runtime errors? One thought I had was that recording the index of selectedListItems might not be the best route since it's a global variable and computed lazily, so it isn't always up to date. Another thought is that something gets lost in translation when I'm creating arrays of the object's property to track an index, instead of being able to find the index of a given spot in the array of objects itself.

Upvotes: 0

Views: 166

Answers (1)

MachTurtle
MachTurtle

Reputation: 220

After flailing around at it for the last 3 hours, I figured it out. In my else case of switchState, instead of using

                    let indexInSelected = arraySelected.indexOf(listItemInCell)!
                    selectedListItems.removeAtIndex(indexInSelected)

I instead used

                    let objectToRemove = self.appearanceArray[indexPath.row]

                    for object in selectedListItems {
                        if object == objectToRemove {
                            selectedListItems.removeAtIndex(selectedListItems.indexOf(objectToRemove)!)
                        }
                    }

Which seems to have completely fixed my index-out-of-range issue I was having with some delete cases. It also looks like I can get rid of a decent amount of the unnecessary code I have that was supporting the original two lines.

Upvotes: 0

Related Questions