I have strange issue - when I register TapGestureRecognizer in the cellForRowAtIndexPath method it works perfect, but when I register TapGestureRecognizer in cell's initWithStyle method tap recognition doesn't work, breakpoint doesn't hit in handler.
The following works.
I have created custom table view cell with corresponding xib file and registered it.
[self.tableView registerNib:[UINib nibWithNibName:#"MyCell"
bundle:[NSBundle mainBundle]]
forCellReuseIdentifier:#"cell"];
...
and in the cellForRowAtIndexPath
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
...
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(didTapCell:)];
[tap setNumberOfTapsRequired:1];
[cell addGestureRecognizer:tap];
The following doesn't work
#implementation MyCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleCellTap:)];
[tgr setDelegate:self];
[tgr setNumberOfTapsRequired:1];
[tgr setNumberOfTouchesRequired:1];
[self addGestureRecognizer:tgr];
//[self.contentView addGestureRecognizer:tgr]; also doesn't work
}
return self;
}
I can leave the working solution, but I want to move the gesture recognition to cell initialization and fire tap event through my delegate.
Why is tap recognition not working if I'm registering recognizer in the cell initialization?
Are you sure initWithStyle:reuseIdentifier is called? Afaik you have to use initWithCoder: if you register a nib for the cell.
In a project of mine I have this
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
pan.delegate = self;
self.gestureRecognizers = [NSArray arrayWithObject:pan];
}
return self;
}
So I am using a pan gesture recognizer and it works from within the init method.
You have registered a xib for a particular cell identifier. Now the tableview will automatically instantiate a cell for you if needed (when you call dequeReusableCell...) but the initWithStyle:reuseIdentifier method does not get called, so your gesture recognizer is never created/added.
If you do need to 'init' stuff when using registered xib(s), override the awakeFromNib in your custom cell class and put your code there. I usually put my 'init' code in a separate method and call it from both initWithStyle and awakeFromNib overrides.
Related
my code look like this
viewcontroller.m
- (void)viewDidLoad {
[super viewDidLoad];
UITableView *tableView = [[UITableView alloc] init];
tableView.delegate = self;
tableView.dataSource = self;
[tableView registerClass:[TableViewCell class] forCellReuseIdentifier:#"cell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
cell.textLabel.text = #"Hello, world";
cell.delegate = self;
return cell;
}
cell.m
- (void)awakeFromNib {
[super awakeFromNib];
[self commonInit];
// Initialization code
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress)];
[self addGestureRecognizer:longPress];
}
- (void)longPress
{
[self.delegate actionTriggered:self];
}
so screen shows a tableview with only one cell contains "hello world"
when use long press gesture nothing happened.
so I set a break point in commonInit.
find out this method does not get called.
can't figure out why. and how to fix it.
appreciating any helps.
In 'viewDidLoad' you create a local variable 'tableView'. That variable gets out-of-scope as soon as the method completes.
This can't work.
Assuming the class in your viewcontroller.m file inherits from UITableViewController you just have to make sure you configure the existing tableView property instead of creating a local variable:
-(void)viewDidLoad {
[super viewDidLoad];
// UITableView *tableView = [[UITableView alloc] init]; <-- Don't!
tableView.delegate = self;
tableView.dataSource = self;
[tableView registerClass:[TableViewCell class] forCellReuseIdentifier:#"cell"];
}
I'm creating UICollectionView like below.
What I want to do about the UICollectionView is,
Each UICollectionViewCell has UIButton ([cell.contentView addSubview:button];)
UIButton need to handle UIControlEventTouchDown and UIControlEventTouchUpInside
Begin interactiveMovement by Long tap of UICollectionView Cell
Don't want to begin interactiveMovement by Long tap of UIButton. UIButton just handle UIControlEvent assigned at Step 2.
Want to handle (didSelectItemAtIndexPath:) when the area of UICollectionViewCell other than UIButton is tapped.
My problem is that handleLongPress for UICollectionView responds and start interactiveMovement even when long tapping UIButton .
I want to start interactiveMovement only when the area of UICollectionView other than UIButton is tapped.
When UIButton is long tapped, I just want to handle UIConrolEvent assigned to UIButton and don't want UICollectionView to respond.
I'd appreciate if you would have some solution.
My code is like below, thank you.
MyCollectionView : UICollectionView
- (id)init
{
self = [super initWithFrame:CGRectZero collectionViewLayout:[[UICollectionViewFlowLayout alloc] init]];
if (self) {
self.delegate = self;
self.dataSource = self;
--- snip ---
[self addLongPressGesture];
--- snip ---
}
return self;
}
- (void)addLongPressGesture
{
UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(handleLongPress:)];
[self addGestureRecognizer:gesture];
}
- (void)handleLongPress:(UILongPressGestureRecognizer *)sender
{
CGPoint point = [sender locationInView:sender.view];
NSIndexPath *indexPath = [self indexPathForItemAtPoint:point];
switch (sender.state) {
case UIGestureRecognizerStateBegan:
[self beginInteractiveMovementForItemAtIndexPath:indexPath];
break;
case UIGestureRecognizerStateChanged:
[self updateInteractiveMovementTargetPosition:point];
break;
case UIGestureRecognizerStateEnded:
[self endInteractiveMovement];
break;
default:
[self cancelInteractiveMovement];
break;
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
// something to do
}
MyUICollectionViewCell : UICollectionViewCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initButton];
}
return self;
}
- (void)initButton
{
_button = [[UIButton alloc] init];
[_button addTarget:self action:#selector(touchUpButton:) forControlEvents:UIControlEventTouchUpInside];
[_button addTarget:self action:#selector(touchDownButton:) forControlEvents:UIControlEventTouchDown];
--- snip ---
[self addSubview:_button];
}
- (void)touchUpButton:(UIButton *)button
{
// something to do
}
- (void)touchDownButton:(UIButton *)button
{
// something to do
}
Add a transparent UIView below UIButton and add long press to it not to the whole collection view as UIButton is considered a sub-Control of the cell
OR
see the long press point x,y if it's intersects with button's frame return from the method
I've resolved my problem. It my not be a good solution but it's working as I expect.
I've added ButtonBaseView(:UIView) under UIButton and add LongPressGesture on the ButtonBaseView.
The LongPressGesture do nothing as it's action. The role is to block LongPressGesture not to pass the event to UICollectionView.
At the same time, set cancelsTouchesInView=NO to handle TouchUpInside or TouchDragExit
My code is like below,
- (void)addDummyLongTapGesture
{
UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:nil];
gesture.cancelsTouchesInView = NO;
[buttonBaseView addGestureRecognizer:gesture];
}
I have a table and custom cell from NIB. Basically what I need is to slide the custom cell from the table to the right side. It has to be smooth, and the table itself should not collapse. (The cell will get back after the action is done). I have some number of sections, and 1 row/section, so basically I need to move with the section. Thanks a lot for the answer!
My cell.m file looks like this:
#import "MIKETableViewCell.h"
static NSString *CellTableIdentifier = #"MIKETableViewCell";
#implementation MIKETableViewCell
#synthesize timeLabel = _timeLabel;
#synthesize priceLabel = _priceLabel;
#synthesize infoLabel = _infoLabel;
- (void)awakeFromNib
{
// Initialization code
}
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:CellTableIdentifier];
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
add this:
-(void)layoutSubviews
{
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tableCellClicked)];
tapGesture.numberOfTapsRequired = 1;
[self addGestureRecognizer:tapGesture];
}
I'm using UITapGestureRecognizer to end editing (because that's the workaround I found useful to end editing on input keyboards that have no other way to do so, like the Decimal Pad). The problem is that inside that viewController I have a tableView (the UITableViewDataSource and UITableViewDelegate of that tableView are set to the viewController) and the method didSelectRowAtIndexPath is not being triggered.
Code:
viewDidLoad {
...
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
[self.view addGestureRecognizer:tapRecognizer];
...
}
- (void)tap:(UIGestureRecognizer*)gr {
[self.view endEditing:YES];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Help!");
}
I know the UITapGestureRecognizer is catching the selection, because if I comment out as following:
viewDidLoad {
...
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
//[self.view addGestureRecognizer:tapRecognizer];
...
}
now the method the didSelectRowAtIndexPath finally triggers out.
I need help with some good practices on how to workaround the "endEditing" or how to forward the tap gesture to the tableView so that the didSelectRowAtIndexPath triggers out.
Thanks!
I assume that you haven't a tableView that takes all the screen, otherwise you can close the keyboard in didSelectRowAtIndexPath.
Any way, you can do your stuff and the pass the event to the next responder (that is kind of class UITableViewCell if there is, so if user tap on the tableView):
- (void)tap:(UIGestureRecognizer*)gr {
[self.view endEditing:YES];
UIResponder *responder = self;
while (responder.nextResponder != nil){
responder = responder.nextResponder;
if ([responder isKindOfClass:[UITableViewCell class]]) {
break;
}
}
[responder touchesBegan:touches withEvent:event];
}
I've set up a UITapGestureRecognizer for a UIScrollView inside a UICollectionView. I've configured it to properly detect taps and trigger a method I wrote, but if I try to set the selector to collectionView:didSelectItemAtIndexPath: the program crashes when a cell is tapped.
Any idea why this is the case?
This works:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
- (void) tapped:(UIGestureRecognizer *)gesture{
//some code
}
This does not work:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(collectionView:didSelectItemAtIndexPath:)];
- (void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
//some code
}
the code you wrote,
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(collectionView:didSelectItemAtIndexPath:)];
The selector is generally just a singleFunction with one input argument which is UITapGestureRecogniser object.
should be like this,
-(void)clicked:(UIGestureRecogniser *)ges{
}
But the selector you used it improper, because it needs two inputs which cant be supplied with gestureRecogniser.Hence the crash.
Change your above code to below one,
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(clicked:)];
-(void)clicked:(UIgestureRecogniser *)ges{
//use gesture to get get the indexPath, using CGPoint (locationInView).
NSIndexPath *indexPath = ...;
[self collectionView:self.collectionView didSelectItemAtIndexPath:indexPath];
}
The action for a gesture recognizer must conform to one of the following signatures:
- (void)handleGesture;
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;
You need to use one of these action signatures and do whatever you need to in that method
, including determining the correct indexPath for the gesture.
See the docs:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIGestureRecognizer_Class/Reference/Reference.html#//apple_ref/occ/instm/UIGestureRecognizer/initWithTarget:action:
We have to call the didSelectItemAtIndexPath from the proper reference object.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
- (void) tapped:(UIGestureRecognizer *)gesture{
NSIndexPath *indexPath = //create your custom index path here
[self.collectionViewObject didSelectItemAtIndexPath:indexPath];
}