Search code examples
iosuitableviewcore-datauibuttonnsfetchedresultscontroller

UITableViewCell button tags return desired IndexPath in UISearchController, not FetchedResultsController


I implemented an NSFetchedResultsController on a UITableView in a Core Data project in Swift 2.0. Additionally, I have a UISearchController implemented. Everything works perfectly with the exception of the behavior I'm encountering on my custom UITableViewCell buttons.

When UISearchController is active, the customTableViewCell's buttons work as they should. If I click the same button when the fetchedResultsController is displaying its results, the method thinks Index 0 is the sender, regardless of which button I click.

func playMP3File(sender: AnyObject) {

    if resultsSearchController.active {
        // ** THIS WORKS **
        // get a hold of my song
        // (self.filteredSounds is an Array)
        let soundToPlay = self.filteredSounds[sender.tag]
        // grab an attribute
        let soundFilename = soundToPlay.soundFilename as String
        // feed the attribute to an initializer of another class
        mp3Player = MP3Player(fileName: soundFilename)
        mp3Player.play()
    } else {

        // ** THIS ALWAYS GETS THE OBJECT AT INDEX 0 **
        let soundToPlay = fetchedResultsController.objectAtIndexPath(NSIndexPath(forRow: sender.tag, inSection: (view.superview?.tag)!)) as! Sound
        // OTHER THINGS I'VE TRIED
        // let soundToPlay = fetchedResultsController.objectAtIndexPath(NSIndexPath(forRow: sender.indexPath.row, inSection: (view.superview?.tag)!)) as! Sound
        // let soundToPlay: Sound = fetchedResultsController.objectAtIndexPath(NSIndexPath(index: sender.indexPath.row)) as! Sound
        let soundFilename = soundToPlay.soundFilename as String
        mp3Player = MP3Player(fileName: soundFilename)
        mp3Player.play()
    }
}

Here's an abbreviated version of my cellForRowAtIndexPath to show I'm setting up the cells' buttons:

let customCell: SoundTableViewCell = tableView.dequeueReusableCellWithIdentifier("customCell", forIndexPath: indexPath) as! SoundTableViewCell

if resultsSearchController.active {
    let sound = soundArray[indexPath.row]
    customCell.playButton.tag = indexPath.row
} else {
    let sound = fetchedResultsController.objectAtIndexPath(indexPath) as! Sound
    customCell.playButton.tag = indexPath.row
}

    // add target actions for cells
    customCell.playButton.addTarget(self, action: "playMP3file:", forControlEvents: UIControlEvents.TouchUpInside)

I've tried a few other approaches I've found here, such as translating CGPoints to IndexPaths, etc. without much luck. Everything that looked promising in the compiler crashed when I clicked the button in the simulator.

Thank you for reading.

Update Installed Xcode 7.1, rebooted, cleaned caches, nuked derived data, did a cold boot.

Solution

Tags will get the job done in many cases (such as getting the location in an Array) and get lots of votes here, but as I've learned, they don't work all the time. Thank you to Mundi for pointing me towards a more robust solution.

// this gets the correct indexPath when resultsSearchController is not active
let button = sender as! UIButton
let view = button.superview
let cell = view?.superview as! SoundTableViewCell
let indexPath: NSIndexPath = self.tableView.indexPathForCell(cell)!
let soundToPlay = fetchedResultsController.objectAtIndexPath(indexPath) as! Sound

Solution

  • I've tried a few other approaches I've found here, such as translating CGPoints to IndexPaths, etc. without much luck.

    Translating points is indeed the most robust solution. This answer contains the correct code.