UITableView 选定的单元格在滚动时不会保持选中状态

Posted

技术标签:

【中文标题】UITableView 选定的单元格在滚动时不会保持选中状态【英文标题】:UITableView selected cell doesn't stay selected when scrolled 【发布时间】:2012-11-07 17:56:53 【问题描述】:

我在滚动表格时遇到表格视图单元格未保持其“选定”状态的问题。以下是相关代码:

@property (nonatomic, strong) NSIndexPath *selectedIndexPath;

-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
    self.selectedIndexPath = indexPath;
    //do other stuff


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

    MyCustomCell_iPhone* cell = [tableView dequeueReusableCellWithIdentifier:@"MyCustomCell_iPhone"];

    if (cell == nil)
        cell = [[[NSBundle mainBundle] loadNibNamed:@"MyCustomCell_iPhone" owner:self options:nil] objectAtIndex:0];

    if ([indexPath compare: self.selectedIndexPath] == NSOrderedSame) 
        [cell setSelected:YES animated:NO];
    

    return cell;

对于单元格:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated

    [super setSelected:selected animated:animated];

    if (selected) 
        self.selectedBg.hidden = NO;
    else
        self.selectedBg.hidden = YES;
    


- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated

    [super setHighlighted:highlighted animated:animated];

    if (highlighted) 
        self.selectedBg.hidden = NO;
    else
        self.selectedBg.hidden = YES;
    

如何让选定的单元格保持突出显示?如果我将它从屏幕上滚动出来,当它在屏幕上向后滚动时,它会显示为未选中状态(隐藏了它的 selectedBg)。

编辑: 从单元格中删除 setHighlighted 方法可以解决此问题。然而,这意味着按下表格单元格时我没有突出显示状态。我想知道这个问题的解决方案。

【问题讨论】:

【参考方案1】:

遇到同样的问题,选定单元格的附件视图在滚动时消失了。我的同事发现了这个问题的好方法。原因是在 ios 7 中的 touchesBegan 事件 UITableView 取消选择选定的单元格并选择按下的单元格。在 iOS 6 中它不会发生,并且在滚动选定的单元格时保持选中状态。要在 iOS 7 中获得相同的行为,请尝试:

1) 在您的 tableView 中启用多项选择。

2) 转到 tableView 委托方法 didSelectRowAtIndexPath,并取消选择用代码触及的单元格:

   NSArray *selectedRows = [tableView indexPathsForSelectedRows];
for(NSIndexPath *i in selectedRows)

    if(![i isEqual:indexPath])
    
        [tableView deselectRowAtIndexPath:i animated:NO];
    

解决了我的问题!希望对您有所帮助,顺便说一句我的英语不好。

【讨论】:

太棒了!这个解决方案对我来说很好。但是当再次选择所选行时,我发现了一个问题。然后它被取消选择。我添加了这个 UITableView 委托方法来解决这个问题: - (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; if ([cell isSelected]) return nil; 返回索引路径; 【参考方案2】:

我知道我的方法不是很正统,但似乎有效。这是我的解决方案:

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
    if cell.selected 
        cell.selected = true
     else 
        cell.selected = false
    

您还必须实现您在帖子中提到的所有方法 (@soleil)

【讨论】:

这是唯一对我有帮助的解决方案。我已经在我的 UITableViewController 子类中的 Obj-C 中实现了它,如果你使用 [self setClearsSelectionOnViewWillAppear:NO]; 你可以做cell.selected = cell.selected,虽然它不那么可读【参考方案3】:

我正在使用 Xcode 9.0.1 和 Swift 4.0。当单元格离开屏幕并返回时,我发现以下代码解决了我的选择标记:

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    if cell.isSelected 
        cell.accessoryType = .checkmark
     else 
        cell.accessoryType = .none
    

【讨论】:

【参考方案4】:

iOS 7/8 都在滚动开始时取消选择单元格(正如 Alexander Larionov 指出的那样)。

对我来说更简单的解决方案是在我的 ViewController 中实现这个 UIScrollViewDelegate 方法:

 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 
     NSInteger    theRow       = [self currentRowIndex]; // my own method
     NSIndexPath *theIndexPath = [NSIndexPath indexPathForRow:theRow inSection:0];
     [self.myTableView selectRowAtIndexPath:theIndexPath 
                                   animated:NO 
                             scrollPosition:UITableViewScrollPositionNone];
 

这是因为我的 viewController 是 UITableView 的委托,而 UITableView 继承自 UIScrollView。

【讨论】:

这是您的替代解决方案 NSIndexPath *indexPath = [_tableView indexPathForSelectedRow]; [_tableView selectRowAtIndexPath:indexPath Animation:NO scrollPosition:UITableViewScrollPositionNone];【参考方案5】:

如果你想在 Swift 中实现同样的目标,那么这里是代码。顺便说一句,我将 Xcode 7.2 与 Swift 2.1 一起使用。

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
    if cell.selected == true
        cell.selected = true
        cell.backgroundColor = UIColor.blackColor()
    else
        cell.backgroundColor = tableViewCellColor //Don't panic its my own custom color created for the table cells.
        cell.selected = false
    

随心所欲地进行其他自定义..

谢谢..

希望这会有所帮助。

【讨论】:

【参考方案6】:

Swift 3 解决方案,2017 年。

我用这行简单的代码解决了这个问题:

cell.isSelected = tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false

tableView(tableView:cellForRowAt indexPath:)方法内部:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

    // Dequeue a reusable cell
    if let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID") 

        cell.isSelected = tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false

        // Now you can safely use cell.isSelected to configure the cell 

        // ...your configurations here

        return cell
    

    return UITableViewCell()

【讨论】:

【参考方案7】:

斯威夫特 5

将以下代码放入您的自定义UITableViewCell 子类中:

override func setHighlighted(_ highlighted: Bool, animated: Bool) 
    guard !isSelected else  return 

    super.setHighlighted(highlighted, animated: animated)
    if highlighted 
        // Style cell for highlighted
     else 
        // Style cell for unhighlighted
    


override func setSelected(_ selected: Bool, animated: Bool) 
    super.setSelected(selected, animated: animated)
    if selected 
        // Style cell for selected
     else 
        // Style cell for unselected
    

解释:尝试在setHighlightedsetSelected 上设置断点。您会发现dequeueReusableCell 方法调用setSelected 然后setHighlighted 按此顺序 来重置新单元格。因此,您的突出显示代码正在破坏您在选择代码中所做的样式。非黑客修复是避免在调用 setHighlighted(false, animated: false) 时破坏您选择的样式。

【讨论】:

【参考方案8】:

您是否尝试过比较索引路径的行而不是整个索引路径对象?

if ((indexPath.row == self.selectedIndexPath.row) && (indexPath.section == self.selectedIndexPath.section)) 
    [cell setSelected:YES animated:NO];

【讨论】:

我更新了代码以包括对部分和行的检查。【参考方案9】:

这是我想出的解决方案——它甚至不会让人觉得老套。

1) 实现-scrollViewWillBeginDragging:-scrollViewWillEndDragging:withVelocity:targetContentOffset: 并在滚动期间手动突出显示所选行(如果有)的单元格。

我的看起来像这样:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollview 
    self.scrollViewIsDragging = YES;

    if( [self.tableView indexPathForSelectedRow] ) 
        [[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]] setHighlighted:YES];
    


- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset 
    self.scrollViewIsDragging = NO;

    if( [self.tableView indexPathForSelectedRow] ) 
        [[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]] setHighlighted:NO];
    

scrollViewIsDragging 属性在那里,以便在-tableView:cellForRowAtIndexPath: 中我们可以确保任何新出列的单元格都具有适当的突出显示(例如,如果所选行的单元格在离开屏幕后滚动到屏幕上)。该方法的相关部分如下所示:

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

    // ... cell creation/configuration ...

    if( self.scrollViewIsDragging && [[tableView indexPathForSelectedRow] isEqual:indexPath]) 
        [cell setHighlighted:YES animated:NO];
    

...你有它。 selectedRow 的单元格将在滚动期间保持突出显示。

【讨论】:

一个警告,我认为这是正确的行为(至少在我的情况下),是在用户开始滚动之前犹豫触摸另一个单元格可能会突出显示该单元格。如果有人有聪明的方法来处理这个问题,我很想听听。【参考方案10】:

UITableViewCell 有一个 BOOL 属性“selected”。每当您加载单元格时,请检查 selected 的状态并在 cellForRowAtIndexPath 定义中相应地进行选择或取消选择:

if (cell.selected) 
    // Maintain selected state

else
     // Maintain deselected state

【讨论】:

【参考方案11】:

在这里发布了一个快速的答案: https://***.com/a/35605984/3754003

在其中,我还解释了为什么会发生这种情况。

【讨论】:

【参考方案12】:

不要使用内置系统属性isSelected

您可以创建自己的属性,例如:

var isSelectedStyle = false
cell.isSelectedStyle = ....

【讨论】:

以上是关于UITableView 选定的单元格在滚动时不会保持选中状态的主要内容,如果未能解决你的问题,请参考以下文章

uitableview 自定义单元格在向下滚动 uitableview 时丢失其内容

用于 UITableView 的自定义 XIB 单元格在滚动时卡住/卡住

UITableView 单元格在向下滚动然后备份后消失

UIScrollView 选定的单元格在旋转到横向时超出当前视图

UITableview刷新单元格在部分标题下滚动

UITableView状态持久性中的复选框