键盘的关闭模式为互动式时,UICollectionView怪异单元格动画

时间:2019-05-30 12:41:04

标签: ios swift uicollectionview

我正在尝试使用UICollectionView建立一个聊天系统-集合视图是上下颠倒的,使用CGAffineTransform(scaleX: -1, y: 1)-的单元格也是如此,到目前为止一切顺利,我正在计算单元格大小手动使用NSString.boundingRect并按预期工作,然后我启用了collectionview的交互式键盘关闭功能,因为此后每当我稍微拖动一下键盘然后放开它,它就会变成一个非常奇怪的动画,我似乎无法理解找出触发它的原因。

我还使用Typist,这是一个小型实用程序类,可简化键盘https://github.com/totocaster/Typist的处理,我并不怀疑它,因为它是一个简单的包装UIKeyboard通知。

我尝试了几种方法,包括但不限于

1-使用单元格显示时禁用动画 UIView.setAnimationsEnabled(false),然后在返回无效的单元格之前再次启用它。

2-完全删除了上下颠倒的方法,即使在正常转换中也没有出现错误,仍然会发生

3-将整个UICollectionView实现重写为UITableView实现,这令人惊讶地产生了更好的效果,奇怪的动画虽然不如UICollectionView上强大,但是它仍在发生在某种程度上

将4个打字员全部删除,然后退回到NotificationCenter,手动操作和不操作将无法正常工作。

UICollectionView初始化

    lazy var chatCollectionView: UICollectionView = {
        let collectionViewFlowLayout = UICollectionViewFlowLayout()
        collectionViewFlowLayout.scrollDirection = .vertical
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .white
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.showsVerticalScrollIndicator = false
        collectionView.register(OutgoingTextCell.self, forCellWithReuseIdentifier: OutgoingTextCell.className)
        collectionView.register(IncomingTextCell.self, forCellWithReuseIdentifier: IncomingTextCell.className)
        collectionView.register(OutgoingImageCell.self, forCellWithReuseIdentifier: OutgoingImageCell.className)
        collectionView.register(IncomingImageCell.self, forCellWithReuseIdentifier: IncomingImageCell.className)
        collectionView.transform = CGAffineTransform(scaleX: 1, y: -1)
        collectionView.semanticContentAttribute = .forceLeftToRight
        collectionView.keyboardDismissMode = .interactive
        return collectionView
    }()

UICollectionView数据源/删除实现

extension ChatViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return presenter.numberOfRows
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if presenter.isSender(at: indexPath) {
            switch presenter.messageType(at: indexPath) {
            case .text:

                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: OutgoingTextCell.className, for: indexPath) as! OutgoingTextCell
                presenter.configure(cell: AnyConfigurableCell(cell), at: indexPath)
                return cell

            case .photo:

                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: OutgoingImageCell.className, for: indexPath) as! OutgoingImageCell
                presenter.configure(cell: AnyConfigurableCell(cell), at: indexPath)
                return cell
            }
        } else {
            switch presenter.messageType(at: indexPath) {
            case .text:

                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: IncomingTextCell.className, for: indexPath) as! IncomingTextCell
                presenter.configure(cell: AnyConfigurableCell(cell), at: indexPath)
                return cell

            case .photo:

                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: IncomingImageCell.className, for: indexPath) as! IncomingImageCell
                presenter.configure(cell: AnyConfigurableCell(cell), at: indexPath)
                return cell
            }
        }
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if presenter.messageType(at: indexPath) == .photo {
            return CGSize(width: view.frame.width, height: 250)
        } else {
            if let height = cachedSizes[presenter.uuidForMessage(at: indexPath)] {
                return CGSize(width: view.frame.width, height: height)
            } else {
                let height = calculateTextHeight(at: indexPath)
                cachedSizes[presenter.uuidForMessage(at: indexPath)] = height
                return CGSize(width: view.frame.width, height: height)
            }
        }
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
    }

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if indexPath.row == presenter.numberOfRows - 1 && !isPaginating {
            presenter.loadNextPage()
        }
    }

    private func calculateTextHeight(at indexPath: IndexPath) -> CGFloat {
        let approximateSize = CGSize(width: view.frame.width - 88, height: .greatestFiniteMagnitude)
        let estimatedHeight = NSString(string: presenter.contentForMessage(at: indexPath)).boundingRect(with: approximateSize, options: .usesLineFragmentOrigin, attributes: [.font: DinNextFont.regular.getFont(ofSize: 14)], context: nil).height
        return estimatedHeight + 40
    }

}

单元实现

class OutgoingTextCell: UICollectionViewCell, ConfigurableCell {

    lazy var containerView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .coral
        view.layer.cornerRadius = 10
        return view
    }()

    lazy var contentLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = .white
        label.numberOfLines = 0
        label.font = DinNextFont.regular.getFont(ofSize: 14)
        return label
    }()

    private lazy var stackView: UIStackView = {
        let stackView = UIStackView(arrangedSubviews: [timeLabel, checkMarkImageView])
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .horizontal
        stackView.distribution = .equalSpacing
        stackView.alignment = .center
        stackView.spacing = 4
        return stackView
    }()

    private lazy var checkMarkImageView: UIImageView = {
        let imageView = UIImageView(image: UIImage(named: "ic_check")?.withRenderingMode(.alwaysTemplate))
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.tintColor = .white
        imageView.heightAnchor.constraint(equalToConstant: 8).isActive = true
        imageView.widthAnchor.constraint(equalToConstant: 10).isActive = true
        return imageView
    }()

    lazy var timeLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = .white
        label.font = DinNextFont.regular.getFont(ofSize: 10)
        label.text = "12:14"
        return label
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        layoutUI()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func addSubviews() {
        addSubview(containerView)
        containerView.addSubview(contentLabel)
        containerView.addSubview(stackView)
    }

    func setupContainerViewConstraints() {
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor, constant: 0),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
            containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 100),
            containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: 34),
            containerView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 64)
        ])
    }

    private func setupContentLabelConstraints() {
        NSLayoutConstraint.activate([
            contentLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 4),
            contentLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8),
            contentLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8),
        ])
    }

    private func setupStackViewConstraints() {
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: 0),
            stackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -6),
            stackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -4),
            stackView.leadingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor, constant: 6),
            stackView.heightAnchor.constraint(equalToConstant: 20),
        ])
    }

    private func layoutUI() {
        addSubviews()
        setupContainerViewConstraints()
        setupContentLabelConstraints()
        setupStackViewConstraints()
    }

    func configure(model: MessageViewModel) {
        containerView.transform = CGAffineTransform(scaleX: 1, y: -1)
        contentLabel.text = model.content
        timeLabel.text = model.time
        checkMarkImageView.isHidden = !model.isSent
    }
}

UICollectionView的输出如下

https://www.youtube.com/watch?v=RajfZk5lGCQ

UITableview的输出如下

https://www.youtube.com/watch?v=6ayG7WhYKXo

这可能还不是很清楚,但是如果您仔细观察,您会发现单元格正在“简短地”制作动画,并且带有较长的消息,看起来像是“滑动动画”,根本没有吸引力。

0 个答案:

没有答案