UITableview:如何禁用某些行而不是其他行的选择


298

我在一个组中显示tableview从XML解析的内容。我想禁用它的click事件(我根本不能单击它)该表包含两个组。我只想禁用第一组的选择,而不能禁用第二组的选择。单击第二组的第一行navigates到我的管中player view

如何仅选择特定的组或行?

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if(indexPath.section!=0)
    if(indexPath.row==0)    

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:tubeUrl]];   
}

谢谢。

Answers:


499

您只需要将此代码放入 cellForRowAtIndexPath

禁用单元格的选择属性:(在点击单元格时)

cell.selectionStyle = UITableViewCellSelectionStyleNone;

为了能够选择(点击)该单元格:(点击该单元格)

// Default style
cell.selectionStyle = UITableViewCellSelectionStyleBlue;

// Gray style
cell.selectionStyle = UITableViewCellSelectionStyleGray;

请注意,带有的单元格selectionStyle = UITableViewCellSelectionStyleNone;在用户didSelectRowAtIndexPath触摸时仍会导致UI调用。为避免这种情况,请按照以下建议进行设置。

cell.userInteractionEnabled = NO;

代替。另请注意,您可能希望将项目设置cell.textLabel.enabled = NO;为灰色。


1
如果您尝试滑动删除,此方法将产生错误。
Vive 2012年

116
这是骇客!为此,请在下面使用答案-将willSelectRowAtIndexPath与nil一起使用!
Peter Stajger 2012年

4
userInteractionEnabled内容不正确,请注意,请钩住willSelectRowAtIndexPath。
强尼

23
在iOS 6.0和更高版本上,tableView:shouldHighlightRowAtIndexPath:是一种新UITableViewDelegate方法,该方法BOOL根据是否indexPath应突出显示所传递的内容来返回a 。如果要针对6.0和更高版本进行构建,则强烈建议您使用此新API。
cbowns

3
如果您在Swift中执行此操作,但遇到麻烦但最终还是在这里,则需要设置cell!.selectionStyle = .None哪个力解开单元格,从而允许您访问其属性。
马科斯(Marcos)2014年

371

如果要使一行(或行的子集)成为不可选择的,请实现该UITableViewDelegate方法-tableView:willSelectRowAtIndexPath:(TechZen也提到)。如果indexPath不能选择,则返回nil,否则返回indexPath。要获得默认的选择行为,您只需将indexPath传递给您的delegate方法返回,但是您还可以通过返回different来更改行选择indexPath

例:

- (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;
}

2
小细节:您说<= 2,不是= <2。笑脸不好!:)
乔纳斯·比斯特罗姆(JonasByström)2011年

23
我还想补充一点,这是正确的答案,但是您还应该在cellForRowAtIndexPath中将selectionStyle设置为UITableViewCellSelectionStyleNone。为什么?因为如果没有此选择样式,则无法选择该单元格,但在触摸时仍会显示“如所选”(因此,如果其他单元格有一个,它将变为所选颜色)
TooManyEduardos 2014年

4
这应该比接受的答案具有更多的投票权
user1244109 2014年

2
我认为这不是接受答案的主要原因是因为接受答案更容易。尽管这不是“最佳方法”,因为您的表仍会在单元格上调用setSelected:,但仍在视觉上起作用。这里的答案需要更多的工作,但这是正确的方法(您还必须结合使用@TooManyEduardos建议)以避免在您的单元格上调用setSelected:的表带来任何不可预见的后果。
Brian Sachetta

我喜欢这个答案,但是,如果您不想在cellForRowAtIndexPath和willSelectRowAtIndexPath中复制逻辑,只需检查willSelectRowAtIndexPath中的cell.selectionStyle == none是否返回nil(请参阅下面的答案)
Eric D'Souza

61

从iOS 6开始,您可以使用

-tableView:shouldHighlightRowAtIndexPath:

如果返回NO,它将同时禁用选择突出显示和与该单元格连接的情节提要触发的序列。

当触摸连续下降时,将调用该方法。返回NO该消息会暂停选择过程,并且不会导致当前选择的行在触摸状态下失去选择的外观。

UITableViewDelegate协议参考


3
同意 对于iOS 7及更高版本,这应该是首选方法(实际上,在iOS 8上其他方法对我不起作用。)
race_carr 2015年

这应该是最佳答案。
卡梅隆E

16

上面的答案均不能正确解决该问题。原因是我们要禁用单元格的选择,但不必禁用单元格内的子视图。

在我的情况下,我在行的中间显示了一个UISwitch,我想禁用该行其余部分(为空)的选择,但不禁用该开关!因此,正确的方法是在方法中

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

形式的陈述

[cell setSelectionStyle:UITableViewCellSelectionStyleNone];

禁用对特定单元格的选择,同时允许用户操纵开关并因此使用适当的选择器。如果有人通过

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

仅准备单元并不允许与UISwitch交互的方法。

而且,使用方法

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

为了取消选择具有以下形式的语句的单元格

[tableView deselectRowAtIndexPath:indexPath animated:NO];

当用户按下单元格的原始contentView时,仍显示所选行。

只是我的两分钱。我敢肯定,很多人会觉得这很有用。


同意。并且返回nil willSelectRowAtIndexPath也有相同的问题,子视图是不可选择的。
jeffjv

还要注意,如果您有静态表,则只需在“界面生成器”中将不希望选择显示的单元格的“选择”设置为“无”即可。
jeffjv

11

您可以使用这些数据源方法来捕获选择。

 tableView:willSelectRowAtIndexPath: 
 tableView:didSelectRowAtIndexPath: 
 tableView:willDeselectRowAtIndexPath: 
 tableView:didDeselectRowAtIndexPath:

在这些方法中,您检查选定的行是否是您想要选择的行。如果是这样,请采取措施,否则请什么也不做。

不幸的是,您不能仅关闭一个区域的选择。这是整张桌子或什么都没有。

但是,您可以将表格单元格selectionStyle属性设置为UITableViewCellSelectionStyleNone。我相信这将使选择不可见。从用户的角度来看,结合以上方法应可使电池看起来完全是惰性的。

编辑01:

如果您有一个只能选择某些行的表,则重要的是,可选行的单元格在视觉上应与非可选行区分开。人字形附件按钮是执行此操作的默认方法。

无论您这样做是什么,您都不希望您的用户尝试选择行并认为该应用程序已瘫痪,因为该行没有任何作用。


请详细说明如何使用这些方法或提供一些示例程序链接
Warrior 2010年

2
@Warrior:如果已经安装了SDK,则可以打开Xcode,转到Help -> Developer documentation,然后将这些方法名称复制到搜索字段中以查看其工作方式。开发人员文档还包含示例代码。
kennytm'2


9

您需要执行以下操作来禁用cellForRowAtIndexPath方法中的单元格选择:

[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell setUserInteractionEnabled:NO];

要显示灰色的单元格,请在tableView:WillDisplayCell:forRowAtIndexPath方法中添加以下内容:

[cell setAlpha:0.5];

一种方法可以控制交互性,另一种方法可以控制UI外观。


5

要停止仅选择某些单元格,请使用:

cell.userInteractionEnabled = NO;

除了防止选择外,这还会停止为设置了该参数的单元格调用tableView:didSelectRowAtIndexPath:。


1
一直发生奇怪的事情:当我userInteractionEnabled在一个单元格(更清楚地是一个indexPath)上设置为NO时,其他一些单元格的用户交互被禁用。我不知道为什么每次userInteractionEnabled在表视图单元格上使用时都会发生这种情况。是已知错误还是我做错了什么?
Philip007

3
@ Philip007是否正在重用“ NO”单元格?如果是这样,您可能需要确保在退出单元格时将其设置为“是”。
JosephH

1
为什么将其设置为“是”?我希望它为NO(禁止用户交互)。一个典型的示例是,在使单元出队后,我将第一行(if [indexPath row] == 0)的用户交互设置为NOin tableView:cellForRowAtIndexPath:。通常通常一开始就可以。然后,在多次调用后reloadData,突然第五行不允许交互,然后是第四行。.我不知道何时会发生这种情况。整个事情看起来很随意。
Philip007

6
@ Philip007如果您正在重用的细胞,你需要将它重置为“是”为您的电池不要想成为互动。否则,如果“ NO”单元格在您要进行交互的行中重复使用,它将仍然被禁用。即:if [indexPath row] == 0) { set to NO } else { set to YES }
JosephH

5

您可以使用该tableView:willDisplayCell方法对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];
     }
} 

在上面的代码中,用户只能在tableView的第二部分中选择第一行。其余所有行均无法选择。谢谢〜



5

我基于数据模型的解决方案:

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
}

5

对于Swift 4.0,这可以解决问题。它将在didSelectRowAtIndexPath方法中禁用单元格,但保持子视图可单击。

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
         if (indexPath.row == clickableIndex ) { 
            return indexPath
         }else{
            return nil
        }
    }

或简而言之:return indexPath.row == clickableIndex ? indexPath : nil,使您的代码更清晰;-)
标记

1

使用它可以使该单元格看起来像是已禁用和不可选择:

cell.selectionStyle = UITableViewCellSelectionStyleNone;

重要说明:请注意,这只是样式属性,实际上并不会禁用单元格。为此,您必须selectionStyledidSelectRowAtIndexPath:委托实现中进行检查:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if(cell.selectionStyle == UITableViewCellSelectionStyleNone) {
        return;
    }

    // do your cell selection handling here
}

1

我喜欢Brian Chapados的回答。但是,这意味着您可能在cellForRowAtIndexPath中然后在willSelectRowAtIndexPath中都复制了逻辑,这很容易导致不同步。无需复制逻辑,只需检查selectionStyle:

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.selectionStyle == UITableViewCellSelectionStyleNone)
        return nil;

    else
        return indexPath;
}

1

我发现这很方便,因为它可同时用于静态和动态表。我只在要允许选择的那些单元格上设置公开指示器。

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.accessoryType != UITableViewCellAccessoryDisclosureIndicator) {
        return nil;
    }
    return indexPath;
}

1

在不进行编码的Xcode 7上,您只需执行以下操作:

在大纲视图中,选择“表视图单元格”。(该单元格嵌套在“表视图控制器场景”>“表视图控制器”>“表视图”下。您可能必须公开那些对象才能查看表视图单元格)

在“属性”检查器中,找到标记为“选择”的字段,然后选择“无”。 属性检查器 使用此选项,当用户点击该单元时,该单元将不会获得视觉突出显示。


0

简单

cell.userInteractionEnabled = YES;如果它可以导航,请仅使用该单元格,cell.userInteractionEnabled = NO;否则


1
这也可以防止与单元格内的任何辅助视图进行交互,而这可能不是所需的行为。
格雷格·布朗

0

仅在tableView:willSelectRowAtIndexPath: 表的数据源中实现方法。如果要突出显示路径中的行,请返回给定的indexPath。如果不这样做,则返回nil。

我的应用程序示例:

- (NSIndexPath *)tableView:(UITableView *)tableView
    willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MySelectableObj* obj = [self objectAtPath:indexPath];
    if(obj==nil) return nil;
    return indexPath;
}

这样做的好处是shouldPerformSegueWithIdentifier:sender:,尽管上面的方法只是为了完整性而重复上述测试,但是如果上述方法返回nil,则不会调用该方法。



0

我同意布莱恩的回答

如果我做

cell.isUserInteractionEnabled = false 

那么该单元格内的子视图将不会与用户互动。

在另一个网站上,

cell.selectionStyle = .none

即使不更新选择颜色,也会触发didSelect方法。

使用willSelectRowAt是解决问题的方法。例:

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

}

0

tableView.allowsMultipleSelection = true选择时,来自您的appart 将需要取消选择该部分的其余单元格:

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) }
    }
}

-1

对于Swift 3.0,您可以使用以下代码禁用与UITableView单元的用户交互

 cell.isUserInteractionEnabled = false
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.