mirror of
https://github.com/MessageKit/MessageKit.git
synced 2026-02-06 19:03:19 +00:00
* 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>
408 lines
16 KiB
Swift
408 lines
16 KiB
Swift
// MIT License
|
||
//
|
||
// Copyright (c) 2017-2019 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 MapKit
|
||
import UIKit
|
||
|
||
// MARK: - MessagesDisplayDelegate
|
||
|
||
/// A protocol used by the `MessagesViewController` to customize the appearance of a `MessageContentCell`.
|
||
public protocol MessagesDisplayDelegate: AnyObject {
|
||
// MARK: - All Messages
|
||
|
||
/// Specifies the `MessageStyle` to be used for a `MessageContainerView`.
|
||
///
|
||
/// - 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.
|
||
///
|
||
/// - Note:
|
||
/// The default value returned by this method is `MessageStyle.bubble`.
|
||
func messageStyle(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView)
|
||
-> MessageStyle
|
||
|
||
/// Specifies the background color of the `MessageContainerView`.
|
||
///
|
||
/// - 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.
|
||
///
|
||
/// - Note:
|
||
/// The default value is `UIColor.clear` for emoji messages.
|
||
/// For all other `MessageKind` cases, the color depends on the `SenderType`.
|
||
///
|
||
/// Current sender: Green
|
||
///
|
||
/// All other senders: Gray
|
||
func backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView)
|
||
-> UIColor
|
||
|
||
/// The section header to use for a given `IndexPath`.
|
||
///
|
||
/// - Parameters:
|
||
/// - message: The `MessageType` that will be displayed for this header.
|
||
/// - indexPath: The `IndexPath` of the header.
|
||
/// - messagesCollectionView: The `MessagesCollectionView` in which this header will be displayed.
|
||
func messageHeaderView(for indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageReusableView
|
||
|
||
/// The section footer to use for a given `IndexPath`.
|
||
///
|
||
/// - Parameters:
|
||
/// - indexPath: The `IndexPath` of the footer.
|
||
/// - messagesCollectionView: The `MessagesCollectionView` in which this footer will be displayed.
|
||
func messageFooterView(for indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageReusableView
|
||
|
||
/// Used to configure the `AvatarView`‘s image in a `MessageContentCell` class.
|
||
///
|
||
/// - Parameters:
|
||
/// - avatarView: The `AvatarView` of the cell.
|
||
/// - 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.
|
||
///
|
||
/// - Note:
|
||
/// The default image configured by this method is `?`.
|
||
func configureAvatarView(
|
||
_ avatarView: AvatarView,
|
||
for message: MessageType,
|
||
at indexPath: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
|
||
/// Used to configure the `AccessoryView` in a `MessageContentCell` class.
|
||
///
|
||
/// - Parameters:
|
||
/// - accessoryView: The `AccessoryView` of the cell.
|
||
/// - 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.
|
||
///
|
||
/// - Note:
|
||
/// The default image configured by this method is `?`.
|
||
func configureAccessoryView(
|
||
_ accessoryView: UIView,
|
||
for message: MessageType,
|
||
at indexPath: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
|
||
// MARK: - Text Messages
|
||
|
||
/// Specifies the color of the text for a `TextMessageCell`.
|
||
///
|
||
/// - Parameters:
|
||
/// - message: A `MessageType` with a `MessageKind` case of `.text` to which the color will apply.
|
||
/// - 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 determined by the messages `SenderType`.
|
||
///
|
||
/// Current sender: UIColor.white
|
||
///
|
||
/// All other senders: UIColor.darkText
|
||
func textColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView)
|
||
-> UIColor
|
||
|
||
/// Specifies the `DetectorType`s to check for the `MessageType`'s text against.
|
||
///
|
||
/// - Parameters:
|
||
/// - message: A `MessageType` with a `MessageKind` case of `.text` or `.attributedText` to which the detectors will apply.
|
||
/// - indexPath: The `IndexPath` of the cell.
|
||
/// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
|
||
///
|
||
/// - Note:
|
||
/// This method returns an empty array by default.
|
||
func enabledDetectors(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView)
|
||
-> [DetectorType]
|
||
|
||
/// Specifies the attributes for a given `DetectorType`
|
||
///
|
||
/// - Parameters:
|
||
/// - detector: The `DetectorType` for the applied attributes.
|
||
/// - message: A `MessageType` with a `MessageKind` case of `.text` or `.attributedText` to which the detectors will apply.
|
||
/// - indexPath: The `IndexPath` of the cell.
|
||
func detectorAttributes(for detector: DetectorType, and message: MessageType, at indexPath: IndexPath)
|
||
-> [NSAttributedString.Key: Any]
|
||
|
||
// MARK: - Location Messages
|
||
|
||
/// Used to configure a `LocationMessageSnapshotOptions` instance to customize the map image on the given location message.
|
||
///
|
||
/// - Parameters:
|
||
/// - message: A `MessageType` with a `MessageKind` case of `.location`.
|
||
/// - indexPath: The `IndexPath` of the cell.
|
||
/// - messagesCollectionView: The `MessagesCollectionView` requesting the information.
|
||
/// - Returns: The LocationMessageSnapshotOptions instance with the options to customize map style.
|
||
func snapshotOptionsForLocation(
|
||
message: MessageType,
|
||
at indexPath: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView) -> LocationMessageSnapshotOptions
|
||
|
||
/// Used to configure the annotation view of the map image on the given location message.
|
||
///
|
||
/// - Parameters:
|
||
/// - message: A `MessageType` with a `MessageKind` case of `.location`.
|
||
/// - indexPath: The `IndexPath` of the cell.
|
||
/// - messagesCollectionView: The `MessagesCollectionView` requesting the information.
|
||
/// - Returns: The `MKAnnotationView` to use as the annotation view.
|
||
func annotationViewForLocation(
|
||
message: MessageType,
|
||
at indexPath: IndexPath,
|
||
in messageCollectionView: MessagesCollectionView) -> MKAnnotationView?
|
||
|
||
/// Ask the delegate for a custom animation block to run when whe map screenshot is ready to be displaied in the given location message.
|
||
/// The animation block is called with the `UIImageView` to be animated.
|
||
///
|
||
/// - Parameters:
|
||
/// - message: A `MessageType` with a `MessageKind` case of `.location`.
|
||
/// - indexPath: The `IndexPath` of the cell.
|
||
/// - messagesCollectionView: The `MessagesCollectionView` requesting the information.
|
||
/// - Returns: The animation block to use to apply the location image.
|
||
func animationBlockForLocation(
|
||
message: MessageType,
|
||
at indexPath: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView) -> ((UIImageView) -> Void)?
|
||
|
||
// MARK: - Media Messages
|
||
|
||
/// Used to configure the `UIImageView` of a `MediaMessageCell`.
|
||
///
|
||
/// - Parameters:
|
||
/// - imageView: The `UIImageView` of the cell.
|
||
/// - 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.
|
||
func configureMediaMessageImageView(
|
||
_ imageView: UIImageView,
|
||
for message: MessageType,
|
||
at indexPath: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
|
||
// MARK: - Audio Message
|
||
|
||
/// Used to configure the audio cell UI:
|
||
/// 1. play button selected state;
|
||
/// 2. progressView progress;
|
||
/// 3. durationLabel text;
|
||
///
|
||
/// - Parameters:
|
||
/// - cell: The `AudioMessageCell` that needs to be configure.
|
||
/// - message: The `MessageType` that configures the cell.
|
||
///
|
||
/// - Note:
|
||
/// This protocol method is called by MessageKit every time an audio cell needs to be configure
|
||
func configureAudioCell(_ cell: AudioMessageCell, message: MessageType)
|
||
|
||
/// Specifies the tint color of play button and progress bar for an `AudioMessageCell`.
|
||
///
|
||
/// - Parameters:
|
||
/// - message: A `MessageType` with a `MessageKind` case of `.audio` to which the color will apply.
|
||
/// - 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 UIColor.sendButtonBlue
|
||
func audioTintColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView)
|
||
-> UIColor
|
||
|
||
/// Used to format the audio sound duration in a readable string
|
||
///
|
||
/// - Parameters:
|
||
/// - duration: The audio sound duration is seconds.
|
||
/// - audioCell: The `AudioMessageCell` that ask for formated duration.
|
||
/// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
|
||
///
|
||
/// - Note:
|
||
/// The default value is computed like fallow:
|
||
/// 1. return the time as 0:ss if duration is up to 59 seconds (e.g. 0:03 means 0 minutes and 3 seconds)
|
||
/// 2. return the time as m:ss if duration is greater than 59 and lower than 3600 (e.g. 12:23 means 12 minutes and 23 seconds)
|
||
/// 3. return the time as h:mm:ss for anything longer that 3600 seconds (e.g. 1:19:08 means 1 hour 19 minutes and 8 seconds)
|
||
func audioProgressTextFormat(
|
||
_ duration: Float,
|
||
for audioCell: AudioMessageCell,
|
||
in messageCollectionView: MessagesCollectionView) -> String
|
||
|
||
/// Used to configure the `UIImageView` of a `LinkPreviewMessageCell`.
|
||
/// - Parameters:
|
||
/// - imageView: The `UIImageView` of the cell.
|
||
/// - 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.
|
||
func configureLinkPreviewImageView(
|
||
_ imageView: UIImageView,
|
||
for message: MessageType,
|
||
at indexPath: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
}
|
||
|
||
extension MessagesDisplayDelegate {
|
||
// MARK: - All Messages Defaults
|
||
|
||
public func messageStyle(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MessageStyle {
|
||
.bubble
|
||
}
|
||
|
||
public func backgroundColor(
|
||
for message: MessageType,
|
||
at _: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
-> UIColor
|
||
{
|
||
switch message.kind {
|
||
case .emoji:
|
||
return .clear
|
||
default:
|
||
guard let dataSource = messagesCollectionView.messagesDataSource else {
|
||
return .white
|
||
}
|
||
return dataSource.isFromCurrentSender(message: message) ? .outgoingMessageBackground : .incomingMessageBackground
|
||
}
|
||
}
|
||
|
||
public func messageHeaderView(
|
||
for indexPath: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
-> MessageReusableView
|
||
{
|
||
messagesCollectionView.dequeueReusableHeaderView(MessageReusableView.self, for: indexPath)
|
||
}
|
||
|
||
public func messageFooterView(
|
||
for indexPath: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
-> MessageReusableView
|
||
{
|
||
messagesCollectionView.dequeueReusableFooterView(MessageReusableView.self, for: indexPath)
|
||
}
|
||
|
||
public func configureAvatarView(_ avatarView: AvatarView, for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) {
|
||
avatarView.initials = "?"
|
||
}
|
||
|
||
public func configureAccessoryView(_: UIView, for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) { }
|
||
|
||
// MARK: - Text Messages Defaults
|
||
|
||
public func textColor(
|
||
for message: MessageType,
|
||
at _: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
-> UIColor
|
||
{
|
||
guard let dataSource = messagesCollectionView.messagesDataSource else {
|
||
fatalError(MessageKitError.nilMessagesDataSource)
|
||
}
|
||
return dataSource.isFromCurrentSender(message: message) ? .outgoingMessageLabel : .incomingMessageLabel
|
||
}
|
||
|
||
public func enabledDetectors(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> [DetectorType] {
|
||
[]
|
||
}
|
||
|
||
public func detectorAttributes(for _: DetectorType, and _: MessageType, at _: IndexPath) -> [NSAttributedString.Key: Any] {
|
||
MessageLabel.defaultAttributes
|
||
}
|
||
|
||
// MARK: - Location Messages Defaults
|
||
|
||
public func snapshotOptionsForLocation(
|
||
message _: MessageType,
|
||
at _: IndexPath,
|
||
in _: MessagesCollectionView)
|
||
-> LocationMessageSnapshotOptions
|
||
{
|
||
LocationMessageSnapshotOptions()
|
||
}
|
||
|
||
public func annotationViewForLocation(
|
||
message _: MessageType,
|
||
at _: IndexPath,
|
||
in _: MessagesCollectionView)
|
||
-> MKAnnotationView?
|
||
{
|
||
MKPinAnnotationView(annotation: nil, reuseIdentifier: nil)
|
||
}
|
||
|
||
public func animationBlockForLocation(
|
||
message _: MessageType,
|
||
at _: IndexPath,
|
||
in _: MessagesCollectionView) -> ((UIImageView) -> Void)?
|
||
{
|
||
nil
|
||
}
|
||
|
||
// MARK: - Media Message Defaults
|
||
|
||
public func configureMediaMessageImageView(
|
||
_: UIImageView,
|
||
for _: MessageType,
|
||
at _: IndexPath,
|
||
in _: MessagesCollectionView) { }
|
||
|
||
// MARK: - Audio Message Defaults
|
||
|
||
public func configureAudioCell(_: AudioMessageCell, message _: MessageType) { }
|
||
|
||
public func audioTintColor(
|
||
for message: MessageType,
|
||
at _: IndexPath,
|
||
in messagesCollectionView: MessagesCollectionView)
|
||
-> UIColor
|
||
{
|
||
guard let dataSource = messagesCollectionView.messagesDataSource else {
|
||
fatalError(MessageKitError.nilMessagesDataSource)
|
||
}
|
||
return dataSource.isFromCurrentSender(message: message) ? .outgoingAudioMessageTint : .incomingAudioMessageTint
|
||
}
|
||
|
||
public func audioProgressTextFormat(_ duration: Float, for _: AudioMessageCell, in _: MessagesCollectionView) -> String {
|
||
var returnValue = "0:00"
|
||
// print the time as 0:ss if duration is up to 59 seconds
|
||
// print the time as m:ss if duration is up to 59:59 seconds
|
||
// print the time as h:mm:ss for anything longer
|
||
if duration < 60 {
|
||
returnValue = String(format: "0:%.02d", Int(duration.rounded(.up)))
|
||
} else if duration < 3600 {
|
||
returnValue = String(format: "%.02d:%.02d", Int(duration / 60), Int(duration) % 60)
|
||
} else {
|
||
let hours = Int(duration / 3600)
|
||
let remainingMinutesInSeconds = Int(duration) - hours * 3600
|
||
returnValue = String(
|
||
format: "%.02d:%.02d:%.02d",
|
||
hours,
|
||
Int(remainingMinutesInSeconds / 60),
|
||
Int(remainingMinutesInSeconds) % 60)
|
||
}
|
||
return returnValue
|
||
}
|
||
|
||
// MARK: - LinkPreview Message Defaults
|
||
|
||
public func configureLinkPreviewImageView(
|
||
_: UIImageView,
|
||
for _: MessageType,
|
||
at _: IndexPath,
|
||
in _: MessagesCollectionView) { }
|
||
}
|