mirror of
https://github.com/MessageKit/MessageKit.git
synced 2026-02-06 19:03:19 +00:00
bff35fda61
* build: Swiftlint plugin * build: Swiftformat plugin * build: Swiftformat plugin * build: Swiftformat bash command * style: Swiftformat rules * style: Swiftformat applied to codebase * style: Ignore Tests for Swiftlint * Update bundler * Update changelog and migration guide * style: Ignore Example for Swiftlint * chore: Changelog * Update Xcode version for ci_pr_tests.yml * Update ci_pr_framework.yml * Update ci_pr_example.yml * chore: Changelog Co-authored-by: Jakub Kaspar <kaspikk@gmail.com>
530 lines
20 KiB
Swift
530 lines
20 KiB
Swift
// MIT License
|
|
//
|
|
// Copyright (c) 2017-2020 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 InputBarAccessoryView
|
|
import Kingfisher
|
|
import MapKit
|
|
import MessageKit
|
|
import UIKit
|
|
|
|
// MARK: - AdvancedExampleViewController
|
|
|
|
final class AdvancedExampleViewController: ChatViewController {
|
|
// MARK: Public
|
|
|
|
// MARK: - UICollectionViewDataSource
|
|
|
|
public override func collectionView(
|
|
_ collectionView: UICollectionView,
|
|
cellForItemAt indexPath: IndexPath)
|
|
-> UICollectionViewCell
|
|
{
|
|
guard let messagesDataSource = messagesCollectionView.messagesDataSource else {
|
|
fatalError("Ouch. nil data source for messages")
|
|
}
|
|
|
|
// Very important to check this when overriding `cellForItemAt`
|
|
// Super method will handle returning the typing indicator cell
|
|
guard !isSectionReservedForTypingIndicator(indexPath.section) else {
|
|
return super.collectionView(collectionView, cellForItemAt: indexPath)
|
|
}
|
|
|
|
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
|
|
if case .custom = message.kind {
|
|
let cell = messagesCollectionView.dequeueReusableCell(CustomCell.self, for: indexPath)
|
|
cell.configure(with: message, at: indexPath, and: messagesCollectionView)
|
|
return cell
|
|
}
|
|
return super.collectionView(collectionView, cellForItemAt: indexPath)
|
|
}
|
|
|
|
// MARK: Internal
|
|
|
|
let outgoingAvatarOverlap: CGFloat = 17.5
|
|
|
|
override func viewDidLoad() {
|
|
messagesCollectionView = MessagesCollectionView(frame: .zero, collectionViewLayout: CustomMessagesFlowLayout())
|
|
messagesCollectionView.register(CustomCell.self)
|
|
super.viewDidLoad()
|
|
|
|
updateTitleView(title: "MessageKit", subtitle: "2 Online")
|
|
}
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
|
|
MockSocket.shared.connect(with: [SampleData.shared.nathan, SampleData.shared.wu])
|
|
.onTypingStatus { [weak self] in
|
|
self?.setTypingIndicatorViewHidden(false)
|
|
}.onNewMessage { [weak self] message in
|
|
self?.setTypingIndicatorViewHidden(true, performUpdates: {
|
|
self?.insertMessage(message)
|
|
})
|
|
}
|
|
}
|
|
|
|
override func loadFirstMessages() {
|
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
let count = UserDefaults.standard.mockMessagesCount()
|
|
SampleData.shared.getAdvancedMessages(count: count) { messages in
|
|
DispatchQueue.main.async {
|
|
self.messageList = messages
|
|
self.messagesCollectionView.reloadData()
|
|
self.messagesCollectionView.scrollToLastItem()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override func loadMoreMessages() {
|
|
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 1) {
|
|
SampleData.shared.getAdvancedMessages(count: 20) { messages in
|
|
DispatchQueue.main.async {
|
|
self.messageList.insert(contentsOf: messages, at: 0)
|
|
self.messagesCollectionView.reloadDataAndKeepOffset()
|
|
self.refreshControl.endRefreshing()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override func configureMessageCollectionView() {
|
|
super.configureMessageCollectionView()
|
|
|
|
let layout = messagesCollectionView.collectionViewLayout as? MessagesCollectionViewFlowLayout
|
|
layout?.sectionInset = UIEdgeInsets(top: 1, left: 8, bottom: 1, right: 8)
|
|
|
|
// Hide the outgoing avatar and adjust the label alignment to line up with the messages
|
|
layout?.setMessageOutgoingAvatarSize(.zero)
|
|
layout?
|
|
.setMessageOutgoingMessageTopLabelAlignment(LabelAlignment(
|
|
textAlignment: .right,
|
|
textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)))
|
|
layout?
|
|
.setMessageOutgoingMessageBottomLabelAlignment(LabelAlignment(
|
|
textAlignment: .right,
|
|
textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)))
|
|
|
|
// Set outgoing avatar to overlap with the message bubble
|
|
layout?
|
|
.setMessageIncomingMessageTopLabelAlignment(LabelAlignment(
|
|
textAlignment: .left,
|
|
textInsets: UIEdgeInsets(top: 0, left: 18, bottom: outgoingAvatarOverlap, right: 0)))
|
|
layout?.setMessageIncomingAvatarSize(CGSize(width: 30, height: 30))
|
|
layout?
|
|
.setMessageIncomingMessagePadding(UIEdgeInsets(
|
|
top: -outgoingAvatarOverlap,
|
|
left: -18,
|
|
bottom: outgoingAvatarOverlap,
|
|
right: 18))
|
|
|
|
layout?.setMessageIncomingAccessoryViewSize(CGSize(width: 30, height: 30))
|
|
layout?.setMessageIncomingAccessoryViewPadding(HorizontalEdgeInsets(left: 8, right: 0))
|
|
layout?.setMessageIncomingAccessoryViewPosition(.messageBottom)
|
|
layout?.setMessageOutgoingAccessoryViewSize(CGSize(width: 30, height: 30))
|
|
layout?.setMessageOutgoingAccessoryViewPadding(HorizontalEdgeInsets(left: 0, right: 8))
|
|
|
|
messagesCollectionView.messagesLayoutDelegate = self
|
|
messagesCollectionView.messagesDisplayDelegate = self
|
|
}
|
|
|
|
override func configureMessageInputBar() {
|
|
// super.configureMessageInputBar()
|
|
|
|
messageInputBar = CameraInputBarAccessoryView()
|
|
messageInputBar.delegate = self
|
|
messageInputBar.inputTextView.tintColor = .primaryColor
|
|
messageInputBar.sendButton.setTitleColor(.primaryColor, for: .normal)
|
|
messageInputBar.sendButton.setTitleColor(
|
|
UIColor.primaryColor.withAlphaComponent(0.3),
|
|
for: .highlighted)
|
|
|
|
messageInputBar.isTranslucent = true
|
|
messageInputBar.separatorLine.isHidden = true
|
|
messageInputBar.inputTextView.tintColor = .primaryColor
|
|
messageInputBar.inputTextView.backgroundColor = UIColor(red: 245 / 255, green: 245 / 255, blue: 245 / 255, alpha: 1)
|
|
messageInputBar.inputTextView.placeholderTextColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
|
|
messageInputBar.inputTextView.textContainerInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 36)
|
|
messageInputBar.inputTextView.placeholderLabelInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 36)
|
|
messageInputBar.inputTextView.layer.borderColor = UIColor(red: 200 / 255, green: 200 / 255, blue: 200 / 255, alpha: 1).cgColor
|
|
messageInputBar.inputTextView.layer.borderWidth = 1.0
|
|
messageInputBar.inputTextView.layer.cornerRadius = 16.0
|
|
messageInputBar.inputTextView.layer.masksToBounds = true
|
|
messageInputBar.inputTextView.scrollIndicatorInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
|
|
configureInputBarItems()
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
func isTimeLabelVisible(at indexPath: IndexPath) -> Bool {
|
|
indexPath.section % 3 == 0 && !isPreviousMessageSameSender(at: indexPath)
|
|
}
|
|
|
|
func isPreviousMessageSameSender(at indexPath: IndexPath) -> Bool {
|
|
guard indexPath.section - 1 >= 0 else { return false }
|
|
return messageList[indexPath.section].user == messageList[indexPath.section - 1].user
|
|
}
|
|
|
|
func isNextMessageSameSender(at indexPath: IndexPath) -> Bool {
|
|
guard indexPath.section + 1 < messageList.count else { return false }
|
|
return messageList[indexPath.section].user == messageList[indexPath.section + 1].user
|
|
}
|
|
|
|
func setTypingIndicatorViewHidden(_ isHidden: Bool, performUpdates updates: (() -> Void)? = nil) {
|
|
updateTitleView(title: "MessageKit", subtitle: isHidden ? "2 Online" : "Typing...")
|
|
setTypingIndicatorViewHidden(isHidden, animated: true, whilePerforming: updates) { [weak self] success in
|
|
if success, self?.isLastSectionVisible() == true {
|
|
self?.messagesCollectionView.scrollToLastItem(animated: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - MessagesDataSource
|
|
|
|
override func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
if isTimeLabelVisible(at: indexPath) {
|
|
return NSAttributedString(
|
|
string: MessageKitDateFormatter.shared.string(from: message.sentDate),
|
|
attributes: [
|
|
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10),
|
|
NSAttributedString.Key.foregroundColor: UIColor.darkGray,
|
|
])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
override func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
if !isPreviousMessageSameSender(at: indexPath) {
|
|
let name = message.sender.displayName
|
|
return NSAttributedString(
|
|
string: name,
|
|
attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
override func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
if !isNextMessageSameSender(at: indexPath), isFromCurrentSender(message: message) {
|
|
return NSAttributedString(
|
|
string: "Delivered",
|
|
attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MARK: Private
|
|
|
|
private func configureInputBarItems() {
|
|
messageInputBar.setRightStackViewWidthConstant(to: 36, animated: false)
|
|
messageInputBar.sendButton.imageView?.backgroundColor = UIColor(white: 0.85, alpha: 1)
|
|
messageInputBar.sendButton.contentEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
|
|
messageInputBar.sendButton.setSize(CGSize(width: 36, height: 36), animated: false)
|
|
messageInputBar.sendButton.image = #imageLiteral(resourceName: "ic_up")
|
|
messageInputBar.sendButton.title = nil
|
|
messageInputBar.sendButton.imageView?.layer.cornerRadius = 16
|
|
let charCountButton = InputBarButtonItem()
|
|
.configure {
|
|
$0.title = "0/140"
|
|
$0.contentHorizontalAlignment = .right
|
|
$0.setTitleColor(UIColor(white: 0.6, alpha: 1), for: .normal)
|
|
$0.titleLabel?.font = UIFont.systemFont(ofSize: 10, weight: .bold)
|
|
$0.setSize(CGSize(width: 50, height: 25), animated: false)
|
|
}.onTextViewDidChange { item, textView in
|
|
item.title = "\(textView.text.count)/140"
|
|
let isOverLimit = textView.text.count > 140
|
|
item.inputBarAccessoryView?
|
|
.shouldManageSendButtonEnabledState = !isOverLimit // Disable automated management when over limit
|
|
if isOverLimit {
|
|
item.inputBarAccessoryView?.sendButton.isEnabled = false
|
|
}
|
|
let color = isOverLimit ? .red : UIColor(white: 0.6, alpha: 1)
|
|
item.setTitleColor(color, for: .normal)
|
|
}
|
|
let bottomItems = [.flexibleSpace, charCountButton]
|
|
|
|
configureInputBarPadding()
|
|
|
|
messageInputBar.setStackViewItems(bottomItems, forStack: .bottom, animated: false)
|
|
|
|
// This just adds some more flare
|
|
messageInputBar.sendButton
|
|
.onEnabled { item in
|
|
UIView.animate(withDuration: 0.3, animations: {
|
|
item.imageView?.backgroundColor = .primaryColor
|
|
})
|
|
}.onDisabled { item in
|
|
UIView.animate(withDuration: 0.3, animations: {
|
|
item.imageView?.backgroundColor = UIColor(white: 0.85, alpha: 1)
|
|
})
|
|
}
|
|
}
|
|
|
|
/// The input bar will autosize based on the contained text, but we can add padding to adjust the height or width if necessary
|
|
/// See the InputBar diagram here to visualize how each of these would take effect:
|
|
/// https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/InputBarAccessoryViewLayout.png
|
|
private func configureInputBarPadding() {
|
|
// Entire InputBar padding
|
|
messageInputBar.padding.bottom = 8
|
|
|
|
// or MiddleContentView padding
|
|
messageInputBar.middleContentViewPadding.right = -38
|
|
|
|
// or InputTextView padding
|
|
messageInputBar.inputTextView.textContainerInset.bottom = 8
|
|
}
|
|
|
|
private func makeButton(named: String) -> InputBarButtonItem {
|
|
InputBarButtonItem()
|
|
.configure {
|
|
$0.spacing = .fixed(10)
|
|
$0.image = UIImage(named: named)?.withRenderingMode(.alwaysTemplate)
|
|
$0.setSize(CGSize(width: 25, height: 25), animated: false)
|
|
$0.tintColor = UIColor(white: 0.8, alpha: 1)
|
|
}.onSelected {
|
|
$0.tintColor = .primaryColor
|
|
}.onDeselected {
|
|
$0.tintColor = UIColor(white: 0.8, alpha: 1)
|
|
}.onTouchUpInside {
|
|
print("Item Tapped")
|
|
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
|
let action = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
|
actionSheet.addAction(action)
|
|
if let popoverPresentationController = actionSheet.popoverPresentationController {
|
|
popoverPresentationController.sourceView = $0
|
|
popoverPresentationController.sourceRect = $0.frame
|
|
}
|
|
self.navigationController?.present(actionSheet, animated: true, completion: nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: MessagesDisplayDelegate
|
|
|
|
extension AdvancedExampleViewController: MessagesDisplayDelegate {
|
|
// MARK: - Text Messages
|
|
|
|
func textColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
|
|
isFromCurrentSender(message: message) ? .white : .darkText
|
|
}
|
|
|
|
func detectorAttributes(
|
|
for detector: DetectorType,
|
|
and message: MessageType,
|
|
at _: IndexPath) -> [NSAttributedString.Key: Any]
|
|
{
|
|
switch detector {
|
|
case .hashtag, .mention:
|
|
if isFromCurrentSender(message: message) {
|
|
return [.foregroundColor: UIColor.white]
|
|
} else {
|
|
return [.foregroundColor: UIColor.primaryColor]
|
|
}
|
|
default: return MessageLabel.defaultAttributes
|
|
}
|
|
}
|
|
|
|
func enabledDetectors(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> [DetectorType] {
|
|
[.url, .address, .phoneNumber, .date, .transitInformation, .mention, .hashtag]
|
|
}
|
|
|
|
// MARK: - All Messages
|
|
|
|
func backgroundColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
|
|
isFromCurrentSender(message: message) ? .primaryColor : UIColor(red: 230 / 255, green: 230 / 255, blue: 230 / 255, alpha: 1)
|
|
}
|
|
|
|
func messageStyle(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> MessageStyle {
|
|
var corners: UIRectCorner = []
|
|
|
|
if isFromCurrentSender(message: message) {
|
|
corners.formUnion(.topLeft)
|
|
corners.formUnion(.bottomLeft)
|
|
if !isPreviousMessageSameSender(at: indexPath) {
|
|
corners.formUnion(.topRight)
|
|
}
|
|
if !isNextMessageSameSender(at: indexPath) {
|
|
corners.formUnion(.bottomRight)
|
|
}
|
|
} else {
|
|
corners.formUnion(.topRight)
|
|
corners.formUnion(.bottomRight)
|
|
if !isPreviousMessageSameSender(at: indexPath) {
|
|
corners.formUnion(.topLeft)
|
|
}
|
|
if !isNextMessageSameSender(at: indexPath) {
|
|
corners.formUnion(.bottomLeft)
|
|
}
|
|
}
|
|
|
|
return .custom { view in
|
|
let radius: CGFloat = 16
|
|
let path = UIBezierPath(
|
|
roundedRect: view.bounds,
|
|
byRoundingCorners: corners,
|
|
cornerRadii: CGSize(width: radius, height: radius))
|
|
let mask = CAShapeLayer()
|
|
mask.path = path.cgPath
|
|
view.layer.mask = mask
|
|
}
|
|
}
|
|
|
|
func configureAvatarView(
|
|
_ avatarView: AvatarView,
|
|
for message: MessageType,
|
|
at indexPath: IndexPath,
|
|
in _: MessagesCollectionView)
|
|
{
|
|
let avatar = SampleData.shared.getAvatarFor(sender: message.sender)
|
|
avatarView.set(avatar: avatar)
|
|
avatarView.isHidden = isNextMessageSameSender(at: indexPath)
|
|
avatarView.layer.borderWidth = 2
|
|
avatarView.layer.borderColor = UIColor.primaryColor.cgColor
|
|
}
|
|
|
|
func configureAccessoryView(_ accessoryView: UIView, for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) {
|
|
// Cells are reused, so only add a button here once. For real use you would need to
|
|
// ensure any subviews are removed if not needed
|
|
accessoryView.subviews.forEach { $0.removeFromSuperview() }
|
|
accessoryView.backgroundColor = .clear
|
|
|
|
let shouldShow = Int.random(in: 0 ... 10) == 0
|
|
guard shouldShow else { return }
|
|
|
|
let button = UIButton(type: .infoLight)
|
|
button.tintColor = .primaryColor
|
|
accessoryView.addSubview(button)
|
|
button.frame = accessoryView.bounds
|
|
button.isUserInteractionEnabled = false // respond to accessoryView tap through `MessageCellDelegate`
|
|
accessoryView.layer.cornerRadius = accessoryView.frame.height / 2
|
|
accessoryView.backgroundColor = UIColor.primaryColor.withAlphaComponent(0.3)
|
|
}
|
|
|
|
func configureMediaMessageImageView(
|
|
_ imageView: UIImageView,
|
|
for message: MessageType,
|
|
at _: IndexPath,
|
|
in _: MessagesCollectionView)
|
|
{
|
|
if case MessageKind.photo(let media) = message.kind, let imageURL = media.url {
|
|
imageView.kf.setImage(with: imageURL)
|
|
} else {
|
|
imageView.kf.cancelDownloadTask()
|
|
}
|
|
}
|
|
|
|
// MARK: - Location Messages
|
|
|
|
func annotationViewForLocation(message _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MKAnnotationView? {
|
|
let annotationView = MKAnnotationView(annotation: nil, reuseIdentifier: nil)
|
|
let pinImage = #imageLiteral(resourceName: "ic_map_marker")
|
|
annotationView.image = pinImage
|
|
annotationView.centerOffset = CGPoint(x: 0, y: -pinImage.size.height / 2)
|
|
return annotationView
|
|
}
|
|
|
|
func animationBlockForLocation(
|
|
message _: MessageType,
|
|
at _: IndexPath,
|
|
in _: MessagesCollectionView) -> ((UIImageView) -> Void)?
|
|
{
|
|
{ view in
|
|
view.layer.transform = CATransform3DMakeScale(2, 2, 2)
|
|
UIView.animate(
|
|
withDuration: 0.6,
|
|
delay: 0,
|
|
usingSpringWithDamping: 0.9,
|
|
initialSpringVelocity: 0,
|
|
options: [],
|
|
animations: {
|
|
view.layer.transform = CATransform3DIdentity
|
|
},
|
|
completion: nil)
|
|
}
|
|
}
|
|
|
|
func snapshotOptionsForLocation(
|
|
message _: MessageType,
|
|
at _: IndexPath,
|
|
in _: MessagesCollectionView)
|
|
-> LocationMessageSnapshotOptions
|
|
{
|
|
LocationMessageSnapshotOptions(
|
|
showsBuildings: true,
|
|
showsPointsOfInterest: true,
|
|
span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10))
|
|
}
|
|
|
|
// MARK: - Audio Messages
|
|
|
|
func audioTintColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
|
|
isFromCurrentSender(message: message) ? .white : .primaryColor
|
|
}
|
|
|
|
func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) {
|
|
audioController
|
|
.configureAudioCell(
|
|
cell,
|
|
message: message) // this is needed especially when the cell is reconfigure while is playing sound
|
|
}
|
|
}
|
|
|
|
// MARK: MessagesLayoutDelegate
|
|
|
|
extension AdvancedExampleViewController: MessagesLayoutDelegate {
|
|
func cellTopLabelHeight(for _: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> CGFloat {
|
|
if isTimeLabelVisible(at: indexPath) {
|
|
return 18
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> CGFloat {
|
|
if isFromCurrentSender(message: message) {
|
|
return !isPreviousMessageSameSender(at: indexPath) ? 20 : 0
|
|
} else {
|
|
return !isPreviousMessageSameSender(at: indexPath) ? (20 + outgoingAvatarOverlap) : 0
|
|
}
|
|
}
|
|
|
|
func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> CGFloat {
|
|
(!isNextMessageSameSender(at: indexPath) && isFromCurrentSender(message: message)) ? 16 : 0
|
|
}
|
|
}
|
|
|
|
// MARK: CameraInputBarAccessoryViewDelegate
|
|
|
|
extension AdvancedExampleViewController: CameraInputBarAccessoryViewDelegate {
|
|
func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith attachments: [AttachmentManager.Attachment]) {
|
|
for item in attachments {
|
|
if case .image(let image) = item {
|
|
self.sendImageMessage(photo: image)
|
|
}
|
|
}
|
|
inputBar.invalidatePlugins()
|
|
}
|
|
|
|
func sendImageMessage(photo: UIImage) {
|
|
let photoMessage = MockMessage(image: photo, user: currentSender as! MockUser, messageId: UUID().uuidString, date: Date())
|
|
insertMessage(photoMessage)
|
|
}
|
|
}
|