【问题标题】:weird tableview behavior when cell is selected选择单元格时奇怪的表格视图行为
【发布时间】:2019-12-31 13:05:44
【问题描述】:

My tableview has a weird behavior when a cell is selected but this behavior is not seen always.当一个单元格被选中时,所选单元格下方的一些单元格会移动。这两个 gif 将在显示和不显示时向您展示两种情况下的行为。 这是 tableview without weird behavior 这是tableview with the weird behavior 这是我的代码:

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell : UserTableViewCell = (tableView.dequeueReusableCell(withIdentifier: "userCell") as? UserTableViewCell)!
    if(self.firebaseUsers.count != 0){
    cell.influentBackgroudView.layer.cornerRadius=10
        let url = URL.init(string:  String().loadCorrectUrl(url:firebaseUsers[indexPath.row].image))
        cell.influentImageView.contentMode = .scaleAspectFit
        cell.influentImageView!.sd_setImage(with: url!, placeholderImage: UIImage.init(named: "placeholder"),options: [.continueInBackground,.progressiveDownload], completed: { (image, error, cacheType, imageURL) in
            if (error != nil) {
                cell.influentImageView!.image = UIImage(named: "placeholder")
            } else {
                cell.influentImageView.contentMode = .scaleAspectFill
                cell.influentImageView.image = image
            }

        })
        cell.influentImageView.layer.cornerRadius=10
        cell.influentImageView.layer.masksToBounds = true
        cell.influentNameLabel.text=" " + firebaseUsers[indexPath.row].name + " "
        cell.influentNameLabel.adjustsFontSizeToFitWidth = true
        cell.influentNameLabel.textAlignment = .center
        if(selectedCellIndex==indexPath ){
        cell.influentBackgroudView.isHidden=false
            }
        else{
            cell.influentBackgroudView.isHidden=true
            }
    cell.selectionStyle = .none
    }

    return cell 
} 
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let previousIndex=selectedCellIndex
        selectedCellIndex=indexPath
        if(previousIndex==selectedCellIndex){
            let nextVC = storyboard?.instantiateViewController(withIdentifier: "VisitedProfileViewController") as! VisitedProfileViewController
            nextVC.passedUser = firebaseUsers[selectedCellIndex!.row]
            navigationController?.pushViewController(nextVC, animated: true)
        }
        if(previousIndex==nil){
            tableView.reloadRows(at: [indexPath], with:.none)
        }
        else{
            if(previousIndex != indexPath){
                tableView.reloadRows(at: [indexPath,previousIndex!], with: .none)
            }
            else {
                tableView.reloadRows(at: [indexPath], with: .none)

                }
        }
}

谢谢大家的帮助!

【问题讨论】:

  • 可能不需要重新加载任何行,但既然你这样做了,我认为这个问题只会在你向下滚动表格视图时发生。我要说的第一个嫌疑人是行的估计高度与实际单元格高度不一致。如果您有静态单元格高度,请尝试在估计的高度委托方法中返回相同的值。如果您有自动单元格高度,您可以将实际高度存储到字典 [IndexPath: CGFloat] 中并在您的委托中使用该高度。你可以填写在didEndDisplaying委托方法(hightCache[indexPath] = cell.bounds.height)。
  • 嘿马蒂奇!谢谢您的回答,我只是将行高常量放在估计的行高委托方法中,问题就消失了。你救了我的一天! (将您的评论作为答案,我会接受并投票)

标签: ios swift xcode uitableview cell


【解决方案1】:

从 cmets 确定,您所面临的问题是通过调用 reloadRows 产生的,当您按下一个单元格并结合不正确的估计行高。因此,要么重新加载需要删除,要么修正估计的高度。第一个解决方案已包含在 A. Amini 提供的答案中。

由于许多此类异常与估计的行高有关,因此改进它仍然有意义。

对于简单的静态行高,您可以实现一个委托方法,例如

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return indexPath.row == 0 ? 44.0 : 200.0
}

其中的值与您的 heightForRowAt 方法中的值完全相同。或者,如果所有行都具有相同的高度,您可以删除此委托方法,但直接在某些初始化方法中设置高度,例如:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = 200
    tableView.estimatedRowHeight = tableView.rowHeight
}

当引入更复杂的单元格并使用自动单元格高度时,我们通常使用单元格的高度缓存。这意味着我们需要在单元格消失时保存它的高度,以便我们以后使用它。所有高度都保存在[IndexPath: CGFloat] 类型的字典中。一个非常简单的实现应该如下所示:

private var cellHeightCache: [IndexPath: CGFloat] = [IndexPath: CGFloat]()

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cellHeightCache[indexPath] = cell.bounds.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeightCache[indexPath] ?? UITableView.automaticDimension
}

在某些情况下需要额外的工作,例如在重新加载表视图时清除缓存。

发生这种情况的原因是表格视图不会重新加载您当前看到的单元格周围的单元格,而是检查内容偏移量并计算您应该看到的单元格。因此,由于估计的行高错误,任何重新加载调用都可能使表格视图跳转或动画单元格。

【讨论】:

    【解决方案2】:

    主要问题是您在“每个”选择后重新加载 tableView。 试试这个代码,如果你需要更多帮助,请告诉我。

    class MyTableViewClass: UITableViewController {
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "userCell" as! UserTableViewCell
    
            if !self.firebaseUsers.isEmpty {
    
                cell.influentBackgroudView.layer.cornerRadius = 10
                cell.influentImageView.contentMode = .scaleAspectFit
    
                let url = URL(string: firebaseUsers[indexPath.row].image)
    
                cell.influentImageView!.sd_setImage(with: url!, placeholderImage: UIImage(named: "placeholder"),options: [.continueInBackground,.progressiveDownload], completed: { (image, error, cacheType, imageURL) in
                    if error != nil {
                        cell.influentImageView.contentMode = .scaleAspectFill
                    }
                })
    
                cell.influentImageView.layer.cornerRadius=10
                cell.influentImageView.layer.masksToBounds = true
                cell.influentNameLabel.text = " \(firebaseUsers[indexPath.row].name) "
                cell.influentNameLabel.adjustsFontSizeToFitWidth = true
                cell.influentNameLabel.textAlignment = .center
    
                cell.selectionStyle = .none
            }
    
            return cell
        }
    
    
        override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            let nextVC = storyboard?.instantiateViewController(withIdentifier: "VisitedProfileViewController") as! VisitedProfileViewController
            nextVC.passedUser = firebaseUsers[indexPath.row]
            navigationController?.pushViewController(nextVC, animated: true)
        }
    }
    
    
    class UserTableViewCell: UITableViewCell {
    
        override var isSelected: Bool {
            didSet {
                if isSelected {
                    // Selected behavior, like change Background to blue
                } else {
                    // Deselected behavior, change Background to clear
                }
            }
        }
    }
    

    【讨论】: