当前位置: 首页 > 知识库问答 >
问题:

具有动态高度和宽度的UITableView单元格没有得到正确重用

施喜
2023-03-14

我有一个具有动态高度和宽度的UITableView单元格。最初,它工作正常,但在重用旧单元格时,约束设置不正确。我正在停用所有旧的约束并再次激活它们。我还调用了 。但这于事无补。

自动高度设置:(我想这会引起一个问题)

discussionTableView.rowHeight = UITableViewAutomaticDimension
discussionTableView.estimatedRowHeight = 10

我的表视图单元格:

class DiscussionChatMessageCell: UITableViewCell {
    
    private let messageLabel: UILabel
    private let senderNameLabel: UILabel
    private let messageBubble: UIView
    
    let screenWidth: CGFloat
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        messageLabel = UILabel()
        senderNameLabel = UILabel()
        screenWidth = UIScreen.main.bounds.size.width
        messageBubble = UIView()
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        //        self.contentView.backgroundColor = .clear
        
        self.contentView.addSubview(messageBubble)
        messageBubble.translatesAutoresizingMaskIntoConstraints = false
        
        messageBubble.addSubview(senderNameLabel)
        senderNameLabel.translatesAutoresizingMaskIntoConstraints = false
        senderNameLabel.numberOfLines = 0
        senderNameLabel.lineBreakMode = .byCharWrapping
        senderNameLabel.font = UIFont.boldSystemFont(ofSize: 15)
        senderNameLabel.textColor = .white
        
        messageBubble.addSubview(messageLabel)
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        messageLabel.numberOfLines = 0
        messageLabel.lineBreakMode = .byWordWrapping
        messageLabel.font = UIFont.systemFont(ofSize: 13)
        messageLabel.textColor = .white
        
        NSLayoutConstraint.activate([
            messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
            messageBubble.bottomAnchor.constraint(equalTo:  self.contentView.bottomAnchor, constant: -10),
            messageBubble.widthAnchor.constraint(lessThanOrEqualToConstant: screenWidth - 100),
            
            senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
            senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
            senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
            
            messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
            messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
            messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
            messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
        ])
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configureCell(message: String, isSender: Bool) {
        senderNameLabel.text = "Default Sender"
        messageLabel.text = message
        
        for constraint in messageBubble.constraints {
            //            messageBubble.removeConstraint(constraint)
            constraint.isActive = false
        }
        for constraint in messageLabel.constraints {
            //            messageLabel.removeConstraint(constraint)
            constraint.isActive = false
        }
        for constraint in senderNameLabel.constraints {
            //senderNameLabel.removeConstraint(constraint)
            constraint.isActive = false
        }
        
        NSLayoutConstraint.deactivate([
            messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10),
            messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
        ])
        NSLayoutConstraint.activate([
            messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
            messageBubble.bottomAnchor.constraint(equalTo:  self.contentView.bottomAnchor, constant: -10),
            messageBubble.widthAnchor.constraint(lessThanOrEqualToConstant: screenWidth - 100),
            
            senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
            senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
            senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
            
            messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
            messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
            messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
            messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
        ])
        
        messageBubble.backgroundColor = isSender ? accentColor : .gray
        if isSender {
            
            NSLayoutConstraint.activate([
                messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10)
            ])
            
            //            let corners: UIRectCorner  = [.topLeft, .topRight, .bottomLeft]
            //            roundCorners(corners: corners, isSender: isSender)
            
        } else {
            NSLayoutConstraint.activate([
                messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
            ])
            
            //            let corners: UIRectCorner  = [.topLeft, .topRight, .bottomRight]
            //            roundCorners(corners: corners, isSender: isSender)
        }
    }

重用单元格:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let discussionChatMessageCell = tableView.dequeueReusableCell(withIdentifier: discussionChatId, for: indexPath) as? DiscussionChatMessageCell else { return UITableViewCell()}
        
        discussionChatMessageCell.configureCell(message: messages[indexPath.row], isSender: isSender[indexPath.row])

        discussionChatMessageCell.setNeedsLayout()
        discussionChatMessageCell.layoutIfNeeded()
        
        return discussionChatMessageCell
    }

重用单元格之前:

重复使用单元格后:

共有3个答案

商燕七
2023-03-14

您可以多次添加子视图,但它是可重用的。别忘了这件事。在。AddSubView(...)之前添加下一个代码

contentView.subviews.forEach { $0.removeFromSuperview() }

或仅更改视图值,不要每次都添加

彭弘方
2023-03-14

我更改了相对于单元格而不是内容视图的代码设置 约束:

 messageBubble.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
 messageBubble.bottomAnchor.constraint(equalTo:  self.bottomAnchor, constant: -10)

正在调用的 :

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let discussionChatMessageCell = tableView.dequeueReusableCell(withIdentifier: discussionChatId, for: indexPath) as? DiscussionChatMessageCell else { return UITableViewCell()}
        
discussionChatMessageCell.configureCell(message: messages[indexPath.row], isSender: isSender[indexPath.row])

discussionChatMessageCell.layoutIfNeeded()
        
return discussionChatMessageCell

}
葛鸿熙
2023-03-14

您所修改的约束远远超出了您的需要。

一个更好的方法是为你的“气泡”创建前导和后导约束--并且改变它们的优先级来决定使用哪一个。

因此,如果是“接收”消息,我们将前导约束优先级设置为高,将后导约束优先级设置为低。如果是“发送”的消息,我们会做相反的操作。

试试看:

class DiscussionChatMessageCell: UITableViewCell {
    
    let accentColor: UIColor = .systemYellow
    
    private let messageLabel: UILabel
    private let senderNameLabel: UILabel
    private let messageBubble: UIView
    
    private var bubbleLeadingConstraint: NSLayoutConstraint!
    private var bubbleTrailingConstraint: NSLayoutConstraint!

    // not needed
    //let screenWidth: CGFloat

    // wrong signature
    //override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        
        messageLabel = UILabel()
        senderNameLabel = UILabel()
        messageBubble = UIView()

        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        //        self.contentView.backgroundColor = .clear
        
        self.contentView.addSubview(messageBubble)
        messageBubble.translatesAutoresizingMaskIntoConstraints = false
        
        messageBubble.addSubview(senderNameLabel)
        senderNameLabel.translatesAutoresizingMaskIntoConstraints = false
        senderNameLabel.numberOfLines = 0
        senderNameLabel.lineBreakMode = .byCharWrapping
        senderNameLabel.font = UIFont.boldSystemFont(ofSize: 15)
        senderNameLabel.textColor = .white
        
        messageBubble.addSubview(messageLabel)
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        messageLabel.numberOfLines = 0
        messageLabel.lineBreakMode = .byWordWrapping
        messageLabel.font = UIFont.systemFont(ofSize: 13)
        messageLabel.textColor = .white
        
        // set hugging and compression resistance for Name label
        senderNameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
        senderNameLabel.setContentHuggingPriority(.required, for: .vertical)
        
        // create bubble Leading and Trailing constraints
        bubbleLeadingConstraint = messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
        bubbleTrailingConstraint = messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10)

        // priority will be changed in configureCell()
        bubbleLeadingConstraint.priority = .defaultHigh
        bubbleTrailingConstraint.priority = .defaultLow
        
        NSLayoutConstraint.activate([
            
            bubbleLeadingConstraint,
            bubbleTrailingConstraint,
            
            messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
            messageBubble.bottomAnchor.constraint(equalTo:  self.contentView.bottomAnchor, constant: -10),
            
            messageBubble.widthAnchor.constraint(lessThanOrEqualTo: self.contentView.widthAnchor, constant: -100),
            
            senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
            senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
            senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
            
            messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
            messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
            messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
            messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
            
        ])
        
        // corners will have radius: 10
        messageBubble.layer.cornerRadius = 10
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configureCell(message: String, isSender: Bool) {
        senderNameLabel.text = "Default Sender"
        messageLabel.text = message

        bubbleLeadingConstraint.priority = isSender ? .defaultHigh : .defaultLow
        bubbleTrailingConstraint.priority = isSender ? .defaultLow : .defaultHigh

        messageBubble.backgroundColor = isSender ? accentColor : .gray
        
        messageBubble.layer.maskedCorners = isSender ?
            // topLeft, topRight, bottomRight
            [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
            :
            // topLeft, topRight, bottomLeft
            [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner]

    }

}

附带说明: 中不需要这两行:

    //discussionChatMessageCell.setNeedsLayout()
    //discussionChatMessageCell.layoutIfNeeded()
 类似资料:
  • 我觉得这可能是一个常见的问题,我想知道是否有任何共同的解决方案。 基本上,我的UITableView拥有每个单元格的动态单元格高度。如果我不在UITableView的顶部,并且我< code > table view . reloaddata(),向上滚动会变得不稳定。 我认为这是因为我在向上滚动时重新加载了数据,UITableView正在重新计算每个可见单元格的高度。如何减轻这种情况,或者如何只

  • 可变长度的文本数据正在注入到表视图单元格标签中。为了正确调整每个单元格高度的大小,我在中实现了: 这估计高度为88.0像素,如果更大,应该会自动调整高度大小。它非常适用于尚未滚动到的单元格(因为在滚动到单元格时被调用),但不适用于最初在加载数据时在屏幕上呈现的单元格。 我尝试过重新加载数据(正如许多其他资源中建议的那样): 在这两个和,它没有帮助。我迷路了…有人知道如何渲染最初在屏幕上加载的单元格

  • 问题内容: 我觉得这可能是一个常见的问题,并且想知道是否有任何通用的解决方案。 基本上,我的UITableView具有每个单元格的动态单元格高度。如果我不在UITableView和I的顶部,则向上滚动变得跳动。 我相信这是由于以下事实:当我向上滚动时重新加载数据时,UITableView正在重新计算每个可见的单元格的高度。如何缓解这种情况,或者如何仅将数据从某个IndexPath重新加载到UITa

  • 我有用自定义单元格(继承自)填充的用户界面视图,每个单元格都包含一个,该视图根据其内容自动调整大小。事情是这样的,如何根据内容(可变视图)更改 单元格的高度。 该解决方案必须是动态的,因为用于填充< code>UIWebViews的HTML是从不断变化的提要中解析的。 我觉得我需要使用委托方法 中更改单元格的高度吗? 任何帮助都是伟大的。谢谢 两年多前我问过这个问题。通过介绍自动布局,可以找到iO

  • 我需要显示一组具有不同高度的collectionViewCells。视图太复杂,我不想手动计算预期的高度。我想强制自动布局来计算单元格高度 在之外调用会破坏ColltionView并导致其崩溃 另一个问题是单元格不在单独的xib中,所以我无法手动实例化临时单元格并将其用于高度计算。 对此有什么解决办法吗? 编辑: 一旦被调用,就会发生崩溃。如果我不调用该方法并返回大小,则一切正常,单元格显示为没有

  • 我是swift新手,正在从事Swift 3.0中的一个项目,其中我有一个包含三个自定义单元格的UITableView。在第一个中,我只有一个图像、按钮和标签。在第二个,我有一个图像加上标签以及可扩展和可折叠的标题。因此,第二个单元格有三个不同的部分。最后,第三个也只包含一个标签。在第一个单元格中,UILabel被设置在包含一个人的描述的图像下面(已经设置了约束)。我的要求是只对第一个单元格根据描述

  • 我有一个表格视图,每个单元格都有自己的高度,因此不适合使用。相反,现在我使用和。这意味着它调用单元格上的函数来确定它的高度。这一切都很好。 问题是当你重新加载屏幕上的单元格时。例如,向下滚动以显示单元格10,然后重新加载单元格10,效果很好。但是当您开始向上滚动,经过您已经看到的单元格时,它会恢复到每个单元格的estimatedRowHeight,完全不考虑< code>sizeThatFits,

  • 问题内容: 我有一个UICollectionView,它从包含标签的可重用单元格加载单元格。数组为该标签提供内容。我可以使用sizeToFit轻松根据内容宽度调整标签宽度的大小。但是我无法制作适合标签的单元格。 这是代码 问题答案: 作为回报,文字大小