// MIT License // // Copyright (c) 2017-2022 MessageKit // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import Foundation import UIKit // MARK: - MessagesLayoutDelegate /// A protocol used by the `MessagesCollectionViewFlowLayout` object to determine /// the size and layout of a `MessageCollectionViewCell` and its contents. @MainActor public protocol MessagesLayoutDelegate: AnyObject { /// Specifies the size to use for a header view. /// /// - Parameters: /// - section: The section number of the header. /// - messagesCollectionView: The `MessagesCollectionView` in which this header will be displayed. /// /// - Note: /// The default value returned by this method is a size of `GGSize.zero`. func headerViewSize(for section: Int, in messagesCollectionView: MessagesCollectionView) -> CGSize /// Specifies the size to use for a footer view. /// /// - Parameters: /// - section: The section number of the footer. /// - messagesCollectionView: The `MessagesCollectionView` in which this footer will be displayed. /// /// - Note: /// The default value returned by this method is a size of `GGSize.zero`. func footerViewSize(for section: Int, in messagesCollectionView: MessagesCollectionView) -> CGSize /// Specifies the size to use for a typing indicator view. /// /// - Parameters: /// - layout: The `MessagesCollectionViewFlowLayout` layout. /// /// - Note: /// The default value returned by this method is the width of the `messagesCollectionView` minus insets and a height of 62. func typingIndicatorViewSize(for layout: MessagesCollectionViewFlowLayout) -> CGSize /// Specifies the top inset to use for a typing indicator view. /// /// - Parameters: /// - messagesCollectionView: The `MessagesCollectionView` in which this view will be displayed. /// /// - Note: /// The default value returned by this method is a top inset of 15. func typingIndicatorViewTopInset(in messagesCollectionView: MessagesCollectionView) -> CGFloat /// Specifies the height for the `MessageContentCell`'s top label. /// /// - Parameters: /// - message: The `MessageType` that will be displayed for this cell. /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default value returned by this method is zero. func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat /// Specifies the height for the `MessageContentCell`'s bottom label. /// /// - Parameters: /// - message: The `MessageType` that will be displayed for this cell. /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default value returned by this method is zero. func cellBottomLabelHeight( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat /// Specifies the height for the message bubble's top label. /// /// - Parameters: /// - message: The `MessageType` that will be displayed for this cell. /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default value returned by this method is zero. func messageTopLabelHeight( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat /// Specifies the label alignment for the message bubble's top label. /// - Parameters: /// - message: The `MessageType` that will be displayed for this cell. /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// - Returns: Optional LabelAlignment for the message bubble's top label. If nil is returned or the delegate method is not implemented, /// alignment from MessageSizeCalculator will be used depending if the message is outgoing or incoming func messageTopLabelAlignment( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> LabelAlignment? /// Specifies the height for the `MessageContentCell`'s bottom label. /// /// - Parameters: /// - message: The `MessageType` that will be displayed for this cell. /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default value returned by this method is zero. func messageBottomLabelHeight( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat /// Specifies the label alignment for the message bubble's bottom label. /// - Parameters: /// - message: The `MessageType` that will be displayed for this cell. /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// - Returns: Optional LabelAlignment for the message bubble's bottom label. If nil is returned or the delegate method is not implemented, /// alignment from MessageSizeCalculator will be used depending if the message is outgoing or incoming func messageBottomLabelAlignment( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> LabelAlignment? /// Specifies the size for the `MessageContentCell`'s avatar image view. /// - Parameters: /// - message: The `MessageType` that will be displayed for this cell. /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// - Returns: Optional CGSize for the avatar image view. If nil is returned or delegate method is not implemented, /// size from `MessageSizeCalculator`'s `incomingAvatarSize` or `outgoingAvatarSize` will be used depending if the message is outgoing or incoming func avatarSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize? /// Text cell size calculator for messages with MessageType.text. /// /// - Parameters: /// - message: The text message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will return nil. You must override this method if you are using your own cell for messages with MessageType.text. func textCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator? /// Attributed text cell size calculator for messages with MessageType.attributedText. /// /// - Parameters: /// - message: The attributedText message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will return nil. You must override this method if you are using your own cell for messages with MessageType.attributedText. func attributedTextCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator? /// Emoji cell size calculator for messages with MessageType.emoji. /// /// - Parameters: /// - message: The emoji message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will return nil. You must override this method if you are using your own cell for messages with MessageType.emoji. func emojiCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator? /// Photo cell size calculator for messages with MessageType.photo. /// /// - Parameters: /// - message: The photo message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will return nil. You must override this method if you are using your own cell for messages with MessageType.text. func photoCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator? /// Video cell size calculator for messages with MessageType.video. /// /// - Parameters: /// - message: The video message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will return nil. You must override this method if you are using your own cell for messages with MessageType.video. func videoCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator? /// Location cell size calculator for messages with MessageType.location. /// /// - Parameters: /// - message: The location message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will return nil. You must override this method if you are using your own cell for messages with MessageType.location. func locationCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator? /// Audio cell size calculator for messages with MessageType.audio. /// /// - Parameters: /// - message: The audio message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will return nil. You must override this method if you are using your own cell for messages with MessageType.audio. func audioCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator? /// Contact cell size calculator for messages with MessageType.contact. /// /// - Parameters: /// - message: The contact message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will return nil. You must override this method if you are using your own cell for messages with MessageType.contact. func contactCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator? /// Custom cell size calculator for messages with MessageType.custom. /// /// - Parameters: /// - message: The custom message /// - indexPath: The `IndexPath` of the cell. /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: /// The default implementation will throw fatalError(). You must override this method if you are using messages with MessageType.custom. func customCellSizeCalculator( for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator } extension MessagesLayoutDelegate { public func headerViewSize(for _: Int, in _: MessagesCollectionView) -> CGSize { .zero } public func footerViewSize(for _: Int, in _: MessagesCollectionView) -> CGSize { .zero } public func typingIndicatorViewSize(for layout: MessagesCollectionViewFlowLayout) -> CGSize { let collectionViewWidth = layout.messagesCollectionView.bounds.width let contentInset = layout.messagesCollectionView.contentInset let inset = layout.sectionInset.horizontal + contentInset.horizontal return CGSize(width: collectionViewWidth - inset, height: 62) } public func typingIndicatorViewTopInset(in _: MessagesCollectionView) -> CGFloat { 15 } public func cellTopLabelHeight(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGFloat { 0 } public func cellBottomLabelHeight(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGFloat { 0 } public func messageTopLabelHeight(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGFloat { 0 } public func messageTopLabelAlignment(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> LabelAlignment? { nil } public func messageBottomLabelHeight(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGFloat { 0 } public func avatarSize(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CGSize? { nil } public func messageBottomLabelAlignment( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> LabelAlignment? { nil } public func textCellSizeCalculator(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator? { nil } public func attributedTextCellSizeCalculator( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator? { nil } public func emojiCellSizeCalculator( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator? { nil } public func photoCellSizeCalculator( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator? { nil } public func videoCellSizeCalculator( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator? { nil } public func locationCellSizeCalculator( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator? { nil } public func audioCellSizeCalculator( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator? { nil } public func contactCellSizeCalculator( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator? { nil } public func customCellSizeCalculator( for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> CellSizeCalculator { fatalError("Must return a CellSizeCalculator for MessageKind.custom(Any?)") } }