Open up MessagesCollectionViewFlowLayout cell sizing API#491
Open up MessagesCollectionViewFlowLayout cell sizing API#491SD10 merged 10 commits intodevelopmentfrom
Conversation
| /// - Parameters: | ||
| /// - attributedText: The `NSAttributedString` used to calculate a size that fits. | ||
| /// - maxWidth: The max width available for the label. | ||
| internal func labelSize(for attributedText: NSAttributedString, considering maxWidth: CGFloat) -> CGSize { |
There was a problem hiding this comment.
These may be able to be combined into a single method for NSAttributedString
|
|
||
| var avatarFrame: CGRect = .zero | ||
| var avatarSize: CGSize = .zero | ||
| var avatarPosition = AvatarPosition(vertical: .cellBottom) |
There was a problem hiding this comment.
It would be better to maybe use IUO's here
| messageContainerView.frame = attributes.messageContainerFrame | ||
| } | ||
| guard let attributes = layoutAttributes as? MessagesCollectionViewLayoutAttributes else { return } | ||
| layoutMessageContainerView(with: attributes) |
There was a problem hiding this comment.
Add comment that this has to be called first
|
|
||
| // MARK: - Origin Calculations | ||
|
|
||
| func layoutAvatarView(with attributes: MessagesCollectionViewLayoutAttributes) { |
| avatarView.frame = CGRect(origin: origin, size: attributes.avatarSize) | ||
| } | ||
|
|
||
| func layoutMessageContainerView(with attributes: MessagesCollectionViewLayoutAttributes) { |
| messageContainerView.frame = CGRect(origin: origin, size: attributes.messageContainerSize) | ||
| } | ||
|
|
||
| func layoutTopLabel(with attributes: MessagesCollectionViewLayoutAttributes) { |
| cellTopLabel.frame = CGRect(origin: origin, size: topLabelSize) | ||
| } | ||
|
|
||
| func layoutBottomLabel(with attributes: MessagesCollectionViewLayoutAttributes) { |
|
It's possible that none of these need to be open func avatarPosition(for message: MessageType, at indexPath: IndexPath) -> AvatarPosition
open func cellTopLabelAlignment(for message: MessageType, at indexPath: IndexPath) -> LabelAlignment
open func cellBottomLabelAlignment(for message: MessageType, at indexPath: IndexPath) -> LabelAlignment
open func messageLabelInsets(for message: MessageType, at indexPath: IndexPath) -> UIEdgeInsets
open func messageContainerPadding(for message: MessageType, at indexPath: IndexPath) -> UIEdgeInsets |
|
|
||
| /// A intermediate context used to store recently calculated values used by | ||
| /// the `MessagesCollectionViewFlowLayout` object to reduce redundant calculations. | ||
| final class MessageIntermediateLayoutAttributes { |
| let avatarVertical = avatarPosition.vertical | ||
|
|
||
| switch (labelAlignment, avatarHorizontal) { | ||
|
|
| open func layoutMessageContainerView(with attributes: MessagesCollectionViewLayoutAttributes) { | ||
| guard attributes.messageContainerSize != .zero else { return } | ||
|
|
||
| var origin: CGPoint = .zero |
There was a problem hiding this comment.
var origin = CGPoint.zero for consistency
There was a problem hiding this comment.
I think I'll flip flop them. I like explicitly typed better 👍
| let bottomLabelPadding = bottomLabelAlignment.insets | ||
| let bottomLabelSize = attributes.bottomLabelSize | ||
|
|
||
| var origin: CGPoint = .zero |
There was a problem hiding this comment.
var origin = CGPoint.zero for consistency
| } | ||
| return messagesCollectionView | ||
| } | ||
| fileprivate var layoutContextCache: [MessageID: MessageCellLayoutContext] = [:] |
There was a problem hiding this comment.
I prefer to choose NSCache , it can control the number of object in memory.
There was a problem hiding this comment.
No layoutContextCache empty operation when prepareLayout?
There was a problem hiding this comment.
Do you think it's necessary? I don't even use the prepareLayout method 🤔
There was a problem hiding this comment.
@SD10 , I think it's necessary, dictionary has two weakness, one is occupy memory, another is performance penalty , the more number of data, the more time consumption when get object because of hash conflict.
Yep, we don't use prepareLayout, I mean we should override it that prepare something like empty cache when layout needs recalculation.
There was a problem hiding this comment.
I currently empty the cache inside of here:
I'm fine with changing this to a more efficient cache 👍
| @@ -0,0 +1,21 @@ | |||
| // | |||
| // UIEdgeInsets+Extensions.swift | |||
| // MessageKit | |||
40eb589 to
f77100c
Compare
Summary of Pull Request:
Resolves: #489, #487, #453, #396, #331, #234
MessageCollectionViewCellsubview frame calculations to methods of the cell Move all cell subview origin calculations into MessagesCollectionViewCell #487This allows users to control the layout of subviews by an
overrideto one of these methods. Theoretically, they could override and make the functions a no-op, handling layout with something like autolayout.This also allows the
MessagesCollectionViewFlowLayoutto become more static. While a lot of these open methods will call intoMessagesLayoutDelegateto retrieve the values, you can override the method that wraps the delegate call and return a static value instead.MessageIntermediateLayoutAttributeswithMessageCellLayoutContextThis is a lighter weight type and caches less information #489. We no longer hold a reference to any
IndexPathorMessageType. We also no longer cache cell subview origins since the layout is now handled by theMessageCollectionViewCell. This fixes the annoying frame caching ofcellTopLabelandcellBottomLabelin #234 and makes cell attribute caching much more useful again.I expect that I messed at least one thing up with these changes. It will need some review. However, this API should be far easier to test now considering most things have been changed from
privatetointernaloropen.TODO: