Unable to select rows of UITableView in UITableViewController - ios

I have created an app using a storyboard. In other screens, I have used a UITableViewController directly and the selection is working as expected.
In this case, I have a UITableView that is one of several controls within a UIViewController.
My custom ViewController.h file has a definition similar to the below:
#interface MyViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>
#property (weak,nonatomic) IBOutlet UITableView *myTableView;
#end
Then within viewDidLoad I am doing this:
_myTableView.delegate = self;
_myTableView.dataSource = self;
Having done this, my numberOfSectionsInTableView, numberOfRowsInSection and cellForRowAtIndexPath methods are all being called and my table looks as I want it to.
The problem I have is that the rows do not select and the didSelectRowAtIndexPath method is not getting called.
I have checked that Selection is set to Single Selection in the storyboard view and I have also tried to set _myTableView.allowsSelection=YES; in viewDidLoad but this doesn't seem to make any difference.
I know that this is probably something to do with the fact that my table is within a normal view controller, but I can't figure out the magic step I've missed to make the selection work.
For now I have added a workaround. In cellForRowAtIndexPath I have attached a UITapGestureRecognizer to each view in the cell:
for(UIView *view in cell.contentView.subviews){
[view setUserInteractionEnabled:YES];
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapRow:)];
[tap setNumberOfTapsRequired:1];
[view addGestureRecognizer:tap];
view.tag = row; // So that I can identify in the handler which row has been tapped
}
Then in the handler:
-(void)tapRow:(id)sender{
UITapGestureRecognizer *gesture = (UITapGestureRecognizer*)sender;
UIView *myView = (UIView*)gesture.view;
int row = myView.tag;
// Handle tap of row here
}
This achieves what I need, but I would still like to figure out what I've done wrong with the row selection!

Make sure there is not another view on top of the table view (e.g. a transparent view that would be stealing touch events). Also ensure that userInteraction is enabled for the tableview and all of its parent views.

We type sometime wrong delegate call didDeselectRowAtIndexPath instead of didSeselectRowAtIndexPath. Is it your case?
I see that you can update your content in the tableview so the connection should be OK. I think about mistakes with function names.
Just to test: Replace your didSelectRowAtIndexPath function with this (copy, paste).
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Selected %d",indexPath.row);
}

Just incase anyone still has this problem. I just realised -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method won't work for any view or subview with a tap gesture recogniser set to it. So remove the tap gesture recogniser from that view of it's parent view. if you still want to handle touch events, then use "touchesBegan" function. ` - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
}`

I had the exactly the same problem with my current project for two minutes yesterday when I realised that I didn't link my delegate and dataSource of the table view. You made it programmatically, so it should work. For me was a delegation problem, you can check your Attributes Inspector to make sure that everything is ok.

In cellForRowAtIndexPath method add this line
cell.userInteractionEnabled = NO;

Related

iOS Disable Double Tap gesture recognizer in Swift

I am working on a app using TableView now i am facing an issue listed below.
Inside my TableView there is UITextView on it, that MUST be selectable, but not editable (because I need to use and proceed links).
My issue is:
when I tap on a link as everybody does, it doesn't work. I need to hold it a bit longer to make it work. I thought that it is because of "Selectable" property brings in a Double Tap Gesture recognizer, so my textView checks if there is a second tap, but I don't know how to find and remove only double tap recognizer.
What should I do?
Thank you.
Have you considered replacing the TextView with a UIWebView, and just do a loadHTMLString function?
This way when you tap on a link, it will open instantly? You can even have a UIWebView delegate and do what you want when the link is pressed(Custom UIWebView instead of auto opening in safari etc)
You've to handle tap event.. Through this code
tapGesture.numberOfTapsRequired = 1
OR
To do this, you will need to embed one in your UITableViewCell. But there's no need to create a custom cell. Here is the basic idea of what you will want to do:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UITextView *comment = [[UITextView alloc] initWithFrame:CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, tableView.rowHeight)];
comment.editable = NO;
comment.delegate = self;
[cell.contentView addSubview:comment];
[comment release];
}
return cell;
}
You will, of course, need to set your rowHeight if you don't want the standard 44pt height that comes with the cell. And if you want actual cells, you'll need to add your own logic so that only the cell you want is a textView, but this is the basic idea. The rest is yours to customize to your fitting. Hope this helps
EDIT: to bypass the textView to get to your cell, there are two ways to go about this.
1) you can make a custom textView class and overwrite touchesBegan to send the message to super:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
}
this will send the touch events to its superview, which would be your tableView. Considering you didn't want to make custom UITableViewCells, I imagine you probably don't want to make a custom textView class either. Which leads me to option two.
2) when creating the textView, remove comment.editable = NO;. We need to keep it editable, but will fix that in a delegate method.
In your code, you will want to insert a textView delegate method and we'll do all our work from there:
EDIT: changing this code to use with a UITableViewController
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
// this method is called every time you touch in the textView, provided it's editable;
NSIndexPath *indexPath = [self.tableView indexPathForCell:textView.superview.superview];
// i know that looks a bit obscure, but calling superview the first time finds the contentView of your cell;
// calling it the second time returns the cell it's held in, which we can retrieve an index path from;
// this is the edited part;
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
// this programmatically selects the cell you've called behind the textView;
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
// this selects the cell under the textView;
return NO; // specifies you don't want to edit the textView;
}
If that's not what you wanted, just let me know and we'll get you sorted out
Finding and Removing Double Tap Gesture recognizer
Objective C
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]])
{
[(UITapGestureRecognizer *)gestureRecognizer setNumberOfTapsRequired:1];
gestureRecognizer.enabled = NO;
}
}
Swift
func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer)
{
if gestureRecognizer.isKindOfClass(UITapGestureRecognizer)
{
(gestureRecognizer as! UITapGestureRecognizer).numberOfTapsRequired = 1
gestureRecognizer.enabled = false
}
}

UITableView didSelectRowAtIndexPath not being called

I have a view controller with a UITableView. I have both the datasource and delegate set to the view controller. The method cellForRowAtIndexPath: is being called and behaving as expected however didSelectRowAtIndexPath: is not being called. Any ideas whats going on here?
I have this in my .h:
#interface DetailViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
And in my viewDidLoad in the .m I have:
self.tableView.dataSource=self;
self.tableView.delegate=self;
It doesn't seem to make sense that the cellForRowAtIndexPath would be called but not didSelectRowAtIndexPath.This is what the method looks like for now:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"did select row at index path");
}
I have encountered some possible reasons:
Make sure the the editing property of tableView is set to NO. The didSelectRowAtIndex: method isn't called when the editing property of table is set to YES. By default this property is set to NO.
self.tableView.editing = NO;
Make sure the allowsSelection property is set to YES. By default it is.
Try to call.[super viewDidLoad] in the viewDidLoad method or setting the delegate and the dataSource properties in the init initializer.
Set the delegate and the dataSource properties in the loadView method and assign the tableView to the view property of the UIViewController. Here's an example:
- (void)loadView
{
UITableView *tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
tableView.delegate = self;
tableView.dataSource = self;
[tableView reloadData];
self.view = tableView;
}
(this is, actually, what I usually do) Try to subclass DetailViewController from UITableViewController which inherits from UIViewController too. UITableViewController comforms automatically to UITableViewDelegate and UITableViewDataSource.
Gesture recognizer on a view controller's view prevented touches, solved by setting gesture recognizer's cancelsTouchesInView to false.
In my case, I used UITapGestureRecognizer and it worked like a magic for me.
cell.tag=indexPath.row;
UITapGestureRecognizer* tap=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapOnCell:)];
[cell addGestureRecognizer:tap];
Here is my tapOnCell method implementation
-(void)tapOnCell:(UITapGestureRecognizer*)tap
{
ADPointCashTransaction* aTransaction=[transactionArray objectAtIndex:tap.view.tag];
// Work with aTransaction Object
}
Putting the answer in the comments of seto nugroho with my work around the problem. If you have a gesture recognizer in the tableView, then the didSelect row will not be called. What you can do is increase the number of taps for the gesture recognizer to two.
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
gestureRecognizer.numberOfTapsRequired = 2
view.addGestureRecognizer(gestureRecognizer)`
I'll add one more: make sure you don't have any other views overlapping the table or cells at any point, even hidden ones. If you do, disable 'User interaction enabled' in storyboard unless you explicitly need to catch gestures through the view.
In my case, I was assigning the dataSource and calling reloadData() all in a background thread. So, make sure you are doing this on the main thread.

Button not working in UIView for UITableView

I've created a header for the first section in my UITableView. The Header is created by a view in .nib file. I've connected the button to the Views owner which is a class called HeaderSection which is a subclass of UITableViewHeaderFooterView.
Here's the code for what should happen when the button is clicked.
#import "HeaderSection.h"
#implementation HeaderSection
- (IBAction)touchButton1:(UIButton *)sender {
NSLog(#"Touch button 1 tapped!");
}
#end
And here's a screen shot of things overall.
The problem is when I tap on the button, it doesn't detect the tap, instead the cell below the button detects the tap and the code runs accordingly (brings on a new UIViewController).
How can I make it detect the top button?
Here's more code from my main ViewController.
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *sectionHeaderNib = [UINib nibWithNibName:#"headerNib" bundle:nil];
NSString *SectionHeaderViewIdentifier = #"sectionHeaderIndentifier";
[self.tableView registerNib:sectionHeaderNib forHeaderFooterViewReuseIdentifier:SectionHeaderViewIdentifier];
self.iconSets = [IconSet iconSets];
self.tableView.allowsSelectionDuringEditing = YES;
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPressGestureRecognized:)];
[self.tableView addGestureRecognizer:longPress];
self.filteredIcons = [NSMutableArray array];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 60.0f;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
NSString *SectionHeaderViewIdentifier = #"sectionHeaderIndentifier";
HeaderSection *sectionHeaderView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:SectionHeaderViewIdentifier];
return sectionHeaderView;
}
And here's a screen shot of the view in the .xib (I've made the underlying view dark grey to stand out).
One problem you have is that in the .xib file, the top-level object must be set, in the idendity inspector, to be the custom UIView subclass that it is, or nothing will work.
However, it looks like other things are going on. The gray from the tableview cell underneath shouldn't be peaking out on the right of the footer. Is this being added in the tableView:viewForHeaderInSection: method of the UITableViewDataSource? It can adjust the headerView's frame there to be the width of the tableView. Depending on what the button is going to do, it might also need a reference to the view controller.
If the search bar and buttons are going to stay on top always, they don't really need to be a header. On that .xib you have, just put a tableview underneath the button bar. Create a ViewController with that file as the .xib, then connect that tableView's dataSource and delegate to that View Controller. You may need to create a ViewController with built-in xib to do this automatically, in or out of storyboards.
Freestanding xibs are discouraged but I think they're perfect for this case, tableView/collectionView reusable views. But if its on top of the table, not in it, it should be part of the view controller that contains the table.

Prevent didSelectRowAtIndexPath being called for STTweetLabel

I'm using this custom component for hashtag detection in my app. I have added STTweetLabel's as subviews to my custom table view cells. However, clicking on any hashtag invokes the STTweetLabel handler block AND also calls the tableview's didSelectRowAtIndexPath delegate which results in a push to another controller. How do I disable this from happening? i.e. prevent the didSelectRowAtIndexPath behaviour on the STTweetLabels?
Code:
[hashtagLabel setDetectionBlock:^(STTweetHotWord hotWord, NSString *string, NSString *protocol, NSRange range) {
// call delegate to respond to hashtag selection
}];
Try to delete
[super touchesBegan:touches withEvent:event];
[super touchesMoved:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
in STTweetLabel.m
I use TTTAttributedLabel now, you can also try it.
If your problem is cell highlight then use this.
cell.selectionStyle = UITableViewCellSelectionStyleNone;
Or use this for prevent invoke didSelectRowAtIndexPath.
tableView:willSelectRowAtIndexPath:
I hope it will help you
You can either prevent the cell from being selected at all in the interface builder or in code using the UITableViewCellSelectionStyleNone selection style.
or in your -(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath method return nil for the indexPath of the cells you don't want to get selected
Actually didSelectRowAtIndexPath method always get called first and this solution may work if your label will not cover whole your cell.
If you are doing some task in didSelectRowAtIndexPath then do this steps
1> cell.selectionStyle = UITableViewCellSelectionStyleNone;
2> make custom cell put button back side of label .On button put uilabel then you can add action on that button and do some task when button tapped and as well you can detect label tap seperately
the dirty way:
put your STTweetLabel inside an UIButton without target and action

didSelectRowAtIndexPath does not trigger in ViewController

So, I've added a UITableViewController and UITableViewCell into a UIViewController and while the cellForRowAtIndexPath delegate method works, didSelectRowAtIndexPath does not. Does anyone have any ideas?
EDIT 2: The delegate for the UITableView is set to the UIViewController.
EDIT 3: I found the answer to my issue on another Stack question here. Basically I had a UITap... on my [self view] that was blocking the didSelectRow.... I have no idea why the tap blocks the delegate method and I also have no idea how to get both the tap and the table working together simultaneously.
EDIT: The part that bugs me is that I've gotten this exact setup working on an earlier app. So that means I've missed a step somewhere along the way. The thing is, I've combed over all the steps and have compared previous app vs current app and I really have no idea what I missed.
I've added logging to both delegate methods and while one outputs, the other does not.
ViewController.h
#import "...TableViewCell.h"
...
UITableViewDataSource,
UITableViewDelegate
...
ViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cellForRowAtIndexPath");
...
return Cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath");
}
...TableViewCell.h (contents not important)
...TableViewCell.m (contents not important)
I found the answer on another StackOverflow question.
I had a UITapGestureRecognizer added to [self view] which I commented out, and then the delegate method worked.
Can anyone please tell me why this worked and also how I can get the UITapGestureRecognizer working on the same screen as the UITableView?
// Hide keyboard when user taps outside of it
UITapGestureRecognizer *tapGestureRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(hideKeyboardOnTap)];
//[[self view] addGestureRecognizer:tapGestureRecognizer];
EDIT: Corrected typo of UITapeGestureRecognizer to UITapGestureRecognizer
have you set the delegate of the tableView?
myTableView.delegate = self;
EDIT: My bad, did not read that cell for row is being called.
You say that you have used a custom tableViewController. If you have overridden the didSelectRowAtIndexPath method, it might be important to call [super didSelectRowAtIndexPath:] in the tableViewController
EDIT 2: One more thing. I do not know the reason for this, but I faced the same issue some time back in a viewController. I resolved it by adding an empty implementation of didDeselectRowAtIndexPathin the same viewController. Try adding it to your table's delegate controller.
To answer to the question "how I can get the UITapGestureRecognizer working on the same screen as the UITableView";
You should "inform" your GestureRecognizer that other Recognizer can handle the same gesture:
You do that by implementing the method of UIGestureRecognizerDelegate
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
This is a small exemple...
#interface MyController : UIViewController<UIGestureRecognizerDelegate>
{
}
#end
-(void)viewDidLoad
{
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(actionOnTapGesture:)];
[gr setNumberOfTapsRequired:1];
[gr setDelegate:self];
[self.view addGestureRecognizer:gr];
}
-(BOOL) gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyGestureRecognizer:(UIGestureRecognizer *) otherGestureRecognizer
{
return YES;
}
Check in the interface builder if the property selection is set to 'No selection'.
Change it to 'Single selection' or other option according to your needs.
This might be the reason why didSelect is not getting triggered.

Resources