Chatto icon indicating copy to clipboard operation
Chatto copied to clipboard

How do I make autosizing custom cell?

Open EddieLukeAtmey opened this issue 6 years ago • 8 comments

I've followed the tutorial and guideline and stuff, finally managed to create a chatVC with my custom cell. However, there's this autosizing issue where I don't know how to make it work.

Setup:

final class ChatContentCVC:  BaseChatViewController {
    override var canBecomeFirstResponder: Bool { return false }
    override func createChatInputView() -> UIView { return UIView() }

    override func createPresenterBuilders() -> [ChatItemType: [ChatItemPresenterBuilderProtocol]] {
       return [ChatMessageModel.type: [ChatMsgPresenterBuilder()] 
    }
}

Where ChatMessageModel & ChatMsgPresenterBuilder is my custom model & builder. Typpically, I don't want to inherit or import the ChattoAdditions, so I built my own text message cell, and it works, until...

The Presenter.

final class ChatMsgPresenter: ChatItemPresenterProtocol {
 
    let msgModel: ChatMessageModel
    private weak var sizingCell: ChatMsgCVCell?

    init (msgModel: ChatMessageModel, sizingCell: ChatMsgCVCell) {
        self.msgModel = msgModel
        self.sizingCell = sizingCell
    }
 
    static func registerCells(_ collectionView: UICollectionView) {
        let sending = ChatMsgCVCell.sendingCellId
        let receiving = ChatMsgCVCell.receivingCellId
        collectionView.register(UINib(nibName: sending, bundle: Bundle(for: self)), forCellWithReuseIdentifier: sending)
        collectionView.register(UINib(nibName: receiving, bundle: Bundle(for: self)), forCellWithReuseIdentifier: receiving)
    }

    func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell {

        let cellId = msgModel.owner == .me ? ChatMsgCVCell.sendingCellId : ChatMsgCVCell.receivingCellId
        return collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
    }

    func configureCell(_ cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?) {
        guard let cell = cell as? ChatMsgCVCell else { return }
        cell.fillData(msgModel)
    }

    var canCalculateHeightInBackground: Bool {
        return false
    }

Now's the tricky part and also the question of this issue: calculate cell's autosizing height

  func heightForCell(maximumWidth width: CGFloat, decorationAttributes: 
 ChatItemDecorationAttributesProtocol?) -> CGFloat {
     // How should I get dynamic sizing here?
     // Should I cache the layout like ChattoAdditions' TextMessageCollectionViewCell?
    //  I've created my own sizingCell, but it doesn't seem to work.
    // Also, using sizingCell might freeze the app if var canCalculateHeightInBackground = false
    return 90 // hard code works okay, but not autosizing is a big problem.
}

EddieLukeAtmey avatar Dec 11 '18 10:12 EddieLukeAtmey

I haven't given this much thought, just off the top of my head. Could modeling something like the estimated size behavior of UITableView/UICollectionView serve as a method of handling the resizing issue? Is there a corresponding set of behaviors in this library? Other than that I'd try putting together a custom cell that leverages auto layout.

mingsai avatar Feb 07 '19 15:02 mingsai

It would be nice if Chatto could support:

func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat {
    return UICollectionViewFlowLayout.automaticSize
}

So that it leaves the cells to auto-layout handling the resizing.

Kalzem avatar Jun 06 '19 14:06 Kalzem

I have the same issue. Have you found a solution?

Loriens avatar Feb 11 '20 16:02 Loriens

I've left the project a long time ago and don't have src code so I don't remember what I did at the time. Probably some hack, or just doesn't use it. It'd be nice if you can post your solution here

EddieLukeAtmey avatar Feb 12 '20 03:02 EddieLukeAtmey

I have found a hack.

I use AutoLayout for creating UI, so I don't know the real cell size before it is created.

  1. Therefore, I create a real cell instance in the initialization of ChatItemProtocol and save its size there (it's my cell model):
let cell: ChatUsernameMessageCollectionCell = UIView.fromNib()
let presenter = ChatUsernameMessageCellPresenter(model: self)
presenter.configureCell(cell, decorationAttributes: nil)
cell.layoutSubviews()
self._cellHeight = cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height

UIView.fromNib() - it creates a view from nib using Bundle(for: ...).loadNibNamed(...)

Note: Multi-line UILabel with flexible width requires preferredMaxLayoutWidth. If it isn't set, systemLayoutSizeFitting returns the wrong value.

  1. Presenter stores the cell model, so I can get the cell height before it is created:
func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat {
    return model.cellHeight
}

I think it is a bad solution because I create a cell twice. But I couldn't find a better way.

Loriens avatar Feb 12 '20 12:02 Loriens

Same issue here, I have the custom text and photocells but auto calculating height not possible :(

NairaManukyan avatar Mar 08 '21 21:03 NairaManukyan

Still, the issue present, need a normal fix, but a workaround. @wiruzx - could you please check it ?

aramhakobyan avatar Mar 08 '21 22:03 aramhakobyan

Hey @NairaManukyan, @aramhakobyan

We're currently mostly using manual layout in our project, so we don't have the best autolayout support for cells.

The reason we initially used manual layout was performance while scrolling. It just wasn't possible to get 60fps scrolling on older devices.

Since then Apple has improved the autolayout performance, so we have plans for the near future to make it work nicely with autolayout.

In mean time you can use sizing cells approach.

Thanks

wiruzx avatar Mar 16 '21 21:03 wiruzx