UITableview: How to Disable Selection for Some Rows but Not Others
IosIphoneObjective CUitableviewIos Problem Overview
I am displaying in a group tableview
contents parsed from XML. I want to disable the click event on it (I should not be able to click it at all) The table contains two groups. I want to disable selection for the first group only but not the second group. Clicking the first row of second group navigates
to my tube player view
.
How can I make just specific groups or rows selectable?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.section!=0)
if(indexPath.row==0)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:tubeUrl]];
}
Thanks.
Ios Solutions
Solution 1 - Ios
You just have to put this code into cellForRowAtIndexPath
To disable the cell's selection property: (while tapping the cell)
cell.selectionStyle = UITableViewCellSelectionStyleNone;
To enable being able to select (tap) the cell: (tapping the cell)
// Default style
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
// Gray style
cell.selectionStyle = UITableViewCellSelectionStyleGray;
Note that a cell with selectionStyle = UITableViewCellSelectionStyleNone;
will still cause the UI to call didSelectRowAtIndexPath
when touched by the user. To avoid this, do as suggested below and set.
cell.userInteractionEnabled = NO;
instead. Also note you may want to set cell.textLabel.enabled = NO;
to gray out the item.
Solution 2 - Ios
If you want to make a row (or subset of rows) non-selectable, implement the UITableViewDelegate
method -tableView:willSelectRowAtIndexPath:
(also mentioned by TechZen). If the indexPath
should be not be selectable, return nil
, otherwise return the indexPath
. To get the default selection behavior, you just return the indexPath
passed to your delegate
method, but you can also alter the row selection by returning a different indexPath
.
example:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// rows in section 0 should not be selectable
if ( indexPath.section == 0 ) return nil;
// first 3 rows in any section should not be selectable
if ( indexPath.row <= 2 ) return nil;
// By default, allow row to be selected
return indexPath;
}
Solution 3 - Ios
Starting in iOS 6, you can use
-tableView:shouldHighlightRowAtIndexPath:
If you return NO
, it disables both the selection highlighting and the storyboard triggered segues connected to that cell.
The method is called when a touch comes down on a row. Returning NO
to that message halts the selection process and does not cause the currently selected row to lose its selected look while the touch is down.
Solution 4 - Ios
None from the answers above really addresses the issue correctly. The reason is that we want to disable selection of the cell but not necessarily of subviews inside the cell.
In my case I was presenting a UISwitch in the middle of the row and I wanted to disable selection for the rest of the row (which is empty) but not for the switch! The proper way of doing that is hence in the method
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
where a statement of the form
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
disables selection for the specific cell while at the same time allows the user to manipulate the switch and hence use the appropriate selector. This is not true if somebody disables user interaction through the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
method which merely prepares the cell and does not allow interaction with the UISwitch.
Moreover, using the method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
in order to deselect the cell with a statement of the form
[tableView deselectRowAtIndexPath:indexPath animated:NO];
still shows the row being selected while the user presses on the original contentView of the cell.
Just my two cents. I am pretty sure many will find this useful.
Solution 5 - Ios
For Swift 4.0 for example:
cell.isUserInteractionEnabled = false
cell.contentView.alpha = 0.5
Solution 6 - Ios
You trap selections with these data source methods.
– tableView:willSelectRowAtIndexPath:
– tableView:didSelectRowAtIndexPath:
– tableView:willDeselectRowAtIndexPath:
– tableView:didDeselectRowAtIndexPath:
In these methods, you check if the selected row is one you want to be selectable. If it is, take an action, if not, do nothing.
Unfortunately, you can't turn off selection for just one section. It's the whole table or nothing.
You can however set the table cells selectionStyle
property to UITableViewCellSelectionStyleNone
. I believe that will make the selection invisible. Combined with the above methods that should make the cells look completely inert from the user's perspective.
Edit01:
If you have a table in which only some of the rows are selectable it is important that the cells of the selectable rows be visually distinct from the non-selectable rows. The chevron accessory button is the default way to do this.
However you do it, you don't want your users trying to select rows and thinking the app has malfed because the row doesn't do anything.
Solution 7 - Ios
You need to do something like the following to disable cell selection within the cellForRowAtIndexPath
method:
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell setUserInteractionEnabled:NO];
To show the cell grayed out, put the following within the tableView:WillDisplayCell:forRowAtIndexPath
method:
[cell setAlpha:0.5];
One method allows you to control the interactivity, the other allows you to control the UI appearance.
Solution 8 - Ios
For swift 4.0 This will do the trick. It will disable the Cell in didSelectRowAtIndexPath method but keep the subviews clickable.
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if (indexPath.row == clickableIndex ) {
return indexPath
}else{
return nil
}
}
Solution 9 - Ios
To stop just some cells being selected use:
cell.userInteractionEnabled = NO;
As well as preventing selection, this also stops tableView:didSelectRowAtIndexPath: being called for the cells that have it set.
Solution 10 - Ios
For swift:
cell.selectionStyle = .None
cell.userInteractionEnabled = false
Solution 11 - Ios
You can use the tableView:willDisplayCell
method to do all the kinds of customization to a tableViewCell.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell setUserInteractionEnabled:NO];
if (indexPath.section == 1 && indexPath.row == 0)
{
[cell setSelectionStyle:UITableViewCellSelectionStyleGray];
[cell setUserInteractionEnabled:YES];
}
}
In this above code, the user can only select the first row in the second section of the tableView. The rest all rows can't be selected. Thanks!~
Solution 12 - Ios
My solution based on data model:
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
let rowDetails = viewModel.rowDetails(forIndexPath: indexPath)
return rowDetails.enabled ? indexPath : nil
}
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
let rowDetails = viewModel.rowDetails(forIndexPath: indexPath)
return rowDetails.enabled
}
Solution 13 - Ios
Use this to make the cell look like it is disabled and non-selectable:
cell.selectionStyle = UITableViewCellSelectionStyleNone;
Important: note that this is only a styling property, and does not actually disable the cell. In order to do that, you have to check for selectionStyle
in your didSelectRowAtIndexPath:
delegate implementation:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if(cell.selectionStyle == UITableViewCellSelectionStyleNone) {
return;
}
// do your cell selection handling here
}
Solution 14 - Ios
I like Brian Chapados answer above. However, this means that you may have logic duplicated in both cellForRowAtIndexPath and then in willSelectRowAtIndexPath which can easily get out of sync. Instead of duplicating the logic, just check the selectionStyle:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.selectionStyle == UITableViewCellSelectionStyleNone)
return nil;
else
return indexPath;
}
Solution 15 - Ios
I found this handy as it works with both static and dynamic tables. I only set the disclosure indicator on those cells I want to allow selection.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType != UITableViewCellAccessoryDisclosureIndicator) {
return nil;
}
return indexPath;
}
Solution 16 - Ios
on Xcode 7 without coding you can simply do the following:
In the outline view, select Table View Cell. (The cell is nested under Table View Controller Scene > Table View Controller > Table View. You may have to disclose those objects to see the table view cell)
In the Attributes inspector, find the field labeled Selection and select None. atribute inspector With this option, the cell won’t get a visual highlight when a user taps it.
Solution 17 - Ios
I agree with Bryan's answer
if I do
cell.isUserInteractionEnabled = false
then the subviews within the cell won't be user interacted.
On the other site, setting
cell.selectionStyle = .none
will trigger the didSelect method despite not updating the selection color.
Using willSelectRowAt is the way I solved my problem. Example:
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if indexPath.section == 0{
if indexPath.row == 0{
return nil
}
}
else if indexPath.section == 1{
if indexPath.row == 0{
return nil
}
}
return indexPath
}
Solution 18 - Ios
appart from tableView.allowsMultipleSelection = true
you will need to deselect the rest of the cells of the section when selecting:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section does not support multiple selection {
// deselect the rest of cells
tableView
.indexPathsForSelectedRows?.compactMap { $0 }
.filter { $0.section == indexPath.section && $0.row != indexPath.row }
.forEach { tableView.deselectRow(at: $0, animated: true) }
}
}
Solution 19 - Ios
Swift 5:
Place the following line inside your cellForRowAt
function:
cell.selectionStyle = UITableViewCell.SelectionStyle.none
Solution 20 - Ios
SIMPLE
Just use cell.userInteractionEnabled = YES;
to the cell if it can navigate and cell.userInteractionEnabled = NO;
otherwise
Solution 21 - Ios
Implement just the method tableView:willSelectRowAtIndexPath:
in the data source for your table. If you want the row at the path to highlight, return the given indexPath. If you do not, return nil.
Example from my app:
- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MySelectableObj* obj = [self objectAtPath:indexPath];
if(obj==nil) return nil;
return indexPath;
}
The nice thing about this is that shouldPerformSegueWithIdentifier:sender:
will not be called if the above method returns nil, although I repeat the test above just for completeness.
Solution 22 - Ios
For Xcode 6.3:
cell.selectionStyle = UITableViewCellSelectionStyle.None;
Solution 23 - Ios
for swift 3.0 you can use below code to disable user interaction to UITableView cell
cell.isUserInteractionEnabled = false