mic
mic

Reputation: 1273

Swift 3 button.addTarget not working on all table cells

I have created an app to allow users to store various voice recordings against reviews. When I display this a table and the data is populated with the following code:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let row = (indexPath as NSIndexPath).item
    let cell = tableView.dequeueReusableCell(withIdentifier: "voiceRecordingCell", for: indexPath) as! VoiceRecordingTableViewCell
    let voiceRecording = self.voiceRecordings[row] as! NSDictionary

    let isoFormatter = DateFormatter()
    isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'.000Z'"
    let createdAt = isoFormatter.date(from: voiceRecording["created_at"] as! String)
    self.recordingIndexPaths.insert(indexPath, at: row)

    cell.recording = voiceRecording
    cell.date.text = getDateFormatter("dd-MM-y").string(from: createdAt!)
    cell.time.text = getDateFormatter("HH:mm").string(from: createdAt!)
    cell.length.text = voiceRecording["length"] as? String
    cell.location.text = voiceRecording["location"] as? String

    let audioPlayerController = self.storyboard?.instantiateViewController(withIdentifier: "AudioPlayerController") as! AudioPlayerController

    audioPlayerController.voiceRecording = voiceRecording

    cell.audioPlayer.addSubview(audioPlayerController.view)
    self.addChildViewController(audioPlayerController)
    audioPlayerController.didMove(toParentViewController: self)

    cell.deleteRecordingButton.tag = row
    cell.deleteRecordingButton.addTarget(self, action: #selector(deleteRecordingPressed(_:)), for: .touchUpInside)

    return cell
}

The cells all appear to be rendering correctly however for the cells that are not initially rendered with the page, the ones I have to scroll down to view, when I click on the buttons either on the audio player controls or the deleteRecordingButton nothing happens, its as though the addTarget is not being set. The code to set the buttons is being called and doesn't create an error, its just not applying to those button.

The buttons that are initially displayed on the screen have the correct actions and all work perfectly so I know that works.

I'm really at a loss as to what is causing this. I did try searching google and stackoverflow but I've not found anything. Any assistance with this would be greatly received.

--------------- UPDATE -----------

I just tried putting some breakpoints in

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

}

That also only works get called on the top 2 cells or the top one if in landscape!

Upvotes: 0

Views: 829

Answers (2)

mic
mic

Reputation: 1273

Finally I have figured this out...

This was happening because I was using a scrollview with to scroll up and down the table without using the tables native scroll functionality.

When using the tables scroll functionality the buttons are applied the actions as they are brought into the view. This is handled by the func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { method. However when you do it the way I was it initially renders all the cells and the ones that are off the screen don't work!

Its a little bit annoying working in this way but at least I now know about it.

Upvotes: 0

SWIE
SWIE

Reputation: 76

Since the cell gets reused all the time the reference get lost. Try something like this:

class ButtonTableViewCell: UITableViewCell {

    typealias TapClosure = () -> Void

    var buttonTapped: TapClosure?

    @IBAction func buttonTouchUpInside(sender: UIButton) {
        buttonTapped?()
    }

}

extension TestViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! ButtonTableViewCell

        cell.buttonTapped = {
            print("Button tapped")
        }

        return cell
    }

}

And another tipp. Never init an DateFormatter in cellForRowAtIndexPath. Instead create it in viewDidLoad or in a static struct for reuse.

Upvotes: 2

Related Questions