TextMediaMessageCell.swift 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import UIKit
  2. // A subclass of `MessageContentCell` used to display mixed media messages.
  3. open class TextMediaMessageCell: MessageContentCell {
  4. static let insetTop: CGFloat = 12
  5. static let insetBottom: CGFloat = 12
  6. static let insetHorizontalBig: CGFloat = 18
  7. static let insetHorizontalSmall: CGFloat = 14
  8. // MARK: - Properties
  9. /// The `MessageCellDelegate` for the cell.
  10. open override weak var delegate: MessageCellDelegate? {
  11. didSet {
  12. messageLabel.delegate = delegate
  13. }
  14. }
  15. /// The label used to display the message's text.
  16. open var messageLabel = MessageLabel()
  17. /// The image view display the media content.
  18. open var imageView: UIImageView = {
  19. let imageView = UIImageView()
  20. imageView.contentMode = .scaleAspectFill
  21. imageView.translatesAutoresizingMaskIntoConstraints = false
  22. return imageView
  23. }()
  24. /// The play button view to display on video messages.
  25. open lazy var playButtonView: PlayButtonView = {
  26. let playButtonView = PlayButtonView()
  27. return playButtonView
  28. }()
  29. // MARK: - Methods
  30. open override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
  31. super.apply(layoutAttributes)
  32. if let attributes = layoutAttributes as? MessagesCollectionViewLayoutAttributes {
  33. messageLabel.textInsets = attributes.messageLabelInsets
  34. messageLabel.messageLabelFont = attributes.messageLabelFont
  35. }
  36. }
  37. private func getMessageLabelHeight() -> CGFloat {
  38. if let text = messageLabel.attributedText {
  39. let height = (text.height(withConstrainedWidth:
  40. messageContainerView.frame.width -
  41. TextMediaMessageCell.insetHorizontalSmall -
  42. TextMediaMessageCell.insetHorizontalBig))
  43. return height + TextMediaMessageCell.insetBottom + TextMediaMessageCell.insetTop
  44. }
  45. return 0
  46. }
  47. /// Responsible for setting up the constraints of the cell's subviews.
  48. open func setupConstraints(for messageKind: MessageKind) {
  49. messageContainerView.removeConstraints(messageContainerView.constraints)
  50. let imageViewHeight = messageContainerView.frame.height - getMessageLabelHeight()
  51. let imageViewConstraints = [imageView.constraintCenterXTo(messageContainerView),
  52. imageView.constraintAlignLeadingTo(messageContainerView),
  53. imageView.constraintAlignTrailingTo(messageContainerView),
  54. imageView.constraintAlignTopTo(messageContainerView),
  55. imageView.heightAnchor.constraint(equalToConstant: imageViewHeight)
  56. ]
  57. messageContainerView.addConstraints(imageViewConstraints)
  58. messageLabel.frame = CGRect(x: 0,
  59. y: messageContainerView.frame.height - getMessageLabelHeight(),
  60. width: messageContainerView.frame.width,
  61. height: getMessageLabelHeight())
  62. switch messageKind {
  63. case .videoText:
  64. playButtonView.constraint(equalTo: CGSize(width: 50, height: 50))
  65. let playButtonViewConstraints = [ playButtonView.constraintCenterXTo(imageView),
  66. playButtonView.constraintCenterYTo(imageView)]
  67. messageContainerView.addConstraints(playButtonViewConstraints)
  68. default:
  69. break
  70. }
  71. }
  72. open override func prepareForReuse() {
  73. super.prepareForReuse()
  74. self.imageView.image = nil
  75. self.messageLabel.attributedText = nil
  76. self.messageLabel.text = nil
  77. }
  78. open override func setupSubviews() {
  79. super.setupSubviews()
  80. messageContainerView.addSubview(imageView)
  81. messageContainerView.addSubview(playButtonView)
  82. messageContainerView.addSubview(messageLabel)
  83. }
  84. open override func configure(with message: MessageType, at indexPath: IndexPath, and messagesCollectionView: MessagesCollectionView) {
  85. super.configure(with: message, at: indexPath, and: messagesCollectionView)
  86. guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
  87. fatalError(MessageKitError.nilMessagesDisplayDelegate)
  88. }
  89. switch message.kind {
  90. case .photoText(let mediaItem), .videoText(let mediaItem), .fileText(let mediaItem):
  91. configureImageView(for: mediaItem)
  92. configureMessageLabel(for: mediaItem,
  93. with: displayDelegate,
  94. message: message,
  95. at: indexPath,
  96. in: messagesCollectionView)
  97. default:
  98. fatalError("Unexpected message kind in TextMediaMessageCell")
  99. }
  100. configurePlayButtonView(for: message.kind)
  101. setupConstraints(for: message.kind)
  102. displayDelegate.configureMediaMessageImageView(imageView, for: message, at: indexPath, in: messagesCollectionView)
  103. }
  104. private func configurePlayButtonView(for messageKind: MessageKind) {
  105. switch messageKind {
  106. case .videoText:
  107. playButtonView.isHidden = false
  108. default:
  109. playButtonView.isHidden = true
  110. }
  111. }
  112. private func configureImageView(for mediaItem: MediaItem) {
  113. imageView.image = mediaItem.image ?? mediaItem.placeholderImage
  114. }
  115. private func configureMessageLabel(for mediaItem: MediaItem,
  116. with displayDelegate: MessagesDisplayDelegate,
  117. message: MessageType,
  118. at indexPath: IndexPath,
  119. in messagesCollectionView: MessagesCollectionView) {
  120. let enabledDetectors = displayDelegate.enabledDetectors(for: message, at: indexPath, in: messagesCollectionView)
  121. messageLabel.configure {
  122. messageLabel.enabledDetectors = enabledDetectors
  123. for detector in enabledDetectors {
  124. let attributes = displayDelegate.detectorAttributes(for: detector, and: message, at: indexPath)
  125. messageLabel.setAttributes(attributes, detector: detector)
  126. }
  127. messageLabel.attributedText = mediaItem.text?[MediaItemConstants.messageText]
  128. }
  129. }
  130. /// Used to handle the cell's contentView's tap gesture.
  131. /// Return false when the contentView does not need to handle the gesture.
  132. open override func cellContentView(canHandle touchPoint: CGPoint) -> Bool {
  133. if !imageView.isHidden {
  134. let touchPointWithoutImageHeight = CGPoint(x: touchPoint.x, y: touchPoint.y - imageView.frame.height)
  135. return messageLabel.handleGesture(touchPointWithoutImageHeight)
  136. }
  137. return messageLabel.handleGesture(touchPoint)
  138. }
  139. }