This question already has answers here:
iOS get specific UITableViewCell
(5 answers)
Correct way to setting a tag to all cells in TableView
(3 answers)
Closed 5 years ago.
I have a cell with a button inside. In my cellForRowAtIndexPath method i refeer to my uibutton in this way:
UIButton *Button= (UIButton *) [cell viewWithTag:3];
I have found a solution, something like this:
Button.tag = indexPath.row;
[Button addTarget:self action:#selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
-(void)yourButtonClicked:(UIButton*)sender
{
if (sender.tag == 0)
{
// Your code here
}
}
But my button.tag is already used to identify the UIbutton view from the others views inside the cell, i can't use it for the indexpath.row. How i can do in my case?
EDIT
Finally i have made a Category for the UIButton, it seems the faster ad reusable solutions but every answers here is a valid option
Create custom button subclass of UIButton and define property based on your need. Button.tag is a default identifier. In custom class you can give as many property as you want. Then user this Custom button class instead of UIButton. And user custom properties followed by dot(.) as like as tag.
You need not access the button using tag and then set Selectorfor each cell. I believe you can achieve much cleaner approach
Step 1:
In your Custom cell, drag the IBAction from button in cell to your custom cell.
#IBAction func buttonTapped(_ sender: Any) {
//wait for implementation
}
Step 2:
In your custom cell, now declare a protocol
protocol CellsProtocol : NSObjectProtocol {
func buttonTapped(at index : IndexPath)
}
and while in being same class, create few variables as well.
weak var delegate : CellsProtocol? = nil
var indexPath : IndexPath! = nil
we will see the usage of these variables soon :)
Step 3:
Lets get back to IBAction we dragged just now
#IBAction func buttonTapped(_ sender: Any) {
self.delegate?.buttonTapped(at: self.indexPath)
}
Step 4:
Now you can get back to your UITableViewController and confirm to the protocol you declared just now
extension ViewController : CellsProtocol {
func buttonTapped(at index: IndexPath) {
//here you have indexpath of cell whose button tapped
}
}
Step 5:
Now finally update your cellForRowAtIndexPath as
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MyTableViewCell = ;
cell.indexPath = indexPath
cell.delegate = self
}
}
Thats it :) Now you have a button action which tells you, button in which cell tapped :) Hope it helps :) This is more generic approach, because even if you have multiple uicomponents you can still use this approach.
EDIT 1:
In comments below rmaddy pointed out that having a indexPath as a property cell might lead to issues and cell should not care to know its indexPath and protocol should be modified to return the cell rather than returning the index path.
Quoting comment :
Not necessarily. You are assuming reloadData is being called. You can
have a visible cell and then insert a row above it. That visible cell
is not updated and its indexPath is not different but the cell's
indexPath property that you have is not updated. Use the cell itself
as the argument. It's much better than passing the index path. If the
index path is needed by the delegate, the delegate can ask the table
view what the cell's current index path is. A cell should never care
or know what its index path is.
The statement above makes sense especially the fact that A cell should never care or know what its index path is. Hence updating my answer below
Step 1:
Go ahead and delete the indexPath property in cell :)
Step 2:
Modify protocol to
protocol CellsProtocol : NSObjectProtocol {
func buttonTapped(in cell : UITableViewCell)
}
Step 3:
Modify your IBAction as
#IBAction func buttonTapped(_ sender: Any) {
self.delegate?.buttonTapped(in: self)
}
Step 4:
Finally modify your protocol confirmation in your ViewController to
extension ViewController : CellsProtocol {
func buttonTapped(in cell: UITableViewCell) {
let indexPath = self.tableView.indexPath(for: cell)
//here you have indexpath of cell whose button tapped
}
}
Thats it :)
Another option - use a "call back" block.
This assumes you have a Prototype cell with "btnCell" identifier, containing a UIButton connected to the - (IBAction)buttonTapped:(id)sender method shown below...
Your cell class:
//
// WithButtonTableViewCell.h
//
// Created by Don Mag on 11/15/17.
//
#import <UIKit/UIKit.h>
#interface WithButtonTableViewCell : UITableViewCell
- (void)setButtonTappedBlock:(void (^)(id sender))buttonTappedBlock;
#end
//
// WithButtonTableViewCell.m
//
// Created by Don Mag on 11/15/17.
//
#import "WithButtonTableViewCell.h"
#interface WithButtonTableViewCell ()
#property (copy, nonatomic) void (^buttonTappedBlock)(id sender);
#end
#implementation WithButtonTableViewCell
- (IBAction)buttonTapped:(id)sender {
// call back if the block has been set
if (self.buttonTappedBlock) {
self.buttonTappedBlock(self);
}
}
#end
Your table view controller class:
//
// WithButtonTableViewController.h
//
// Created by Don Mag on 7/12/17.
//
#import <UIKit/UIKit.h>
#interface WithButtonTableViewController : UITableViewController
#end
//
// WithButtonTableViewController.m
//
// Created by Don Mag on 11/15/17.
//
#import "WithButtonTableViewController.h"
#import "WithButtonTableViewCell.h"
#interface WithButtonTableViewController ()
#end
#implementation WithButtonTableViewController
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
WithButtonTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"btnCell" forIndexPath:indexPath];
[cell setButtonTappedBlock:^(id sender) {
NSLog(#"Button in cell at row %ld in section: %ld was tapped", (long)indexPath.row, (long)indexPath.section);
// sender is the cell
// do whatever else you want here...
}];
return cell;
}
#end
Now, when the button in the cell is tapped, it will "call back" to the view controller, at which point the code inside the Block will be executed.
I am using a custom cell class with a button in it.
import UIKit
protocol TouchDelegateForShotsCell {
func touchedTwitterButton()
}
class ShotsCell : UITableViewCell {
let touchDelegateForShotsCell: TouchDelegateForShotsCell = MasterViewController()
#IBAction func twitterButtonPressed(sender: AnyObject) {
touchDelegateForShotsCell.touchedTwitterButton()
}
}
When the button is pressed I call a delegate function in the MasterViewController which contains the following standard code for sharing on Twitter:
func touchedTwitterButton() {
var shareToTwitter :SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
self.presentViewController(shareToTwitter, animated: true, completion: nil)
}
I receive the error :
"Attempt to present ViewController whose view is not in the window hierarchy!".
I've tried several workarounds. For example, creating a separate view controller and performing a segue from the button. That works in part, however, when I try to pass data I get another error :
"'NSInvalidArgumentException', reason: Receiver ViewController has no segue with identifier 'segueToTwitterShare'".
Which isn't true. For this I tried deleting my app and restarting xcode which worked for some people. However, the problem persists.
let touchDelegateForShotsCell: TouchDelegateForShotsCell = MasterViewController()
It's not the good delegate. When you write MasterViewController() you create a new instance of MasterViewController (so it is not in the window hierarchy) but you don't pass the intense who is executed.
I suggest you an example to better understand what is the problem and see where changes is needed.
I add an init() function into ShotsCell :
class ShotsCell : UITableViewCell {
var touchDelegateForShotsCell: TouchDelegateForShotsCell!
init(withDelegate delegate: MasterViewController){
super.init(style: UITableViewCellStyle.Default, reuseIdentifier: "cellIdentifier")
touchDelegateForShotsCell = delegate
}
#IBAction func twitterButtonPressed(sender: AnyObject) {
touchDelegateForShotsCell.touchedTwitterButton()
}
}
And pass your MasterViewController to your cell in cellForRowAtIndexPath :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = ShotsCell(withDelegate: self)
return cell
}
Hope that helps. It works for me.
The code below works but there is probably a better way. My goal is to call a UIViewController function from the UITableCell when aborting from edit mode.
I am doing this by setting an instantiated UIViewController reference to every UITableViewCell and then calling a function, CancelDelete(), on the UITableViewCell state change.
The code seems inefficient since for every MyCell I first instantiate a placeholder MyViewContoller as the public variable and then replace it with reference to the UIViewController when the UITableView initializes.
Is there a better way to do this?
class MyCell : UITableViewCell
{
var previousState : UITableViewCellStateMask = UITableViewCellStateMask.allZeros
// This holds a reference to the parent view controller
// Seems wasteful to instantiate since it gets replaced
var controller:MyViewController = MyViewController()
// This is called when the user aborts edit mode
override func willTransitionToState(state: UITableViewCellStateMask) {
if state & UITableViewCellStateMask.ShowingEditControlMask != nil {
if previousState & UITableViewCellStateMask.ShowingDeleteConfirmationMask != nil {
// send notification to controller
controller.CancelDelete(self)
}
}
previousState = state
}
}
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Give cell access to this controller
var cell:MyCell = tableView.dequeueReusableCellWithIdentifier("cell") as MyCell
cell.controller = self
cell.tag = indexPath.row
}
// This is called from the table cell
func CancelDelete(cell:MyCell) {
editButtons[cell.tag].hidden = false
}
}
Change the type of controller to MyViewController! instead of MyViewController. Also, set it to a default value of nil.
The declaration of controller should look like this:
var controller: MyViewController! = nil
If you have any questions about types that end with an exclamation mark (!), look at:
https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
(in the section named Optionals).
Usually, I change view like this:
var goBackToMainView:MainViewController = self.storyboard?.instantiateViewControllerWithIdentifier("navigation2") as MainViewController
self.presentViewController(goBackToMainView, animated: true, completion: nil)
But if I try to do this in my custom UITableViewCell when the user tap a button, it gives me errors like customTableViewCell does not have a member named 'storyboard' and 'presentViewController'
How can I change views in my TableViewCell ?
Have you tried adding as a subview?
Example:
var tableViewCell = UITableViewCell(frame: aFrame)
tableViewCell.addSubview(aView)
Why do it in the UITableViewCell subclass? You should be doing your view management in the ViewController.
In your view controller:
import UIKit
// Take note of UITableViewDelegate, you'll need it to be able to use
// tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
class MyTableViewController: UITableViewController, UITableViewDelegate {
// Assuming that you have a storyboard variable in AppDelegate
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
// .. more code here like viewDidLoad, etc.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var viewController = appDelegate.storyboard.instantiateViewControllerWithIdentifier("MainViewController") as MainViewController
// This will probably appear as a modal
self.presentViewController(viewController, animated: true, completion: nil)
}
}
Note: I just did a quick playground for the code above so you may need to unwrap optionals.
If you have your view controller embedded in a navigation controller, you might want to use Unwind Segues. More information about that here: What are Unwind segues for and how do you use them?
Cheers!
I have a view controller with a table view and a separate nib for the table cell template. The cell template has some buttons. I want to access the button click along with the index of the cell clicked inside the view controller where I have defined the Table view.
So I have ViewController.h and ViewController.m where I have the UITableView and TableTemplate.h, TableTemplate.m and TableTemplate.xib where I have the nib defined. I want the button click event with cell index in ViewController.m.
Any help on how can I do that?
1) In your cellForRowAtIndexPath: method, assign button tag as index:
cell.yourbutton.tag = indexPath.row;
2) Add target and action for your button as below:
[cell.yourbutton addTarget:self action:#selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
3) Code actions based on index as below in ViewControler:
-(void)yourButtonClicked:(UIButton*)sender
{
if (sender.tag == 0)
{
// Your code here
}
}
Updates for multiple Section:
You can check this link to detect button click in table view for multiple row and section.
Delegates are the way to go.
As seen with other answers using views might get outdated. Who knows tomorrow there might be another wrapper and may need to use cell superview]superview]superview]superview]. And if you use tags you would end up with n number of if else conditions to identify the cell. To avoid all of that set up delegates. (By doing so you will be creating a re usable cell class. You can use the same cell class as a base class and all you have to do is implement the delegate methods.)
First we need a interface (protocol) which will be used by cell to communicate(delegate) button clicks. (You can create a separate .h file for protocol and include in both table view controller and custom cell classes OR just add it in custom cell class which will anyway get included in table view controller)
#protocol CellDelegate <NSObject>
- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data;
#end
Include this protocol in custom cell and table view controller. And make sure table view controller confirms to this protocol.
In custom cell create two properties :
#property (weak, nonatomic) id<CellDelegate>delegate;
#property (assign, nonatomic) NSInteger cellIndex;
In UIButton IBAction delegate click : (Same can be done for any action in custom cell class which needs to be delegated back to view controller)
- (IBAction)buttonClicked:(UIButton *)sender {
if (self.delegate && [self.delegate respondsToSelector:#selector(didClickOnCellAtIndex:withData:)]) {
[self.delegate didClickOnCellAtIndex:_cellIndex withData:#"any other cell data/property"];
}
}
In table view controller cellForRowAtIndexPath after dequeing the cell, set the above properties.
cell.delegate = self;
cell.cellIndex = indexPath.row; // Set indexpath if its a grouped table.
And implement the delegate in table view controller:
- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data
{
// Do additional actions as required.
NSLog(#"Cell at Index: %d clicked.\n Data received : %#", cellIndex, data);
}
This would be the ideal approach to get custom cell button actions in table view controller.
Instead of playing with tags, I took different approach. Made delegate for my subclass of UITableViewCell(OptionButtonsCell) and added an indexPath var. From my button in storyboard I connected #IBAction to the OptionButtonsCell and there I send delegate method with the right indexPath to anyone interested. In cell for index path I set current indexPath and it works :)
Let the code speak for itself:
Swift 3 Xcode 8
OptionButtonsTableViewCell.swift
import UIKit
protocol OptionButtonsDelegate{
func closeFriendsTapped(at index:IndexPath)
}
class OptionButtonsTableViewCell: UITableViewCell {
var delegate:OptionButtonsDelegate!
#IBOutlet weak var closeFriendsBtn: UIButton!
var indexPath:IndexPath!
#IBAction func closeFriendsAction(_ sender: UIButton) {
self.delegate?.closeFriendsTapped(at: indexPath)
}
}
MyTableViewController.swift
class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OptionButtonsDelegate {...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "optionCell") as! OptionButtonsTableViewCell
cell.delegate = self
cell.indexPath = indexPath
return cell
}
func closeFriendsTapped(at index: IndexPath) {
print("button tapped at index:\(index)")
}
This should help :-
UITableViewCell* cell = (UITableViewCell*)[sender superview];
NSIndexPath* indexPath = [myTableView indexPathForCell:cell];
Here sender is the UIButton instance that is sending the event.
myTableView is the UITableView instance you're dealing with.
Just get the cell reference right and all the work is done.
You may need to remove the buttons from cell's contentView &
add them directly to UITableViewCell instance as it's subview.
Or
You can formulate a tag naming scheme for different UIButtons in cell.contentView.
Using this tag, later you can know the row & section information as needed.
Following code might Help you.
I have taken UITableView with custom prototype cell class named UITableViewCell inside UIViewController.
So i have ViewController.h, ViewController.m and TableViewCell.h,TableViewCell.m
Here is the code for that:
ViewController.h
#interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *tblView;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return (YourNumberOfRows);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = #"cell";
__weak TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (indexPath.row==0) {
[cell setDidTapButtonBlock:^(id sender)
{
// Your code here
}];
}
return cell;
}
Custom cell class :
TableViewCell.h
#interface TableViewCell : UITableViewCell
#property (copy, nonatomic) void (^didTapButtonBlock)(id sender);
#property (strong, nonatomic) IBOutlet UILabel *lblTitle;
#property (strong, nonatomic) IBOutlet UIButton *btnAction;
- (void)setDidTapButtonBlock:(void (^)(id sender))didTapButtonBlock;
#end
and
UITableViewCell.m
#implementation TableViewCell
- (void)awakeFromNib {
// Initialization code
[self.btnAction addTarget:self action:#selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)didTapButton:(id)sender {
if (self.didTapButtonBlock)
{
self.didTapButtonBlock(sender);
}
}
Note: Here I have taken all UIControls using Storyboard.
Hope that can help you...!!!
Use Swift closures :
class TheCell: UITableViewCell {
var tapCallback: (() -> Void)?
#IBAction func didTap(_ sender: Any) {
tapCallback?()
}
}
extension TheController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TheCell.identifier, for: indexPath) as! TheCell {
cell.tapCallback = {
//do stuff
}
return cell
}
}
The reason i like below technique because it also help me to identify the section of table.
Add Button in cell cellForRowAtIndexPath:
UIButton *selectTaskBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[selectTaskBtn setFrame:CGRectMake(15, 5, 30, 30.0)];
[selectTaskBtn setTag:indexPath.section]; //Not required but may find useful if you need only section or row (indexpath.row) as suggested by MR.Tarun
[selectTaskBtn addTarget:self action:#selector(addTask:) forControlEvents:UIControlEventTouchDown];
[cell addsubview: selectTaskBtn];
Event addTask:
-(void)addTask:(UIButton*)btn
{
CGPoint buttonPosition = [btn convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil)
{
int currentIndex = indexPath.row;
int tableSection = indexPath.section;
}
}
Hopes this help.
Tarun's code doesnt work on iOS7, since the UITableViewCell structure changed and now he would get "UITableViewCellScrollView" instead.
This post Getting UITableViewCell with superview in iOS 7 has a good solution creating a loop to find the correct parent view, regardless of any future changes in the structure. It boils down to creating a loop:
UIView *superView = [sender superview];
UIView *foundSuperView = nil;
while (nil != superView && nil == foundSuperView) {
if ([superView isKindOfClass:[UITableViewCell class]]) {
foundSuperView = superView;
} else {
superView = superView.superview;
}
}
The link has code for a more reusable solution, but this should work.
Its Work For me.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UIButton *Btn_Play = (UIButton *)[cell viewWithTag:101];
[Btn_Play addTarget:self action:#selector(ButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)ButtonClicked:(UIButton*)sender {
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.Tbl_Name];
NSIndexPath *indexPath = [self.Tbl_Name indexPathForRowAtPoint:buttonPosition];
}
Swift 2.2
You need to add target for that button.
myButton.addTarget(self, action: #selector(ClassName.FunctionName(_:), forControlEvents: .TouchUpInside)
FunctionName: connected // for example
And of course you need to set tag of that button since you are using it.
myButton.tag = indexPath.row
You can achieve this by subclassing UITableViewCell. Use it in interface builder, drop a button on that cell, connect it via outlet and there you go.
To get the tag in the connected function:
func connected(sender: UIButton) {
let buttonTag = sender.tag
// Do any additional setup
}
Swift 3 with a Closure
A nice solution is using a closure in a custom UITableViewCell to callback to the viewController for an action.
In cell:
final class YourCustomCell: UITableViewCell {
var callbackClosure: (() -> Void)?
// Configure the cell here
func configure(object: Object, callbackClosure: (() -> Void)?) {
self.callbackClosure = callbackClosure
}
// MARK: - IBAction
extension YourCustomCell {
#IBAction fileprivate func actionPressed(_ sender: Any) {
guard let closure = callbackClosure else { return }
closure()
}
}
In View Controller: Tableview Delegate
extension YourViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let cell: YourCustomCell = cell as? YourCustomCell else { return }
cell.configure(object: object, callbackClosure: { [weak self] in
self?.buttonAction()
})
}
}
fileprivate extension YourViewController {
func buttonAction() {
// do your actions here
}
}
I find it simplest to subclass the button inside your cell (Swift 3):
class MyCellInfoButton: UIButton {
var indexPath: IndexPath?
}
In your cell class:
class MyCell: UICollectionViewCell {
#IBOutlet weak var infoButton: MyCellInfoButton!
...
}
In the table view's or collection view's data source, when dequeueing the cell, give the button its index path:
cell.infoButton.indexPath = indexPath
So you can just put these code into your table view controller:
#IBAction func handleTapOnCellInfoButton(_ sender: MyCellInfoButton) {
print(sender.indexPath!) // Do whatever you want with the index path!
}
And don't forget to set the button's class in your Interface Builder and link it to the handleTapOnCellInfoButton function!
edited:
Using dependency injection. To set up calling a closure:
class MyCell: UICollectionViewCell {
var someFunction: (() -> Void)?
...
#IBAction func didTapInfoButton() {
someFunction?()
}
}
and inject the closure in the willDisplay method of the collection view's delegate:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
(cell as? MyCell)?.someFunction = {
print(indexPath) // Do something with the indexPath.
}
}
If you want to pass parameter value from cell to UIViewController using closure then
//Your Cell Class
class TheCell: UITableViewCell {
var callBackBlockWithParam: ((String) -> ()) = {_ in }
//Your Action on button
#IBAction func didTap(_ sender: Any) {
callBackBlockWithParam("Your Required Parameter like you can send button as sender or anything just change parameter type. Here I am passing string")
}
}
//Your Controller
extension TheController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TheCell.identifier, for: indexPath) as! TheCell {
cell.callBackBlockWithParam = { (passedParamter) in
//you will get string value from cell class
print(passedParamter)
}
return cell
}
}
// Add action in cell for row at index path -tableView
cell.buttonName.addTarget(self, action: #selector(ViewController.btnAction(_:)), for: .touchUpInside)
// Button Action
#objc func btnAction(_ sender: AnyObject) {
var position: CGPoint = sender.convert(.zero, to: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: position)
let cell: UITableViewCell = tableView.cellForRow(at: indexPath!)! as
UITableViewCell
}
for swift 4:
inside the cellForItemAt ,
cell.chekbx.addTarget(self, action: #selector(methodname), for: .touchUpInside)
then outside of cellForItemAt
#objc func methodname()
{
//your function code
}
#Mani answer is good, however tags of views inside cell's contentView often are used for other purposes. You can use cell's tag instead (or cell's contentView tag):
1) In your cellForRowAtIndexPath: method, assign cell's tag as index:
cell.tag = indexPath.row; // or cell.contentView.tag...
2) Add target and action for your button as below:
[cell.yourbutton addTarget:self action:#selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
3) Create method that returns row of the sender (thanks #Stenio Ferreira):
- (NSInteger)rowOfSender:(id)sender
{
UIView *superView = sender.superview;
while (superView) {
if ([superView isKindOfClass:[UITableViewCell class]])
break;
else
superView = superView.superview;
}
return superView.tag;
}
4) Code actions based on index:
-(void)yourButtonClicked:(UIButton*)sender
{
NSInteger index = [self rowOfSender:sender];
// Your code here
}
CustomTableCell.h is a UITableViewCell:
#property (weak, nonatomic) IBOutlet UIButton *action1Button;
#property (weak, nonatomic) IBOutlet UIButton *action2Button;
MyVC.m after imports:
#interface MYTapGestureRecognizer : UITapGestureRecognizer
#property (nonatomic) NSInteger dataint;
#end
Inside "cellForRowAtIndexPath" in MyVC.m:
//CustomTableCell
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
//Set title buttons
[cell.action1Button setTitle:[NSString stringWithString:NSLocalizedString(#"action1", nil)] forState:UIControlStateNormal];
[cell.action2Button setTitle:[NSString stringWithString:NSLocalizedString(#"action2", nil)] forState:UIControlStateNormal];
//Set visibility buttons
[cell.action1Button setHidden:FALSE];
[cell.action2Button setHidden:FALSE];
//Do 1 action
[cell.action1Button addTarget:self action:#selector(do1Action :) forControlEvents:UIControlEventTouchUpInside];
//Do 2 action
MYTapGestureRecognizer *action2Tap = [[MYTapGestureRecognizer alloc] initWithTarget:self action:#selector(do2Action :)];
cancelTap.numberOfTapsRequired = 1;
cancelTap.dataint = indexPath.row;
[cell.action2Button setUserInteractionEnabled:YES];
[cell.action2Button addGestureRecognizer:action2Tap];
MyVC.m:
-(void)do1Action :(id)sender{
//do some action that is not necessary fr data
}
-(void)do2Action :(UITapGestureRecognizer *)tapRecognizer{
MYTapGestureRecognizer *tap = (MYTapGestureRecognizer *)tapRecognizer;
numberTag = tap.dataint;
FriendRequest *fr = [_list objectAtIndex:numberTag];
//connect with a WS o do some action with fr data
//actualize list in tableView
[self.myTableView reloadData];
}
cell.show.tag=indexPath.row;
[cell.show addTarget:self action:#selector(showdata:) forControlEvents:UIControlEventTouchUpInside];
-(IBAction)showdata:(id)sender
{
UIButton *button = (UIButton *)sender;
UIStoryboard *storyBoard;
storyBoard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SecondViewController *detailView = [storyBoard instantiateViewControllerWithIdentifier:#"SecondViewController"];
detailView.string=[NSString stringWithFormat:#"%#",[_array objectAtIndex:button.tag]];
[self presentViewController:detailView animated:YES completion:nil];
}