隐藏的TableView在模型未显示的可见单元之后隐藏单元

时间:2019-05-17 08:30:32

标签: swift xcode uitableview uikit

我目前正在为客户端构建一个聊天机器人应用程序。 使用带有自定义单元格的UITableView可以实现聊天。 来自该机器人的文本消息被构建为带有UITextView的自定义UITableViewCell(因为链接需要单击该消息,否则我将使用UILabel)包装在UIView容器中以在其周围绘制气泡。同样,这个容器视图有一个阴影可以取悦设计师:P

每次收到来自漫游器的新消息时,该消息都会延迟一小部分时间,以便用户可以先阅读该消息。在那个时候,所谓的WaitingMessageTableViewCell会显示在索引处,新单元将到达。时间到期后,将从模型中删除WaitingMessageTableViewCell,然后从tableView中删除,并插入新单元格。

            tableView.beginUpdates()
            if let insertRows = insertRows {
                tableView.insertRows(at: insertRows, with: .none)
            }
            if let deleteRows = deleteRows {
                tableView.deleteRows(at: deleteRows, with: .none)
            }
            if let reloadRows = reloadRows {
                tableView.reloadRows(at: reloadRows, with: .none)
            }
            tableView.endUpdates()

设计人员发现问题所在,即当WaitingMessageTableViewCell即将出现时,该单元的阴影在短时间内变得更强。我进行了调试,发现在插入WaitingMessageTableViewCell之前它出现在单元格之前,对我来说这没有意义(这是重用机制吗?!) 现在出现了一个非常奇怪的部分:我使用UI分析器内置到Xcode中,看到在可见单元后面有隐藏的单元,这些单元未由模型表示。

(lldb) po tableView.subviews
▿ 5 elements
  ▿ 0 : <CHAT.WaitingMessageTableViewCell: 0x7fdef286a600; baseClass = UITableViewCell; frame = (0 395.5; 375 40); hidden = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x600003952d80>>
  ▿ 1 : <CHAT.AgentMessageTableViewCell: 0x7fdef2861e00; baseClass = UITableViewCell; frame = (0 294.5; 375 101); hidden = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x6000039bf160>>
  ▿ 2 : <CHAT.AgentMessageTableViewCell: 0x7fdef3897600; baseClass = UITableViewCell; frame = (0 395.5; 375 101.5); opaque = NO; autoresize = W; layer = <CALayer: 0x6000039aba20>>
  ▿ 3 : <CHAT.AgentMessageTableViewCell: 0x7fdef382e200; baseClass = UITableViewCell; frame = (0 294.5; 375 101); opaque = NO; autoresize = W; layer = <CALayer: 0x600003955a00>>
  ▿ 4 : <CHAT.WelcomeTableViewCell: 0x7fdef2882c00; baseClass = UITableViewCell; frame = (0 0; 375 294.5); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x600003951160>>
(lldb) po tableView.visibleCells
▿ 3 elements
  ▿ 0 : <CHAT.WelcomeTableViewCell: 0x7fdef2882c00; baseClass = UITableViewCell; frame = (0 0; 375 294.5); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x600003951160>>
  ▿ 1 : <CHAT.AgentMessageTableViewCell: 0x7fdef382e200; baseClass = UITableViewCell; frame = (0 294.5; 375 101); opaque = NO; autoresize = W; layer = <CALayer: 0x600003955a00>>
  ▿ 2 : <CHAT.AgentMessageTableViewCell: 0x7fdef3897600; baseClass = UITableViewCell; frame = (0 395.5; 375 101.5); opaque = NO; autoresize = W; layer = <CALayer: 0x6000039aba20>>
(lldb) po AssistantDataManager.shared.cellModels
▿ 3 elements
  ▿ 0 : <WelcomeCellModel: 0x600002c80740>
  ▿ 1 : <SimpleAgentTextCellModel: 0x600001af0050>
  ▿ 2 : <AskQuestionCellModel: 0x600002c80400>

这也是我的cellForRows方法:

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

        guard indexPath.row < AssistantDataManager.shared.cellModels.count else {
            return UITableViewCell()
        }
        let cellModel = AssistantDataManager.shared.cellModels[indexPath.row]

        if let cell = tableView.dequeueReusableCell(withIdentifier: cellModel.cellReuseIdentifier) as? AssistantActionBaseTableViewCell {
            //Quickfix for stronger shadow
            cell.backgroundColor = Colors.tableViewBackgroundColor

            cell.configure(with: cellModel)
            if let topContraint = cell.topContraint {
                topContraint.constant = calculateTopContraintSpacing(for: indexPath)
            }

            if let bottomContraint = cell.bottomContraint {
                bottomContraint.constant = 0
            }

            cell.selectionStyle = .none
            return cell
        }
        return UITableViewCell()
    }

有人可以告诉我为什么新插入的WaitingMessageTableViewCell首先位于上方的单元格之后然后向下移动,为什么我的tableView填充的单元格要多得多?

非常感谢您。

2 个答案:

答案 0 :(得分:0)

我发现了为什么新单元格首先显示在前一个单元格之后然后向下移动的原因。它是隐式动画和生命周期的结合。

以前,我是在一次批量更新中插入删除并重新加载所有单元格的。由于存在隐式动画,因此tableView决定对此变化进行动画处理。但是,当我第一次插入和删除,然后重新加载单元格时,一切都会按预期进行。

tableView.performBatchUpdates({
    if let insertRows = insertRows {
        tableView.insertRows(at: insertRows, with: .none)
    }
    if let deleteRows = deleteRows {
        tableView.deleteRows(at: deleteRows, with: .none)
    }

}) { (finished) in
    self.tableView.performBatchUpdates({
        if let reloadRows = reloadRows {
            self.tableView.reloadRows(at: reloadRows, with: .none)
        }
    }, completion: nil)
}

答案 1 :(得分:0)

建议您不要使用运行时为此添加和删除Cell的方法,而是建议您使用tableView的footerView功能,您可以创建一个自定义视图并将其分配给tableView

例如

import UIKit

class WaitingMessageView: UIView{
    var width: CGFloat?
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit(){
        [imageView,label].forEach{addSubview($0)}
        setConstraints()
    }

     func setConstraints() {
        let constrains = [
            imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
            imageView.widthAnchor.constraint(equalToConstant: 30),
            imageView.heightAnchor.constraint(equalToConstant: 30),
            imageView.centerYAnchor.constraint(equalTo: centerYAnchor),

            label.centerYAnchor.constraint(equalTo: centerYAnchor),
            label.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10)
        ]
        NSLayoutConstraint.activate(constrains)
    }


    public let imageView: UIImageView = {
        let v = UIImageView()
        v.contentMode = .scaleAspectFill
        v.clipsToBounds = true
        v.translatesAutoresizingMaskIntoConstraints = false
        v.image = #imageLiteral(resourceName: "babs")
        v.layer.cornerRadius = 15
        return v
    }()

    public let label: UILabel = {
        let v = UILabel()
        v.text = "thinking..."
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
}

在UIViewController的viewDidLoad或所需的任何生命周期方法中:

tableView.tableFooterView = WaitingMessageView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 44))

,当您要显示/隐藏WaitingMessageView时,可以分别使用以下属性,其值为false / true:

tableView?.tableFooterView?.isHidden