Merge pull request #1444 from amirpirzad/development

Display message time by swiping left
This commit is contained in:
Jakub Kašpar
2020-09-03 13:34:48 +02:00
committed by GitHub
10 changed files with 152 additions and 5 deletions
+1
View File
@@ -8,6 +8,7 @@ The changelog for `MessageKit`. Also see the [releases](https://github.com/Messa
- Fixes missing insets for link preview messages [#1447](https://github.com/MessageKit/MessageKit/pull/1447) by [@bguidolim](https://github.com/bguidolim)
### Added
- Show message time by swiping left over the chat controller. [#1444](https://github.com/MessageKit/MessageKit/pull/1444) by [@amirpirzad](https://github.com/amirpirzad)
### Changed
@@ -278,7 +278,6 @@ final class AdvancedExampleViewController: ChatViewController {
}
return nil
}
}
// MARK: - MessagesDisplayDelegate
@@ -112,6 +112,8 @@ class ChatViewController: MessagesViewController, MessagesDataSource {
scrollsToBottomOnKeyboardBeginsEditing = true // default false
maintainPositionOnKeyboardFrameChanged = true // default false
showMessageTimestampOnSwipeLeft = true // default false
messagesCollectionView.refreshControl = refreshControl
}
@@ -34,6 +34,7 @@ struct MessagesView: UIViewControllerRepresentable {
messagesVC.messageInputBar.delegate = context.coordinator
messagesVC.scrollsToBottomOnKeyboardBeginsEditing = true // default false
messagesVC.maintainPositionOnKeyboardFrameChanged = true // default false
messagesVC.showMessageTimestampOnSwipeLeft = true // default false
return messagesVC
}
@@ -95,6 +96,14 @@ extension MessagesView.Coordinator: MessagesDataSource {
let dateString = formatter.string(from: message.sentDate)
return NSAttributedString(string: dateString, attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption2)])
}
func messageTimestampLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
let sentDate = message.sentDate
let sentDateString = MessageKitDateFormatter.shared.string(from: sentDate)
let timeLabelFont: UIFont = .boldSystemFont(ofSize: 10)
let timeLabelColor: UIColor = .systemGray
return NSAttributedString(string: sentDateString, attributes: [NSAttributedString.Key.font: timeLabelFont, NSAttributedString.Key.foregroundColor: timeLabelColor])
}
}
@available(iOS 13.0, *)
@@ -28,7 +28,7 @@ import InputBarAccessoryView
/// A subclass of `UIViewController` with a `MessagesCollectionView` object
/// that is used to display conversation interfaces.
open class MessagesViewController: UIViewController,
UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UIGestureRecognizerDelegate {
/// The `MessagesCollectionView` managed by the messages view controller object.
open var messagesCollectionView = MessagesCollectionView()
@@ -56,6 +56,22 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
/// The default value of this property is `false`.
open var maintainPositionOnKeyboardFrameChanged: Bool = false
/// Display the date of message by swiping left.
/// The default value of this property is `false`.
open var showMessageTimestampOnSwipeLeft: Bool = false {
didSet {
messagesCollectionView.showMessageTimestampOnSwipeLeft = showMessageTimestampOnSwipeLeft
if showMessageTimestampOnSwipeLeft {
addPanGesture()
} else {
removePanGesture()
}
}
}
/// Pan gesture for display the date of message by swiping left.
private var panGesture: UIPanGestureRecognizer?
open override var canBecomeFirstResponder: Bool {
return true
}
@@ -148,6 +164,53 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
// MARK: - Methods [Private]
/// Display time of message by swiping the cell
private func addPanGesture() {
panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
guard let panGesture = panGesture else {
return
}
panGesture.delegate = self
messagesCollectionView.addGestureRecognizer(panGesture)
messagesCollectionView.clipsToBounds = false
}
private func removePanGesture() {
guard let panGesture = panGesture else {
return
}
panGesture.delegate = nil
self.panGesture = nil
messagesCollectionView.removeGestureRecognizer(panGesture)
messagesCollectionView.clipsToBounds = true
}
@objc
private func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
guard let parentView = gesture.view else {
return
}
switch gesture.state {
case .began, .changed:
messagesCollectionView.showsVerticalScrollIndicator = false
let translation = gesture.translation(in: view)
let minX = -(view.frame.size.width * 0.35)
let maxX: CGFloat = 0
var offsetValue = translation.x
offsetValue = max(offsetValue, minX)
offsetValue = min(offsetValue, maxX)
parentView.frame.origin.x = offsetValue
case .ended:
messagesCollectionView.showsVerticalScrollIndicator = true
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.8, options: .curveEaseOut, animations: {
parentView.frame.origin.x = 0
}, completion: nil)
default:
break
}
}
private func setupDefaults() {
extendedLayoutIncludesOpaqueBars = true
view.backgroundColor = .collectionViewBackground
@@ -420,4 +483,15 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
@objc private func clearMemoryCache() {
MessageStyle.bubbleImageCache.removeAllObjects()
}
// MARK: - UIGestureRecognizerDelegate
/// check pan gesture direction
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer else {
return false
}
let velocity = panGesture.velocity(in: messagesCollectionView)
return abs(velocity.x) > abs(velocity.y)
}
}
+13 -1
View File
@@ -81,6 +81,7 @@ open class MessageSizeCalculator: CellSizeCalculator {
attributes.cellTopLabelSize = cellTopLabelSize(for: message, at: indexPath)
attributes.cellTopLabelAlignment = cellTopLabelAlignment(for: message)
attributes.cellBottomLabelSize = cellBottomLabelSize(for: message, at: indexPath)
attributes.messageTimeLabelSize = messageTimeLabelSize(for: message, at: indexPath)
attributes.cellBottomLabelAlignment = cellBottomLabelAlignment(for: message)
attributes.messageTopLabelSize = messageTopLabelSize(for: message, at: indexPath)
attributes.messageTopLabelAlignment = messageTopLabelAlignment(for: message)
@@ -197,7 +198,18 @@ open class MessageSizeCalculator: CellSizeCalculator {
let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
return isFromCurrentSender ? outgoingMessageTopLabelAlignment : incomingMessageTopLabelAlignment
}
// MARK: - Message time label
open func messageTimeLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
let dataSource = messagesLayout.messagesDataSource
guard let attributedText = dataSource.messageTimestampLabelAttributedText(for: message, at: indexPath) else {
return .zero
}
let size = attributedText.size()
return CGSize(width: size.width, height: size.height)
}
// MARK: - Bottom cell Label
open func cellBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
@@ -50,6 +50,8 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
public var messageBottomLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
public var messageBottomLabelSize: CGSize = .zero
public var messageTimeLabelSize: CGSize = .zero
public var accessoryViewSize: CGSize = .zero
public var accessoryViewPadding: HorizontalEdgeInsets = .zero
public var accessoryViewPosition: AccessoryPosition = .messageCenter
@@ -74,6 +76,7 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
copy.cellTopLabelSize = cellTopLabelSize
copy.cellBottomLabelAlignment = cellBottomLabelAlignment
copy.cellBottomLabelSize = cellBottomLabelSize
copy.messageTimeLabelSize = messageTimeLabelSize
copy.messageTopLabelAlignment = messageTopLabelAlignment
copy.messageTopLabelSize = messageTopLabelSize
copy.messageBottomLabelAlignment = messageBottomLabelAlignment
@@ -100,6 +103,7 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
&& attributes.cellTopLabelSize == cellTopLabelSize
&& attributes.cellBottomLabelAlignment == cellBottomLabelAlignment
&& attributes.cellBottomLabelSize == cellBottomLabelSize
&& attributes.messageTimeLabelSize == messageTimeLabelSize
&& attributes.messageTopLabelAlignment == messageTopLabelAlignment
&& attributes.messageTopLabelSize == messageTopLabelSize
&& attributes.messageBottomLabelAlignment == messageBottomLabelAlignment
+26 -1
View File
@@ -102,6 +102,18 @@ public protocol MessagesDataSource: AnyObject {
///
/// The default value returned by this method is `nil`.
func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString?
/// The attributed text to be used for cell's timestamp label.
/// The timestamp label is shown when showMessageTimestampOnSwipeLeft is enabled by swiping left over the chat controller.
///
/// - Parameters:
/// - message: The `MessageType` that will be displayed by this cell.
/// - indexPath: The `IndexPath` of the cell.
/// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
///
/// The default value returned by this method is `nil`.
func messageTimestampLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString?
/// Custom collectionView cell for message with `custom` message type.
///
@@ -148,7 +160,20 @@ public extension MessagesDataSource {
func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
return nil
}
func messageTimestampLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
let sentDate = message.sentDate
let sentDateString = MessageKitDateFormatter.shared.string(from: sentDate)
let timeLabelFont: UIFont = .boldSystemFont(ofSize: 10)
let timeLabelColor: UIColor
if #available(iOS 13, *) {
timeLabelColor = .systemGray
} else {
timeLabelColor = .darkGray
}
return NSAttributedString(string: sentDateString, attributes: [NSAttributedString.Key.font: timeLabelFont, NSAttributedString.Key.foregroundColor: timeLabelColor])
}
func customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell {
fatalError(MessageKitError.customDataUnresolvedCell)
}
+18 -1
View File
@@ -69,6 +69,9 @@ open class MessageContentCell: MessageCollectionViewCell {
return label
}()
/// The time label of the messageBubble.
open var messageTimestampLabel: InsetLabel = InsetLabel()
// Should only add customized subviews - don't change accessoryView itself.
open var accessoryView: UIView = UIView()
@@ -95,6 +98,7 @@ open class MessageContentCell: MessageCollectionViewCell {
contentView.addSubview(cellBottomLabel)
contentView.addSubview(messageContainerView)
contentView.addSubview(avatarView)
contentView.addSubview(messageTimestampLabel)
}
open override func prepareForReuse() {
@@ -103,6 +107,7 @@ open class MessageContentCell: MessageCollectionViewCell {
cellBottomLabel.text = nil
messageTopLabel.text = nil
messageBottomLabel.text = nil
messageTimestampLabel.attributedText = nil
}
// MARK: - Configuration
@@ -118,6 +123,7 @@ open class MessageContentCell: MessageCollectionViewCell {
layoutMessageTopLabel(with: attributes)
layoutAvatarView(with: attributes)
layoutAccessoryView(with: attributes)
layoutTimeLabelView(with: attributes)
}
/// Used to configure the cell.
@@ -150,11 +156,13 @@ open class MessageContentCell: MessageCollectionViewCell {
let bottomCellLabelText = dataSource.cellBottomLabelAttributedText(for: message, at: indexPath)
let topMessageLabelText = dataSource.messageTopLabelAttributedText(for: message, at: indexPath)
let bottomMessageLabelText = dataSource.messageBottomLabelAttributedText(for: message, at: indexPath)
let messageTimestampLabelText = dataSource.messageTimestampLabelAttributedText(for: message, at: indexPath)
cellTopLabel.attributedText = topCellLabelText
cellBottomLabel.attributedText = bottomCellLabelText
messageTopLabel.attributedText = topMessageLabelText
messageBottomLabel.attributedText = bottomMessageLabelText
messageTimestampLabel.attributedText = messageTimestampLabelText
messageTimestampLabel.isHidden = !messagesCollectionView.showMessageTimestampOnSwipeLeft
}
/// Handle tap gesture on contentView and its subviews.
@@ -344,4 +352,13 @@ open class MessageContentCell: MessageCollectionViewCell {
accessoryView.frame = CGRect(origin: origin, size: attributes.accessoryViewSize)
}
/// Positions the message bubble's time label.
/// - attributes: The `MessagesCollectionViewLayoutAttributes` for the cell.
open func layoutTimeLabelView(with attributes: MessagesCollectionViewLayoutAttributes) {
let paddingLeft: CGFloat = 10
let origin = CGPoint(x: contentView.frame.size.width + paddingLeft, y: contentView.frame.size.height * 0.5)
let size = CGSize(width: attributes.messageTimeLabelSize.width, height: attributes.messageTimeLabelSize.height)
messageTimestampLabel.frame = CGRect(origin: origin, size: size)
}
}
@@ -41,6 +41,10 @@ open class MessagesCollectionView: UICollectionView {
return messagesCollectionViewFlowLayout.isTypingIndicatorViewHidden
}
/// Display the date of message by swiping left.
/// The default value of this property is `false`.
internal var showMessageTimestampOnSwipeLeft: Bool = false
private var indexPathForLastItem: IndexPath? {
let lastSection = numberOfSections - 1
guard lastSection >= 0, numberOfItems(inSection: lastSection) > 0 else { return nil }