TextMediaMessageCell.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import UIKit
  2. // A subclass of `MessageContentCell` used to display mixed media messages.
  3. open class TextMediaMessageCell: MessageContentCell {
  4. public static let insetTop: CGFloat = 12
  5. public static let insetBottom: CGFloat = 12
  6. public static let insetHorizontalBig: CGFloat = 23
  7. public static let insetHorizontalSmall: CGFloat = 12
  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. open lazy var fileView: UIImageView = {
  30. let fileView = UIImageView(image: UIImage(named: "ic_attach_file_36pt"))
  31. fileView.translatesAutoresizingMaskIntoConstraints = false
  32. return fileView
  33. }()
  34. // MARK: - Methods
  35. open override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
  36. super.apply(layoutAttributes)
  37. if let attributes = layoutAttributes as? MessagesCollectionViewLayoutAttributes {
  38. messageLabel.textInsets = attributes.messageLabelInsets
  39. messageLabel.messageLabelFont = attributes.messageLabelFont
  40. }
  41. }
  42. func getMessageLabelHeight() -> CGFloat {
  43. if let text = messageLabel.attributedText {
  44. let height = (text.height(withConstrainedWidth:
  45. messageContainerView.frame.width -
  46. TextMediaMessageCell.insetHorizontalSmall -
  47. TextMediaMessageCell.insetHorizontalBig))
  48. return height + TextMediaMessageCell.insetBottom + TextMediaMessageCell.insetTop
  49. }
  50. return 0
  51. }
  52. /// Responsible for setting up the constraints of the cell's subviews.
  53. open func setupConstraints(for messageKind: MessageKind) {
  54. messageContainerView.removeConstraints(messageContainerView.constraints)
  55. let imageViewHeight = messageContainerView.frame.height - getMessageLabelHeight()
  56. let imageViewConstraints = [imageView.constraintCenterXTo(messageContainerView),
  57. imageView.constraintAlignLeadingTo(messageContainerView),
  58. imageView.constraintAlignTrailingTo(messageContainerView),
  59. imageView.constraintAlignTopTo(messageContainerView),
  60. imageView.heightAnchor.constraint(equalToConstant: imageViewHeight)
  61. ]
  62. messageContainerView.addConstraints(imageViewConstraints)
  63. messageLabel.frame = CGRect(x: 0,
  64. y: messageContainerView.frame.height - getMessageLabelHeight(),
  65. width: messageContainerView.frame.width,
  66. height: getMessageLabelHeight())
  67. switch messageKind {
  68. case .videoText:
  69. playButtonView.constraint(equalTo: CGSize(width: 35, height: 35))
  70. let playButtonViewConstraints = [ playButtonView.constraintCenterXTo(imageView),
  71. playButtonView.constraintCenterYTo(imageView)]
  72. messageContainerView.addConstraints(playButtonViewConstraints)
  73. case .fileText:
  74. fileView.constraint(equalTo: CGSize(width: 35, height: 35))
  75. let fileViewConstraints = [ fileView.constraintCenterXTo(imageView),
  76. fileView.constraintCenterYTo(imageView)]
  77. messageContainerView.addConstraints(fileViewConstraints)
  78. default:
  79. break
  80. }
  81. }
  82. open override func prepareForReuse() {
  83. super.prepareForReuse()
  84. self.imageView.image = nil
  85. self.messageLabel.attributedText = nil
  86. self.messageLabel.text = nil
  87. }
  88. open override func setupSubviews() {
  89. super.setupSubviews()
  90. messageContainerView.addSubview(imageView)
  91. messageContainerView.addSubview(playButtonView)
  92. messageContainerView.addSubview(fileView)
  93. messageContainerView.addSubview(messageLabel)
  94. }
  95. open override func configure(with message: MessageType, at indexPath: IndexPath, and messagesCollectionView: MessagesCollectionView) {
  96. super.configure(with: message, at: indexPath, and: messagesCollectionView)
  97. guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
  98. fatalError(MessageKitError.nilMessagesDisplayDelegate)
  99. }
  100. switch message.kind {
  101. case .photoText(let mediaItem), .videoText(let mediaItem), .fileText(let mediaItem):
  102. configureImageView(for: mediaItem)
  103. configureMessageLabel(for: mediaItem,
  104. with: displayDelegate,
  105. message: message,
  106. at: indexPath,
  107. in: messagesCollectionView)
  108. default:
  109. fatalError("Unexpected message kind in TextMediaMessageCell")
  110. }
  111. configurePlayButtonView(for: message.kind)
  112. configureFileView(for: message.kind)
  113. setupConstraints(for: message.kind)
  114. displayDelegate.configureMediaMessageImageView(imageView, for: message, at: indexPath, in: messagesCollectionView)
  115. }
  116. func configurePlayButtonView(for messageKind: MessageKind) {
  117. switch messageKind {
  118. case .videoText:
  119. playButtonView.isHidden = false
  120. default:
  121. playButtonView.isHidden = true
  122. }
  123. }
  124. func configureFileView(for messageKind: MessageKind) {
  125. switch messageKind {
  126. case .fileText:
  127. fileView.isHidden = false
  128. default:
  129. fileView.isHidden = true
  130. }
  131. }
  132. func configureImageView(for mediaItem: MediaItem) {
  133. imageView.image = mediaItem.image ?? mediaItem.placeholderImage
  134. }
  135. func configureMessageLabel(for mediaItem: MediaItem,
  136. with displayDelegate: MessagesDisplayDelegate,
  137. message: MessageType,
  138. at indexPath: IndexPath,
  139. in messagesCollectionView: MessagesCollectionView) {
  140. let enabledDetectors = displayDelegate.enabledDetectors(for: message, at: indexPath, in: messagesCollectionView)
  141. messageLabel.configure {
  142. messageLabel.enabledDetectors = enabledDetectors
  143. for detector in enabledDetectors {
  144. let attributes = displayDelegate.detectorAttributes(for: detector, and: message, at: indexPath)
  145. messageLabel.setAttributes(attributes, detector: detector)
  146. }
  147. messageLabel.attributedText = mediaItem.text
  148. }
  149. }
  150. /// Used to handle the cell's contentView's tap gesture.
  151. /// Return false when the contentView does not need to handle the gesture.
  152. open override func cellContentView(canHandle touchPoint: CGPoint) -> Bool {
  153. return messageLabel.handleGesture(touchPoint)
  154. }
  155. }