Adding UILongPressGestureRecognizer to UIButton inside custom UITableViewCell - ios

I have a custom cell with various IBOutlets, but on one button I want to add a UILongPressGestureRecognizer for long press gestures. Here is my code (btw outlets are connected correctly and the IBAction method of the button is called correctly):
MyCustomCell.h
#interface MyCustomCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UIButton *myButton;
#property (strong, nonatomic) UILongPressGestureRecognizer *longPressGestureRecognizer;
#end
MyCustomCell.m
- (void)awakeFromNib
{
// Initialization code
self.longPressGestureRecognizer = nil;
}
MyViewController.m
#import MyCustomCell.h
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"MyCell";
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
if (!cell){
cell = [[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
cell.longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressGestures:)];
cell.longPressGestureRecognizer.minimumPressDuration = 1.0f;
cell.longPressGestureRecognizer.allowableMovement = 300.0f;
[cell.myButton addGestureRecognizer:cell.longPressGestureRecognizer];
}
- (void)handleLongPressGestures:(UIGestureRecognizer *)recognizer
{
if ([recognizer.view isKindOfClass:[UIButton class]]){
if (recognizer.state == UIGestureRecognizerStateBegan){
NSLog(#"Long press began");
} else if (recognizer.state = UIGestureRecognizerStateEnded){
NSLog(#"Long press ended");
}
}
}
The problem is handleLongPressGestures: method is never called.

the longPressGestureRecognizer should be a property on the controller and not the view(MyCustomCell). Move the property over to MyViewController and try again. My guess is something weird is happening when it queues and dequeues the MyCustomCell.
Objects(cells) for reuse should be lightweight. In this case, the longPressGestureRecognizer's target is the view controller and is nasty.

Try out this way
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"MyCell";
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
if (!cell){
cell = [[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
if ([[cell gestureRecognizers] count]<1) {
UILongPressGestureRecognizer *longPressGestureRecognizer;
longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressGestures:)];
longPressGestureRecognizer.minimumPressDuration = 1.0f;
longPressGestureRecognizer.allowableMovement = 300.0f;
longPressGestureRecognizer.delegate = self;
[cell.myButton addGestureRecognizer:cell.longPressGestureRecognizer];
}
}
This type of code works for me.

Try This!
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UILongPressGestureRecognizer *LongPress=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPressGestures:)];
LongPress.minimumPressDuration = 1.0f;
LongPress.allowableMovement = 300.0f;
[cell.myButton addGestureRecognizer:LongPress];
}
- (void)handleLongPressGestures:(UIGestureRecognizer *)recognizer
{
if ([recognizer.view isKindOfClass:[UIButton class]]){
if (recognizer.state == UIGestureRecognizerStateBegan){
NSLog(#"Long press began");
}
else if (recognizer.state = UIGestureRecognizerStateEnded)
{
NSLog(#"Long press ended");
}
}
}

Related

Pass cell.section to CustomTableCell

I have a tableview called AdminOrderViewController and it has customcell called StepperProgressCell.
This customcell has a custom UIView called AYStepperView. There is a button in this UIView and I implemented a delegate on it, whenever it gets clicked and I clicked method is getting called on AdminOrderViewController.
However, I could not able to figure out how to pass clicked header cell.section to AYStepperView ??
AdminOrderViewController.m
#interface AdminOrderViewController : UIViewController <AYStepperViewDelegate>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"StepperProgressCell";
StepperProgressTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[StepperProgressTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.stepperView.delegate= self;
return cell;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
AdminHeaderFooterView *sectionHeaderView = [self.adminTableView dequeueReusableHeaderFooterViewWithIdentifier:SectionHeaderViewIdentifier];
if (sectionHeaderView == nil)
{
sectionHeaderView = [[AdminHeaderFooterView alloc] initWithReuseIdentifier:SectionHeaderViewIdentifier];
}
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(selectHeaderAction:)];
[sectionHeaderView addGestureRecognizer:tapGesture];
return sectionHeaderView;
}
-(void) selectHeaderAction :(UITapGestureRecognizer*) gestureRecognizer
{
AdminHeaderFooterView* cell = (AdminHeaderFooterView*)gestureRecognizer.view;
[self toggleSection:cell withSection: cell.section];
// how to pass clicked section to AYStepperView??
}
-(void)clicked :(NSUInteger) currentSection
{
NSLog(#"Stepper clicked %lu", currentSection);
}
StepperProgressTableViewCell.m
#implementation StepperProgressTableViewCell
#synthesize stepperView;
- (void)awakeFromNib {
[super awakeFromNib];
[self setUpViews];
}
- (void)setUpViews {
self.stepperView = [[AYStepperView alloc]initWithFrame:CGRectMake(0, 0 , [[UIScreen mainScreen] bounds].size.width, kFormStepperViewHeight) titles:#[#"Processing",#"Ready",#"Delivered", nil)]];
[self addSubview:self.stepperView];
}
AYStepperView.h
#property (nonatomic) NSUInteger currentSection;
AYStepperView.m
#protocol AYStepperViewDelegate <NSObject>
#required
- (void)clicked :(NSUInteger) currentSection;
#end
- (void)buttonPressed:(UIButton *)sender {
[stepperDelegate clicked : currentSection];
}
The cell should not need to know which row or section it is in; Your table view controller can find this easily, given a reference to the cell.
Your view controller should not set itself as the delegate of the stepper view. It should be a delegate of the cell. The cell should be the delegate of the stepper view. This is a bit more complicated but it maintains better separation of concerns and makes the whole thing cleaner.
AYStepperView.h
#protocol AYStepperViewDelegate <NSObject>
#required
- (void)clicked;
#end
AYStepperView.m
- (void)buttonPressed:(UIButton *)sender {
[stepperDelegate clicked];
}
StepperProgressTableViewCell.h
#protocol StepperProgressTableViewCellDelegate <NSObject>
#required
- stepperChanged: (StepperProgressTableViewCell) cell;
StepperProgressTableViewCell.m
-(void)awakeFromNib {
self.stepperView.delegate= self;
}
- (void)clicked {
[self.delegate stepperChanged: self];
}
AdminOrderViewController.m
#interface AdminOrderViewController : UIViewController <StepperProgressTableViewCellDelegate>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"StepperProgressCell";
StepperProgressTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[StepperProgressTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.delegate= self;
return cell;
}
-(void)stepperChanged:(StepperProgressTableViewCell)cell {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// Now do something with indexPath.section
}
in cellForRowAtIndexPath set section to stepper view
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"StepperProgressCell";
StepperProgressTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[StepperProgressTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.stepperView.delegate= self;
cell.stepperView.currentSection = indexPath.section;//set section value
return cell;
}

Button in custom cell in dynamic table: how to know which row in action method? [duplicate]

This question already has answers here:
Select UITableView's row when clicking on UISwitch
(7 answers)
Closed 8 years ago.
I have a table of dynamic content. In the prototype cell I have a button. In that button's action method, how do I then determine which row's button was pressed?
(I know that, for example, in a segue method one can determine which row has been pressed by querying the selected path of the table view, but in this case, no row has actually been selected.)
There are of course ways of doing this using tags, or storing an indexPath in your cell etc.
I prefer to use something similar to the following, which I think is cleaner than the above. You could add this to a category UITableViewController.
- (NSIndexPath *)indexPathForCellSubview:(UIView *)subview
{
if (subview) {
UITableViewCell *cell = [self tableViewCellForCellSubview:subview];
return [self.tableView indexPathForCell:cell];
}
return nil;
}
- (UITableViewCell *)tableViewCellForCellSubview:(UIView *)subview
{
if (subview) {
UIView *superView = subview.superview;
while (true) {
if (superView) {
if ([superView isKindOfClass:[UITableViewCell class]]) {
return (UITableViewCell *)superView;
}
superView = [superView superview];
} else {
return nil;
}
}
} else {
return nil;
}
}
Edit with suggestion from #staticVoidMan:
More concise implementation:
- (UITableViewCell *)tableViewCellForCellSubview:(UIView *)subview
{
UIView *checkSuperview = subview.superview;
while (checkSuperview) {
if ([checkSuperview isKindOfClass:[UITableViewCell class]]) {
break;
} else {
checkSuperview = checkSuperview.superview;
}
}
return (UITableViewCell *)checkSuperview;
}
Best approach:
-(void)buttonMethod:(UIButton *)sender event:(UIEvent *)event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint pointCurrent = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForItemAtPoint:pointCurrent];
//...
}
PS: Provided you have done something like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
[cell.someButton addTarget:self
action:#selector(buttonMethod:event:)
forControlEvents:UIControlEventTouchUpInside];
//...
}
You could use tags, associated objects, or superview trickery.
Here's a question that has all three answers.
Tags
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MySpecialCell";
MySpecialCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[MySpecialCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.mySpecialTextView.delegate = self;
}
cell.mySpecialTextView.tag = indexPath.row;
return cell;
}
- (void)textViewDidChange:(UITextView *)textView
int rowOfTextViewThatJustChanged = textView.tag;
}
Associated Objects
static NSString *const kIndexPathKey = #"indexPathKey";
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MultiSelectCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UISwitch *switchView = [[UISwitch alloc] init];
[switchView addTarget:self action:#selector(switchValueChanged:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView = switchView;
}
UISwitch *switchView = (id)cell.accessoryView;
[self setIndexPath:indexPath onSwitch:switchView];
switchView.on = [self isIndexPathSelected:indexPath];
id item = [self itemAtIndexPath:indexPath];
cell.textLabel.text = safePerformSelector(item, self.itemDescriptionSelector);
return cell;
}
- (void)switchValueChanged:(UISwitch *)sender {
NSIndexPath *indexPath = [self indexPathOfSwitch:sender];
[self setRowSelected:sender.isOn atIndexPath:indexPath];
[self.delegate didSelectItem:[self itemAtIndexPath:indexPath] atIndexPath:indexPath selected:sender.isOn sender:self];
}
- (void)setIndexPath:(NSIndexPath *)indexPath onSwitch:(UISwitch *)switchView {
[switchView setAssociatedObject:indexPath forKey:kIndexPathKey];
}
- (NSIndexPath *)indexPathOfSwitch:(UISwitch *)switchView {
return [switchView associatedObjectForKey:kIndexPathKey];
}
#implementation NSObject (AssociatedObjects)
- (void)setAssociatedObject:(id)object forKey:(NSString *const)key {
objc_setAssociatedObject(self, (__bridge const void *)(key), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObjectForKey:(NSString *const)key {
return objc_getAssociatedObject(self, (__bridge const void *)(key));
}
#end

cannot get the row number when the uiswitch is toggled within uitableviewcell

I am trying to get the row number when the switch is toggled inside the uitableviewcell however i am getting null for the indexpath under the enableAbsense method. Any idea where went wrong? Here are the codes. Thanks in advance.
in the implementation file:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == DetailsViewClassTable) {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier ];// forIndexPath:indexPath];
ClassRecord * ClassObject;
ClassObject = [DetailsViewClassArray objectAtIndex:indexPath.row];
//add switch button switch
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
switchAbsense = [[UISwitch alloc] init];
CGSize switchSize = [switchAbsense sizeThatFits:CGSizeZero];
switchAbsense.frame = CGRectMake(cell.contentView.bounds.size.width - switchSize.width - 355.0f,
(cell.contentView.bounds.size.height - switchSize.height) / 5.0f,
switchSize.width,
switchSize.height);
switchAbsense.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
switchAbsense.tag = 101;
[switchAbsense addTarget:self action:#selector(enableAbsense:) forControlEvents:UIControlEventValueChanged];
[cell.contentView addSubview:switchAbsense];
}else{
switchAbsense=(UISwitch *)[cell.contentView viewWithTag:101];
}
// Configure the cell...
if ([ClassObject.absense isEqual:#"Yes"]){
switchAbsense.on=YES;
}else{
switchAbsense.on=NO;
}
if ([ClassObject.makeup isEqual:#"Yes"]){
switchMakeup.on=YES;
}else{
switchMakeup.on=NO;
}
//Accessory
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
}
-(void) enableAbsense:(UISwitch *)sender
{
UITableViewCell *cell = (UITableViewCell *)sender.superview.superview;
NSIndexPath *indexpath =[DetailsViewClassTable indexPathForCell:cell];
NSLog(#"%ld",(long)indexpath.row);
}
in the header file
#property (strong, nonatomic) IBOutlet UITableView *DetailsViewClassTable;
-(void) enableAbsense:(UISwitch *)sender
{
CGPoint point = [sender.superview convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:point];
}

How to get value from UITableViewCell

I'm having a problem in getting a value in uitableviewcell.
first of all, i have a label on my cell which I've created a tap gesture on it. What i want to do is on tapping that label, viewUser method will be call and it will get the details of that cell being tapped.
Here is my code:
on cellForRowAtIndexPath:
cell.userNameLabel.text = [[_workflowList objectAtIndex:indexPath.row] userName];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewUser:)];
tapGestureRecognizer.numberOfTapsRequired = 1;
[cell.userNameLabel addGestureRecognizer:tapGestureRecognizer];
cell.userNameLabel.userInteractionEnabled = YES;
Now, when i call my ontap method which is viewUser:
- (IBAction)viewUser:(id)sender {
//this should not be hard coded
//data should come from cell.
//usernamelabel or i will get the index selected
//and get the details in my array
// like this --> [_workflowList objectAtIndex:index]
WorkflowProfileViewController *chkDtl = [[WorkflowProfileViewController alloc]init];
chkDtl.name = #"USER, USER USER"; ;
chkDtl.phoneNo = #"09173210836";
chkDtl.email = #"romelync#sxchange.com";
[self.navigationController pushViewController:chkDtl animated:YES];
}
Please help me on this.
The best option would be to use UITableView delegate method tableView:didSelectRowAtIndexpath: as below:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
WorkflowProfileViewController *chkDtl = [[WorkflowProfileViewController alloc]init];
chkDtl.name=[[_workflowList objectAtIndex:indexPath.row] userName];
[self.navigationController pushViewController:chkDtl animated:YES];
}
Don't forget to set delegate of your tableView.
UPDATE: Then I think you want to do something like below. In your cellForRowAtaIndexPath: add following:
cell.userNameLabel.tag=indexPath.row;
And in viewUser method:
UITapGestureRecognizer *tap=(UITapGestureRecognizer*)sender;
WorkflowProfileViewController *chkDtl = [[WorkflowProfileViewController alloc]init];
chkDtl.name=[[_workflowList objectAtIndex:tap.view.tag] userName];
[self.navigationController pushViewController:chkDtl animated:YES];
When creating UILable you could put the tag value as row number
lable.tag = indexPath.row
In your method then retrieve label and look for tag value.
Something like below will get the cell you are tapping (replace swipe with tap):
- (void)handleSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer{
CGPoint location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *swipedIndexPath = [self.tableView indexPathForRowAtPoint:location];
self.currentSwipedCell = [self.tableView cellForRowAtIndexPath:swipedIndexPath];
}
Havent tested it yet but:
-(void)viewUser:(UITapGestureRecognizer):gestureRecognizer{
CGPoint location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
self.currentTappedCell = [self.tableView cellForRowAtIndexPath:indexPath];
NSLog(self.currentTappedCell.name); // If you have a custom cell with a name property
}
Definitely you should use the proper delegate methods instead implementing UITapGestureRecognizer on an UITableViewCell. Then you can get the informations you want pretty easily:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *theUserNameYouWantToGet = [_workflowList objectAtIndex:[indexPath row]];
}
EDIT:
If you want that action not to work for the whole cell, you could subclass UITableViewCell, add properties to it and implement your own delegate protocol to that cell:
#protocol JWTableViewCellDelegate;
#interface JWTableViewCell : UITableViewCell
#property (nonatomic, retain) NSString *username;
#property (nonatomic, assign) id<JWTableViewCellDelegate>delegate;
#end
#protocol JWTableViewCell <NSObject>
- (void)tableViewCellDidClickUsername:(JWTableViewCell *)cell;
#end
#implementation JWTableViewCell
- (void)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
[self setupView];
}
return self;
}
// if you use a prototyping NIB
- (void)awakeFromNib {
[self setupView];
}
- (void)setupView {
// add gesture recognizer
}
- (void)handleGesture {
if ([_delegate respondsToSelector:#selector(tableViewCellDidClickUsername:)]) {
[_delegate tableViewCellDidClickUsername:self];
}
}
Then use this in your viewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
JWTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"reuseID" forIndexPath:indexpath];
[cell setUsername:#"Paul"];
[cell setDelegate:self];
}
- (void)tableViewCellDidClickUsername:(JWTableViewCell *)cell {
NSString *theUserNameYouWantToGet = [cell username];
  }

Invoking Gesture Recognizer for UIImageView inside UITableView

I have a UITableView with custom cell TaskCell. TaskCell has checkboxImageView and I want that when user clicks on the checkboxImageView a method is fired. For some reason it is always firing the didSelectTableView delegate method. Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TaskCell";
Task *task = [tasks objectAtIndex:[indexPath row]];
TaskCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[TaskCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[cell bindTo:task];
return cell;
}
TaskCell.m:
-(void) prepareGestureRecognizers
{
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onSingleTap:)];
singleTapGestureRecognizer.delegate = self;
singleTapGestureRecognizer.numberOfTapsRequired = 1;
self.checkboxImageView.userInteractionEnabled = YES;
[self.checkboxImageView addGestureRecognizer:singleTapGestureRecognizer];
}
-(void) onSingleTap:(UITapGestureRecognizer *) sender
{
NSLog(#"single tap!");
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(void) bindTo:(Task *)task
{
[self prepareGestureRecognizers];
self.titleLabel.text = task.title;
}
If you add the gesture recognizer in your TaskCell.m, then the following line
singleTapGestureRecognizer.delegate = self;
sets the delegate of the recognizer to the cell, not your controller. You should add the recognizer in your controller file so that the delegate is properly set up.
I'm not sure if this will work as I haven't tried myself, but you can try set the recognizer up in your Storyboard and let it be a #property of your cell. Then you can set the delegate to be your controller in your controller file.

Resources