29

I know this issue is already been asked few times in SO. Despite trying those out, I am still unable to solve my problem.

I am using a UITableView inside a UIViewController. I have a custom UITableViewCell which has couple of buttons in it. However, I am not able to make the Button respond to Click event.

The development environment is iOS 9 and Swift 2

Snippets used :

BranchNearMeTableViewCell.swift contains

@IBOutlet weak var btnDetails: UIButton!

view controller class

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("branchNearTableCell") as! BranchNearMeTableViewCell

 cell.btnDetails.tag = indexPath.row
        cell.btnDetails.addTarget(self, action: "showDetails:", forControlEvents: .TouchUpInside)

}

func showDetails(sender: UIButton){

        print("Button Pressed:")
    }

Additional Info:

TableView and TableCellView has User interaction disabled in Interface builder since don't want the entire cell to be clickable.

UIButton inside TableViewCell has User Interaction enabled.

Being an iOS noob, I may be making a silly mistake which I might have overlooked.

Similar questions that I checked include:

SO1

SO2

SO3

I Deeply appreciate any help regarding this question.

Community
  • 1
  • 1
user2695433
  • 2,013
  • 4
  • 25
  • 44

18 Answers18

84

I faced a similar issue. I was programmatically adding an UIButton to the UITableViewCell via addSubview. The button would not respond to touch events. Using Debug View Hierarchy, I finally discovered that any subviews added to the UITableViewCell was behind contentView, which was blocking user input from reaching the UIButton. The issue was resolved by adding the UIButton to contentView instead of the UITableViewCell.

Sargis
  • 1,196
  • 1
  • 18
  • 31
Peter Liaw
  • 961
  • 1
  • 7
  • 6
12

I would have userInteractionEnabled set to true on the table view cell as well. I would prevent taps using the UITableView allowsSelection to false

Also remember to remove the target and action in tableView:cellForRowAtIndexPath: since the cells are recycled, the button might already have the target and action, it might add a second.

Kyle Redfearn
  • 2,172
  • 16
  • 34
12

I found a simple solution:

Inherits UITableViewCell, and override init()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    //init subviews, eg. self.switch = UISwitch()
    super.init(style: style, reuseIdentifier: reuseIdentifier)
                
    // add this line magic code 
    contentView.isUserInteractionEnabled = true
                
    //add subviews, e.g. self.addSubView(self.switch)
}
luffy
  • 121
  • 1
  • 4
  • 3
    Please add some explanation to your answer such that others can learn from it – Nico Haase Jan 25 '21 at 15:42
  • The documentation for `isUserInteractionEnabled` indicates that "The default value of this property is true", therefore, this shouldn't need to be explicitly set. – Derek Lee Jul 18 '23 at 16:45
3

You only have to do (in ViewDidLoad):

mTableView.delaysContentTouches = false
Eray Alparslan
  • 806
  • 7
  • 12
3

For programmatically created views, the only thing to remember is to declare buttons using lazy var in UITableViewCell. And also add subviews to contentView instead of the cell itself For example:

class CounterCell: UITableViewCell {

    lazy var incrementButton: UIButton = {
        let button = UIButton()
        button.setTitle("+", for: .normal)
        button.addTarget(self, action: #selector(incrementAction), for: .touchUpInside)
        return button
    }()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        contentView.addSubview(incrementButton)
        // Your constrains here
    }

    @objc func incrementAction() {
    }

}

When using programmatically views, there's no need to add .userInteractionEnabled flags.

Then to take the action out of the cell, just add a delegate and assign it from the UITableViewDataSource.

Camilo Ortegón
  • 3,414
  • 3
  • 26
  • 34
3

I came across this issue today, with a button inside a static UITableview cell, that was not responding to user events. I realised the 'Content View' of the cell also has a 'User Interaction Enabled' tick box. Make sure you select the 'Content View' inside the UITableview cell in your Document Outline menu, then tick the box for 'User Interaction Enabled' in the Attributes Inspector - see attached photo for reference. 'User Interaction Enabled' also needs to be checked for the cell for this to work. Hope this helps. XCcode screen shot

1

Also, make sure you are adding target actions to your buttons outside their setup. So instead of

let button: UIButton = {
    //addTarget...
}()

you can have a function to set up your buttons after something happens:

func setButtonsUp() {
    // myButton.addTarget
}
Taiwosam
  • 469
  • 7
  • 13
1

For anyone else struggling, here's my solution:

sendSubviewToBack(cell.contentView)

The thing that there's now an extra UITableViewCellContentView layer which blocks interaction with views behind it. Related issue: An extra UITableViewCellContentView overlay appears in a TableView on iOS 14 preventing taps, but works fine on iOS 13

Ilya Shevyryaev
  • 756
  • 6
  • 8
0

Ad a first sight nothing seems to be wrong with your code.
So I suggest you to add a background color to the superview of the button, why? because if the button is outside the frame of its superview it will never receive touches.
If you see that the button is not inside the background color probably you have an issue positioning the item, check constraints or whatever you are using. Check also the frame of the button.
You can also do both by inspecting the view at runtime, here a tutorial.

Andrea
  • 26,120
  • 10
  • 85
  • 131
  • Thanks Andrea, tried your suggession by adding background color, the button is positioned within the superview color. – user2695433 Feb 12 '16 at 08:31
0

I dont know what wrong in the code but i can suggest which i personally use and it works for me

In BranchNearMeTableViewCell.swift

@IBOutlet var btnDetails: UIButton!
@IBAction func btnDetailsClick(sender: AnyObject) {
    tapButton?(self)
}
var tapButton: (UITableViewCell -> Void)?

In Table view controller

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("branchNearTableCell") as! BranchNearMeTableViewCell

 cell.tapButton = {(user) in
             //User will be tablecell here do whatever you want to do here
        }

}

So if you click on button in table cell this cell.tapButton will be called you can do whatever you want to do here

Ajay Beniwal
  • 18,857
  • 9
  • 81
  • 99
  • May I know which all properties you have enabled and disabled in Design like User Interaction and Selection for TableView and TableViewCell – user2695433 Feb 12 '16 at 09:10
  • user interaction is enabled by default for button and if button interaction is enabled it should work irrespective of tableviewproperties – Ajay Beniwal Feb 12 '16 at 09:14
  • share gist for xib and view controllers files then only we can debug the issue more – Ajay Beniwal Feb 12 '16 at 09:21
0

The only things we need to do is in cellForRowAt just put:

cell.selectionStyle = .none

in this way, UITableview will bypass the touch of selecting cells and allow buttons inside our cells to be clickable.

Sajjad Sarkoobi
  • 827
  • 8
  • 18
0
  • set cell and cell content view isUserInteractionEnabled = true
  • Add Tapgesture to the button
  • Add a closure to handle gesture action
Francis Ken
  • 71
  • 1
  • 1
0

Add target for that button.

button.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)

Set tag of that button since you are using it.

button.tag = indexPath.row

Achieve this by subclassing UITableViewCell. button on that cell, connect it via outlet.

Make sure button.isUserInteractionEnabled = true

To get the tag in the connected function:

@objc func connected(sender: UIButton){
    let buttonTag = sender.tag
}
AzeTech
  • 623
  • 11
  • 21
0

Make sure that ALL of tableView's superviews do have isUserInteractionEnabled set to true

0

User interaction was already enabled for my UIButton. The thing that worked for me is switching the stackView distribution to "Fill".

0

In your UITableview cell, add the subviews in the ContentView like:

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
{
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    contentView.addSubview(button)
}
0

Recently had the issue of my tableview custom buttons not registering button presses. Peter Liaws answer is correct, the contentView is blocking the cell from registering buttons. However, by adding the subviews to the content view, your layout could become overly complicated, especially if you're using dynamic height based on your cells constraints. I've found that by simply using:

    contentView.isUserInteractionEnabled = false

You're able to interact with the buttons

Brandon Bravos
  • 390
  • 1
  • 2
  • 10
0

In my case, even though I was adding the button to the contentView, I made the mistake of forgetting to set an auto layout constraint pinning the UIButton to the bottom of the superview, and therefore the contentView size was still the default UITableViewCell size. I only discovered this when checking the Debug View Hierarchy which showed the content view's position compared to the UIButton. (I'll note this mistake as one drawback to managing auto-layout constraints programmatically.)

The content view is the selected light blue view, and with only the top portion of the button being "within" that view, this was the only part that was tappable:

Xcode Debug View Hierarchy Content View Cut Off

Setting an auto layout constraint to pin the button to the bottom of the view resolved this issue:

Xcode Debug View Hierarchy Content View Includes Button

Derek Lee
  • 3,452
  • 3
  • 30
  • 39