Problem:Fast UITableViewCell with a UIWebView
Using UITapGestureRecognizer achieve UIWebView click event,but when click link in UIWebView, UITapGestureRecognizer and Link event will be triggered. How to solve it?
cellForRowAtIndexPath code:
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
singleTap.view.tag = indexPath.row;
singleTap.delegate = self;
singleTap.cancelsTouchesInView = NO;
[cell.wvContent addGestureRecognizer:singleTap]; // cell.wvContent is UIWebView
Click on any row will be repeated trigger multiple times,how to solve it?
Its not advised to use webview within tableview cell. Both classses are inherited from scrollview :)
Related
I have a tableView with custom cells. I have in one cell a imageView. I want add to this imageView one tapGesture for load a function then I click the imageView. So I have this code into cellForRowAtIndexPath:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected:)];
singleTap.numberOfTapsRequired = 1;
[cell.image setUserInteractionEnabled:YES];
[cell.image addGestureRecognizer:singleTap];
and
-(void)tapDetected:(id)sender{
NSLog(#"single Tap on imageview");
}
My problem is that when I scroll the table view this tap is added multiple times and changes into imageView then first click changes.
How can I change add only one time the tapGesture to all cells?
if you have a custom cell you can a function like this:
-(void) addTapGesture:(UITapGestureRecognizer*)tapGesture {
if (self.tapGesture == nil) {
[self.image setUserInteractionEnabled:YES];
[self.image addGestureRecognizer:singleTap];
self.tapGesture = tapGesture
}
}
in this way you are sure to set the tapGesture just once per cell.
Let me know if this can fix your problem
The Problem
Right now, you are adding a new TapGestureRecognizer every time cellForRowAtIndexPath is called, which is every time a cell is about to appear on the screen!
Solution 1: Keep the Old Gesture Recognizer
You can also just check if a cell already contains a gesture recognizer and not add a new one if it does. This will ensure that only one TapGestureRecognizer is applied to each cell:
// Check if the cell already contains a gesture recognizer
// if not, add one!
if (cell.gestureRecognizers.count == 0) {
// Now add the tap gesture recognizer and it will be the only one
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected:)];
singleTap.numberOfTapsRequired = 1;
[cell.image setUserInteractionEnabled:YES];
[cell.image addGestureRecognizer:singleTap];
}
Note: You should probably check and make sure the cell contains
exactly the gesture recognizer that you desire- you may want to add
more gesture recognizers to your cells and then you'll have to make
sure it contains the right one!
Solution 2: Remove Gesture Recognizers
Remove all gesture recognizers from the cell before adding the TapGestureRecognizer. This will ensure that only one TapGestureRecognizer is applied to each cell:
// Removes all gesture recognizers in the cell
for (UIGestureRecognizer *recognizer in cell.gestureRecognizers) {
[cell removeGestureRecognizer:recognizer];
}
// Now add the tap gesture recognizer and it will be the only one
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected:)];
singleTap.numberOfTapsRequired = 1;
[cell.image setUserInteractionEnabled:YES];
[cell.image addGestureRecognizer:singleTap];
just in you custom cell,do this :(i use swift as example)
class YourCell:UITableViewCell{
//add a variable to track has added a tapgesture to image view
var isAddedTapGesture = false
......
}
in your cellForRowAtIndexPath:
if(cell.isAddedTapGesture == false){
//add the tap gesture to your image
cell.cell.isAddedTapGesture = true
}
In your custom cell header, create a property like
#property (strong, nonatomic) UITapGestureRecognizer *tapGesture;
Add Getter method for the gesture.
- (UITapGestureRecognizer*)tapGesture{
if (!_tapGesture) {
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected:)];
}
return _tapGesture;
}
and inside the awakeFomNib function, Remove and re - add the gesture for the image view like the below.
[self.image removeGestureRecognizer:self.tapGesture];
[self.image addGestureRecognizer:self.tapGesture];
Hope this will be helpfull.
In a custom class MMTimerButton, I set up the button to respond to a single and a double tap
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setBackgroundColor: [UIColor clearColor]];
UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
singleTapRecognizer.numberOfTapsRequired=1;
singleTapRecognizer.delaysTouchesEnded=YES;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
doubleTapRecognizer.numberOfTapsRequired=2;
[singleTapRecognizer requireGestureRecognizerToFail: doubleTapRecognizer];
[self addGestureRecognizer:singleTapRecognizer];
[self addGestureRecognizer:doubleTapRecognizer];
}
return self;
}
I implement the two methods like this in the same file
-(void)handleTap:(UITapGestureRecognizer *)sender{
NSLog(#"single tap");
}
-(void)handleDoubleTap:(UITapGestureRecognizer *)sender{
NSLog(#"double tap");
}
In the header, I declare two methods
#import <UIKit/UIKit.h>
#interface MMTimerButton : UIButton
-(void)handleTap:(UITapGestureRecognizer *)sender;
-(void)handleDoubleTap:(UITapGestureRecognizer *)sender;
In the storyboard, I added a button to the interface and set its custom class to be MMTimerButton.
Nothing's happening when I click the button (in the simulator). One click, two click, both nothing.
I also tried doing it without a button, just adding gesture recognizers to the titleView. Nothing happens when I click on the title in the simulator
UITapGestureRecognizer* doubleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleDoubleTap:)];
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleSingleTap:)];
[singleTap requireGestureRecognizerToFail : doubleTap];
[doubleTap setDelaysTouchesBegan : YES];
[singleTap setDelaysTouchesBegan : YES];
[doubleTap setNumberOfTapsRequired : 2];
[singleTap setNumberOfTapsRequired : 1];
self.navigationItem.title =#"title";
[self.navigationItem.titleView addGestureRecognizer:doubleTap];
[self.navigationItem.titleView addGestureRecognizer:singleTap];
However, when I add this code to header view, it works fine (in the simulator)
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UILabel *headerLabel = [[UILabel alloc]init];
headerLabel.tag = section;
headerLabel.userInteractionEnabled = YES;
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.text = [NSString stringWithFormat:#"0:00",section];
headerLabel.textAlignment = UITextAlignmentCenter;
headerLabel.frame=CGRectMake(10,-5,tableView.frame.size.width,41);
UITapGestureRecognizer* doubleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleDoubleTap:)];
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleSingleTap:)];
[singleTap requireGestureRecognizerToFail : doubleTap];
[doubleTap setDelaysTouchesBegan : YES];
[singleTap setDelaysTouchesBegan : YES];
[doubleTap setNumberOfTapsRequired : 2];
[singleTap setNumberOfTapsRequired : 1];
self.navigationItem.title =#"title";
[headerLabel addGestureRecognizer:doubleTap];
[headerLabel addGestureRecognizer:singleTap];
return headerLabel;
//return nil;
}
You are showing a lot of troubled code here, so it's hard to know where to start.
In the case of your initWithFrame:, the problem is that this method is never called for a button. Moreover, you've got a conflict because the button already contains the ability to respond to a tap, internally (it's a button!).
In the second set of code, you never set the titleView to a view, so it is nil, and your code does nothing. Also, you can't set both the title and the titleView (you are setting the title).
The answer to your title question is, of course a titleView can respond to a tap. Lots of apps do this (including mine).
Also, you do not need your requireGestureRecognizerToFail - a single tap gesture recognizer and a double tap gesture recognizer already know how to mediate between themselves.
So basically my advice would be: stop over-complicating everything. Don't make big assumptions and dive in. Start with stuff that you know works and take little steps. Test everything with logging / breakpoints as you go, to see that what's happening is what you imagine.
I'm implementing long press in UITableViewCell using following code. Everything works fine except that when long press is over I have to click two times on a cell to select it.
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)];
longPress.minimumPressDuration = 0.5;
longPress.delegate = self;
[cell addGestureRecognizer:longPress];
Does anybody knows the reason behind this behavior ?
Thanks.
Today I tried to add an additional UILongPressGestureRecognizer to an UISwitch, which did not really work. My code is the following:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(doSomething:)];
[lpgr setDelaysTouchesBegan:YES];
[lpgr setDelaysTouchesEnded:YES];
[lpgr setCancelsTouchesInView:YES];
[self.switcher addGestureRecognizer:lpgr];
in the viewDidLoad method of the viewController that is the parent viewController of that switch. (the switcher is an instance variable, set through a storyboard. The IBOutlet is set properly from the UISwitch to the switcherProperty of its viewController).
On a couple of other controls (like a UIButton, UISlider, UIStepper and so on) this works, even without the touches cancelled or delayed, and perfectly triggers the target method. However, with my switch, it refuses that behavior.
I have tried removing any other gestureRecognizer on the UISwitch by iterating through all gestureRecognizers on that switch and calling [switcher removeGestureRecognizer: ... ], I´ve also set all of them to require my longPressGestureRecognizer to fail. Other types of gestureRecognizers don´t fire as well.
As far as I understand, GestureRecognizers will receive touches, before the view or control, which they belong to, receives them. Thus, setCancelsTouchesInView:YES and setDelaysTouchesInView:YES should enable the gestureRecognizer to handle every single gesture until it fails or succeeds, without the view knowing, right?
--------------EDIT------------------
The whole content of my method:
- (void)viewDidLoad
{
[super viewDidLoad];
UIGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.view addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.button addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.slider addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.segmentedControl addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.switcher addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.stepper addGestureRecognizer:lpgr];
}
As I said, for the other controls this is working, only the switch does not want to work
try with it
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of tap gestures in the segmented control.
if ((touch.view == yourSwitch)) {//change it to your condition
return NO;
}
return YES;
}
make sure you confirm UIGestureRecognizerDelegate for it to work.
As the title of this question said, I need to create a view where it needs to have action when one tap it or hold it. That means I have to add UITapGestureRecognizer and a UILongPressGestureRecognizer. I have already tried it. Somehow it redirect me to the screen where I needed to go, but it has affected it's back button. Affected by in the sense of it goes back to default text which is "Item" and it cannot perform it's assigned action.
Yes you can add these two gestures in one view. See the below code
UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(didTap:)];
singleTapRecognizer.numberOfTapsRequired = 1;
singleTapRecognizer.delegate = self;
[self.view addGestureRecognizer:singleTapRecognizer];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(didLongPress:)];
[longPressRecognizer setDelegate:self];
longPressRecognizer.allowableMovement = 1.0f;
longPressRecognizer.minimumPressDuration = 2.0;
[self.view addGestureRecognizer:longPressRecognizer];
As i mentioned in my comment you can do so,here is some piece of code to help you out.
UITapGestureRecognizer * recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
recognizer.delegate = self;
[view addGestureRecognizer:recognizer];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleLongPress:)];
longPress.minimumPressDuration = 2.0;
[view addGestureRecognizer:longPress];
and here are are some links too for your better understanding:-
https://developer.apple.com/library/ios/documentation/uikit/reference/UILongPressGestureRecognizer_Class/Reference/Reference.html
https://developer.apple.com/library/ios/documentation/uikit/reference/UITapGestureRecognizer_Class/Reference/Reference.html
Hope this will help you out.