mirror of
https://github.com/MessageKit/MessageKit.git
synced 2026-02-06 19:03:19 +00:00
Add customizable accessoryView besides messageContainer
This commit is contained in:
@@ -180,14 +180,17 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
|
||||
case .text, .attributedText, .emoji:
|
||||
let cell = messagesCollectionView.dequeueReusableCell(TextMessageCell.self, for: indexPath)
|
||||
cell.configure(with: message, at: indexPath, and: messagesCollectionView)
|
||||
messagesDataSource.configCell(cell, for: message, at: indexPath)
|
||||
return cell
|
||||
case .photo, .video:
|
||||
let cell = messagesCollectionView.dequeueReusableCell(MediaMessageCell.self, for: indexPath)
|
||||
cell.configure(with: message, at: indexPath, and: messagesCollectionView)
|
||||
messagesDataSource.configCell(cell, for: message, at: indexPath)
|
||||
return cell
|
||||
case .location:
|
||||
let cell = messagesCollectionView.dequeueReusableCell(LocationMessageCell.self, for: indexPath)
|
||||
cell.configure(with: message, at: indexPath, and: messagesCollectionView)
|
||||
messagesDataSource.configCell(cell, for: message, at: indexPath)
|
||||
return cell
|
||||
case .custom:
|
||||
fatalError(MessageKitError.customDataUnresolvedCell)
|
||||
|
||||
@@ -68,6 +68,9 @@ open class MessageSizeCalculator: CellSizeCalculator {
|
||||
|
||||
attributes.messageBottomLabelAlignment = messageBottomLabelAlignment(for: message)
|
||||
attributes.messageBottomLabelSize = messageBottomLabelSize(for: message, at: indexPath)
|
||||
|
||||
attributes.accessoryViewSize = accessoryViewSize(for: message)
|
||||
attributes.accessoryViewPadding = accessoryViewPadding(for: message)
|
||||
}
|
||||
|
||||
open override func sizeForItem(at indexPath: IndexPath) -> CGSize {
|
||||
@@ -185,6 +188,20 @@ open class MessageSizeCalculator: CellSizeCalculator {
|
||||
return isFromCurrentSender ? outgoingMessageBottomLabelAlignment : incomingMessageBottomLabelAlignment
|
||||
}
|
||||
|
||||
// MARK: - Accessory View
|
||||
|
||||
public func accessoryViewSize(for message: MessageType) -> CGSize {
|
||||
let layoutDelegate = messagesLayout.messagesLayoutDelegate
|
||||
let collectionView = messagesLayout.messagesCollectionView
|
||||
return layoutDelegate.accessoryViewSize(for: message, in: collectionView)
|
||||
}
|
||||
|
||||
public func accessoryViewPadding(for message: MessageType) -> UIEdgeInsets {
|
||||
let layoutDelegate = messagesLayout.messagesLayoutDelegate
|
||||
let collectionView = messagesLayout.messagesCollectionView
|
||||
return layoutDelegate.accessoryViewPadding(for: message, in: collectionView)
|
||||
}
|
||||
|
||||
// MARK: - MessageContainer
|
||||
|
||||
public func messageContainerPadding(for message: MessageType) -> UIEdgeInsets {
|
||||
@@ -201,7 +218,9 @@ open class MessageSizeCalculator: CellSizeCalculator {
|
||||
open func messageContainerMaxWidth(for message: MessageType) -> CGFloat {
|
||||
let avatarWidth = avatarSize(for: message).width
|
||||
let messagePadding = messageContainerPadding(for: message)
|
||||
return messagesLayout.itemWidth - avatarWidth - messagePadding.horizontal
|
||||
let accessoryWidth = accessoryViewSize(for: message).width
|
||||
let accessoryPadding = accessoryViewPadding(for: message)
|
||||
return messagesLayout.itemWidth - avatarWidth - messagePadding.horizontal - accessoryWidth - accessoryPadding.horizontal
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
@@ -46,6 +46,9 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
|
||||
public var messageBottomLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
|
||||
public var messageBottomLabelSize: CGSize = .zero
|
||||
|
||||
public var accessoryViewSize: CGSize = .zero
|
||||
public var accessoryViewPadding: UIEdgeInsets = .zero
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
open override func copy(with zone: NSZone? = nil) -> Any {
|
||||
@@ -63,6 +66,8 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
|
||||
copy.messageTopLabelSize = messageTopLabelSize
|
||||
copy.messageBottomLabelAlignment = messageBottomLabelAlignment
|
||||
copy.messageBottomLabelSize = messageBottomLabelSize
|
||||
copy.accessoryViewSize = accessoryViewSize
|
||||
copy.accessoryViewPadding = accessoryViewPadding
|
||||
return copy
|
||||
// swiftlint:enable force_cast
|
||||
}
|
||||
@@ -82,6 +87,8 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
|
||||
&& attributes.messageTopLabelSize == messageTopLabelSize
|
||||
&& attributes.messageBottomLabelAlignment == messageBottomLabelAlignment
|
||||
&& attributes.messageBottomLabelSize == messageBottomLabelSize
|
||||
&& attributes.accessoryViewSize == accessoryViewSize
|
||||
&& attributes.accessoryViewPadding == accessoryViewPadding
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -93,6 +93,14 @@ public protocol MessagesDataSource: AnyObject {
|
||||
/// The default value returned by this method is `nil`.
|
||||
func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString?
|
||||
|
||||
/// Provide a chance to config a cell before it's presented. e.g. to customize the accessoryView.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - cell: The `MessageContentCell` that can be configured.
|
||||
/// - message: The `MessageType` that will be displayed by this cell.
|
||||
/// - indexPath: The `IndexPath` of the cell.
|
||||
///
|
||||
func configCell(_ cell: MessageContentCell, for message: MessageType, at indexPath: IndexPath)
|
||||
}
|
||||
|
||||
public extension MessagesDataSource {
|
||||
@@ -117,4 +125,5 @@ public extension MessagesDataSource {
|
||||
return nil
|
||||
}
|
||||
|
||||
func configCell(_ cell: MessageCollectionViewCell, for message: MessageType, at indexPath: IndexPath) {}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,25 @@ public protocol MessagesLayoutDelegate: AnyObject {
|
||||
/// The default value returned by this method is zero.
|
||||
func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat
|
||||
|
||||
/// Specifies the size for the `MessageContentCell`'s accessory view.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The `MessageType` that will be displayed for this cell.
|
||||
/// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
|
||||
///
|
||||
/// - Note:
|
||||
/// The default value returned by this method is zero.
|
||||
func accessoryViewSize(for message: MessageType, in messagesCollectionView: MessagesCollectionView) -> CGSize
|
||||
|
||||
/// Specifies the padding for the `MessageContentCell`'s accessory view.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: The `MessageType` that will be displayed for this cell.
|
||||
/// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
|
||||
///
|
||||
/// - Note:
|
||||
/// The default value returned by this method is zero.
|
||||
func accessoryViewPadding(for message: MessageType, in messagesCollectionView: MessagesCollectionView) -> UIEdgeInsets
|
||||
}
|
||||
|
||||
public extension MessagesLayoutDelegate {
|
||||
@@ -104,4 +123,12 @@ public extension MessagesLayoutDelegate {
|
||||
func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
|
||||
return 0
|
||||
}
|
||||
|
||||
func accessoryViewSize(for message: MessageType, in messagesCollectionView: MessagesCollectionView) -> CGSize {
|
||||
return .zero
|
||||
}
|
||||
|
||||
func accessoryViewPadding(for message: MessageType, in messagesCollectionView: MessagesCollectionView) -> UIEdgeInsets {
|
||||
return .zero
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ open class MessageContentCell: MessageCollectionViewCell {
|
||||
return label
|
||||
}()
|
||||
|
||||
// Should only add customized subviews - don't change accessoryView itself.
|
||||
open var accessoryView: UIView = .init()
|
||||
|
||||
/// The `MessageCellDelegate` for the cell.
|
||||
open weak var delegate: MessageCellDelegate?
|
||||
|
||||
@@ -74,6 +77,7 @@ open class MessageContentCell: MessageCollectionViewCell {
|
||||
}
|
||||
|
||||
open func setupSubviews() {
|
||||
contentView.addSubview(accessoryView)
|
||||
contentView.addSubview(cellTopLabel)
|
||||
contentView.addSubview(messageTopLabel)
|
||||
contentView.addSubview(messageBottomLabel)
|
||||
@@ -86,6 +90,9 @@ open class MessageContentCell: MessageCollectionViewCell {
|
||||
cellTopLabel.text = nil
|
||||
messageTopLabel.text = nil
|
||||
messageBottomLabel.text = nil
|
||||
for view in accessoryView.subviews {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
@@ -99,6 +106,7 @@ open class MessageContentCell: MessageCollectionViewCell {
|
||||
layoutCellTopLabel(with: attributes)
|
||||
layoutMessageTopLabel(with: attributes)
|
||||
layoutAvatarView(with: attributes)
|
||||
layoutAccessoryView(with: attributes)
|
||||
}
|
||||
|
||||
/// Used to configure the cell.
|
||||
@@ -270,5 +278,25 @@ open class MessageContentCell: MessageCollectionViewCell {
|
||||
|
||||
messageBottomLabel.frame = CGRect(origin: origin, size: attributes.messageBottomLabelSize)
|
||||
}
|
||||
|
||||
|
||||
/// Positions the cell's accessory view.
|
||||
/// - attributes: The `MessagesCollectionViewLayoutAttributes` for the cell.
|
||||
open func layoutAccessoryView(with attributes: MessagesCollectionViewLayoutAttributes) {
|
||||
guard attributes.accessoryViewSize != .zero else { return }
|
||||
|
||||
let y = (bounds.height - attributes.accessoryViewSize.height) / 2
|
||||
var origin = CGPoint(x: 0, y: y)
|
||||
|
||||
// Accessory view is always on the opposite side of avatar
|
||||
switch attributes.avatarPosition.horizontal {
|
||||
case .cellLeading:
|
||||
origin.x = messageContainerView.frame.maxX + attributes.accessoryViewPadding.left
|
||||
case .cellTrailing:
|
||||
origin.x = messageContainerView.frame.minX - attributes.accessoryViewPadding.right - attributes.accessoryViewSize.width
|
||||
case .natural:
|
||||
fatalError(MessageKitError.avatarPositionUnresolved)
|
||||
}
|
||||
|
||||
accessoryView.frame = CGRect(origin: origin, size: attributes.accessoryViewSize)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user