Added Swiftlint and Swiftformat plugins (#1729)

* 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>
This commit is contained in:
Martin PúčikandJakub Kaspar authored and GitHub committed 2022-07-25 08:46:14 +00:00
1 parent f3f61ebbc9
commit bff35fda61
133 files changed
+12886 -11957

No files matched your search

@@ -1,389 +1,432 @@
/*
MIT License
// 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.
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 UIKit
import MessageKit
import InputBarAccessoryView
import Kingfisher
import MessageKit
import UIKit
// MARK: - AutocompleteExampleViewController
final class AutocompleteExampleViewController: ChatViewController {
// MARK: Internal
lazy var joinChatButton: UIButton = {
let button = UIButton()
button.layer.cornerRadius = 16
button.backgroundColor = .primaryColor
button.setTitle("JOIN CHAT", for: .normal)
button.setTitleColor(.white, for: .normal)
button.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted)
button.addTarget(self, action: #selector(joinChat), for: .touchUpInside)
return button
}()
lazy var joinChatButton: UIButton = {
let button = UIButton()
button.layer.cornerRadius = 16
button.backgroundColor = .primaryColor
button.setTitle("JOIN CHAT", for: .normal)
button.setTitleColor(.white, for: .normal)
button.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted)
button.addTarget(self, action: #selector(joinChat), for: .touchUpInside)
return button
}()
/// The object that manages autocomplete, from InputBarAccessoryView
lazy var autocompleteManager: AutocompleteManager = { [unowned self] in
let manager = AutocompleteManager(for: self.messageInputBar.inputTextView)
manager.delegate = self
manager.dataSource = self
return manager
}()
/// The object that manages autocomplete, from InputBarAccessoryView
lazy var autocompleteManager: AutocompleteManager = { [unowned self] in
let manager = AutocompleteManager(for: self.messageInputBar.inputTextView)
manager.delegate = self
manager.dataSource = self
return manager
}()
var hashtagAutocompletes: [AutocompleteCompletion] = {
var array: [AutocompleteCompletion] = []
for _ in 1...100 {
array.append(AutocompleteCompletion(text: Lorem.word(), context: nil))
}
return array
}()
// Completions loaded async that get appended to local cached completions
var asyncCompletions: [AutocompleteCompletion] = []
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)
})
}
var hashtagAutocompletes: [AutocompleteCompletion] = {
var array: [AutocompleteCompletion] = []
for _ in 1 ... 100 {
array.append(AutocompleteCompletion(text: Lorem.word(), context: nil))
}
return array
}()
override func viewDidLoad() {
super.viewDidLoad()
// Completions loaded async that get appended to local cached completions
var asyncCompletions: [AutocompleteCompletion] = []
messageInputBar.inputTextView.keyboardType = .twitter
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Configure AutocompleteManager
autocompleteManager.register(prefix: "@", with: [.font: UIFont.preferredFont(forTextStyle: .body), .foregroundColor: UIColor.primaryColor, .backgroundColor: UIColor.primaryColor.withAlphaComponent(0.3)])
autocompleteManager.register(prefix: "#")
autocompleteManager.maxSpaceCountDuringCompletion = 1 // Allow for autocompletes with a space
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)
})
}
}
// Set plugins
messageInputBar.inputPlugins = [autocompleteManager]
override func viewDidLoad() {
super.viewDidLoad()
messageInputBar.inputTextView.keyboardType = .twitter
// Configure AutocompleteManager
autocompleteManager.register(
prefix: "@",
with: [
.font: UIFont.preferredFont(forTextStyle: .body),
.foregroundColor: UIColor.primaryColor,
.backgroundColor: UIColor.primaryColor.withAlphaComponent(0.3),
])
autocompleteManager.register(prefix: "#")
autocompleteManager.maxSpaceCountDuringCompletion = 1 // Allow for autocompletes with a space
// Set plugins
messageInputBar.inputPlugins = [autocompleteManager]
}
override func configureMessageCollectionView() {
super.configureMessageCollectionView()
let layout = messagesCollectionView.collectionViewLayout as? MessagesCollectionViewFlowLayout
layout?.sectionInset = UIEdgeInsets(top: 1, left: 8, bottom: 1, right: 8)
layout?.setMessageOutgoingCellBottomLabelAlignment(.init(textAlignment: .right, textInsets: .zero))
layout?.setMessageOutgoingAvatarSize(.zero)
layout?
.setMessageOutgoingMessageTopLabelAlignment(LabelAlignment(
textAlignment: .right,
textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 12)))
layout?
.setMessageOutgoingMessageBottomLabelAlignment(LabelAlignment(
textAlignment: .right,
textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 12)))
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
additionalBottomInset = 30
}
override func configureMessageInputBar() {
super.configureMessageInputBar()
messageInputBar.layer.shadowColor = UIColor.black.cgColor
messageInputBar.layer.shadowRadius = 4
messageInputBar.layer.shadowOpacity = 0.3
messageInputBar.layer.shadowOffset = CGSize(width: 0, height: 0)
messageInputBar.separatorLine.isHidden = true
messageInputBar.setRightStackViewWidthConstant(to: 0, animated: false)
messageInputBar.setMiddleContentView(joinChatButton, animated: false)
}
@objc
func joinChat() {
configureMessageInputBarForChat()
}
// 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) {
setTypingIndicatorViewHidden(isHidden, animated: true, whilePerforming: updates) { [weak self] success in
if success, self?.isLastSectionVisible() == true {
self?.messagesCollectionView.scrollToLastItem(animated: true)
}
}
}
override func configureMessageCollectionView() {
super.configureMessageCollectionView()
// MARK: - MessagesDataSource
let layout = messagesCollectionView.collectionViewLayout as? MessagesCollectionViewFlowLayout
layout?.sectionInset = UIEdgeInsets(top: 1, left: 8, bottom: 1, right: 8)
layout?.setMessageOutgoingCellBottomLabelAlignment(.init(textAlignment: .right, textInsets: .zero))
layout?.setMessageOutgoingAvatarSize(.zero)
layout?.setMessageOutgoingMessageTopLabelAlignment(LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 12)))
layout?.setMessageOutgoingMessageBottomLabelAlignment(LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 12)))
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
additionalBottomInset = 30
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 configureMessageInputBar() {
super.configureMessageInputBar()
messageInputBar.layer.shadowColor = UIColor.black.cgColor
messageInputBar.layer.shadowRadius = 4
messageInputBar.layer.shadowOpacity = 0.3
messageInputBar.layer.shadowOffset = CGSize(width: 0, height: 0)
messageInputBar.separatorLine.isHidden = true
messageInputBar.setRightStackViewWidthConstant(to: 0, animated: false)
messageInputBar.setMiddleContentView(joinChatButton, animated: false)
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
}
private func configureMessageInputBarForChat() {
messageInputBar.setMiddleContentView(messageInputBar.inputTextView, animated: false)
messageInputBar.setRightStackViewWidthConstant(to: 52, animated: false)
let bottomItems = [makeButton(named: "ic_at"), makeButton(named: "ic_hashtag"), .flexibleSpace]
messageInputBar.setStackViewItems(bottomItems, forStack: .bottom, animated: false)
messageInputBar.sendButton.activityViewColor = .white
messageInputBar.sendButton.backgroundColor = .primaryColor
messageInputBar.sendButton.layer.cornerRadius = 10
messageInputBar.sendButton.setTitleColor(.white, for: .normal)
messageInputBar.sendButton.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted)
messageInputBar.sendButton.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .disabled)
messageInputBar.sendButton
.onSelected { item in
item.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
}.onDeselected { item in
item.transform = .identity
}
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
}
@objc
func joinChat() {
configureMessageInputBarForChat()
// Async autocomplete requires the manager to reload
func inputBar(_: InputBarAccessoryView, textViewTextDidChangeTo _: String) {
guard autocompleteManager.currentSession != nil, autocompleteManager.currentSession?.prefix == "#" else { return }
// Load some data asyncronously for the given session.prefix
DispatchQueue.global(qos: .default).async {
// fake background loading task
var array: [AutocompleteCompletion] = []
for _ in 1 ... 10 {
array.append(AutocompleteCompletion(text: Lorem.word()))
}
sleep(1)
DispatchQueue.main.async { [weak self] in
self?.asyncCompletions = array
self?.autocompleteManager.reloadData()
}
}
}
// MARK: - Helpers
// MARK: Private
func isTimeLabelVisible(at indexPath: IndexPath) -> Bool {
return indexPath.section % 3 == 0 && !isPreviousMessageSameSender(at: indexPath)
}
private func configureMessageInputBarForChat() {
messageInputBar.setMiddleContentView(messageInputBar.inputTextView, animated: false)
messageInputBar.setRightStackViewWidthConstant(to: 52, animated: false)
let bottomItems = [makeButton(named: "ic_at"), makeButton(named: "ic_hashtag"), .flexibleSpace]
messageInputBar.setStackViewItems(bottomItems, forStack: .bottom, animated: false)
func isPreviousMessageSameSender(at indexPath: IndexPath) -> Bool {
guard indexPath.section - 1 >= 0 else { return false }
return messageList[indexPath.section].user == messageList[indexPath.section - 1].user
}
messageInputBar.sendButton.activityViewColor = .white
messageInputBar.sendButton.backgroundColor = .primaryColor
messageInputBar.sendButton.layer.cornerRadius = 10
messageInputBar.sendButton.setTitleColor(.white, for: .normal)
messageInputBar.sendButton.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted)
messageInputBar.sendButton.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .disabled)
messageInputBar.sendButton
.onSelected { item in
item.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
}.onDeselected { item in
item.transform = .identity
}
}
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) {
setTypingIndicatorViewHidden(isHidden, animated: true, whilePerforming: updates) { [weak self] success in
if success, self?.isLastSectionVisible() == true {
self?.messagesCollectionView.scrollToLastItem(animated: true)
}
}
}
private func makeButton(named: String) -> InputBarButtonItem {
return 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 { _ in
print("Item Tapped")
}
}
// 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
}
// Async autocomplete requires the manager to reload
func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) {
guard autocompleteManager.currentSession != nil, autocompleteManager.currentSession?.prefix == "#" else { return }
// Load some data asyncronously for the given session.prefix
DispatchQueue.global(qos: .default).async {
// fake background loading task
var array: [AutocompleteCompletion] = []
for _ in 1...10 {
array.append(AutocompleteCompletion(text: Lorem.word()))
}
sleep(1)
DispatchQueue.main.async { [weak self] in
self?.asyncCompletions = array
self?.autocompleteManager.reloadData()
}
}
}
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 { _ in
print("Item Tapped")
}
}
}
// MARK: AutocompleteManagerDelegate, AutocompleteManagerDataSource
extension AutocompleteExampleViewController: AutocompleteManagerDelegate, AutocompleteManagerDataSource {
// MARK: - AutocompleteManagerDataSource
// MARK: - AutocompleteManagerDataSource
func autocompleteManager(_ manager: AutocompleteManager, autocompleteSourceFor prefix: String) -> [AutocompleteCompletion] {
if prefix == "@" {
return SampleData.shared.senders
.map { user in
return AutocompleteCompletion(text: user.displayName,
context: ["id": user.senderId])
}
} else if prefix == "#" {
return hashtagAutocompletes + asyncCompletions
func autocompleteManager(_: AutocompleteManager, autocompleteSourceFor prefix: String) -> [AutocompleteCompletion] {
if prefix == "@" {
return SampleData.shared.senders
.map { user in
AutocompleteCompletion(
text: user.displayName,
context: ["id": user.senderId])
}
return []
} else if prefix == "#" {
return hashtagAutocompletes + asyncCompletions
}
return []
}
func autocompleteManager(_ manager: AutocompleteManager, tableView: UITableView, cellForRowAt indexPath: IndexPath, for session: AutocompleteSession) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: AutocompleteCell.reuseIdentifier, for: indexPath) as? AutocompleteCell else {
fatalError("Oops, some unknown error occurred")
}
let users = SampleData.shared.senders
let id = session.completion?.context?["id"] as? String
let user = users.filter { return $0.senderId == id }.first
if let sender = user {
cell.imageView?.image = SampleData.shared.getAvatarFor(sender: sender).image
}
cell.imageViewEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
cell.imageView?.layer.cornerRadius = 14
cell.imageView?.layer.borderColor = UIColor.primaryColor.cgColor
cell.imageView?.layer.borderWidth = 1
cell.imageView?.clipsToBounds = true
cell.textLabel?.attributedText = manager.attributedText(matching: session, fontSize: 15)
return cell
func autocompleteManager(
_ manager: AutocompleteManager,
tableView: UITableView,
cellForRowAt indexPath: IndexPath,
for session: AutocompleteSession)
-> UITableViewCell
{
guard
let cell = tableView
.dequeueReusableCell(withIdentifier: AutocompleteCell.reuseIdentifier, for: indexPath) as? AutocompleteCell else
{
fatalError("Oops, some unknown error occurred")
}
// MARK: - AutocompleteManagerDelegate
func autocompleteManager(_ manager: AutocompleteManager, shouldBecomeVisible: Bool) {
setAutocompleteManager(active: shouldBecomeVisible)
let users = SampleData.shared.senders
let id = session.completion?.context?["id"] as? String
let user = users.filter { $0.senderId == id }.first
if let sender = user {
cell.imageView?.image = SampleData.shared.getAvatarFor(sender: sender).image
}
cell.imageViewEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
cell.imageView?.layer.cornerRadius = 14
cell.imageView?.layer.borderColor = UIColor.primaryColor.cgColor
cell.imageView?.layer.borderWidth = 1
cell.imageView?.clipsToBounds = true
cell.textLabel?.attributedText = manager.attributedText(matching: session, fontSize: 15)
return cell
}
// Optional
func autocompleteManager(_ manager: AutocompleteManager, shouldRegister prefix: String, at range: NSRange) -> Bool {
return true
}
// MARK: - AutocompleteManagerDelegate
// Optional
func autocompleteManager(_ manager: AutocompleteManager, shouldUnregister prefix: String) -> Bool {
return true
}
func autocompleteManager(_: AutocompleteManager, shouldBecomeVisible: Bool) {
setAutocompleteManager(active: shouldBecomeVisible)
}
// Optional
func autocompleteManager(_ manager: AutocompleteManager, shouldComplete prefix: String, with text: String) -> Bool {
return true
}
// Optional
func autocompleteManager(_: AutocompleteManager, shouldRegister _: String, at _: NSRange) -> Bool {
true
}
// MARK: - AutocompleteManagerDelegate Helper
// Optional
func autocompleteManager(_: AutocompleteManager, shouldUnregister _: String) -> Bool {
true
}
func setAutocompleteManager(active: Bool) {
let topStackView = messageInputBar.topStackView
if active && !topStackView.arrangedSubviews.contains(autocompleteManager.tableView) {
topStackView.insertArrangedSubview(autocompleteManager.tableView, at: topStackView.arrangedSubviews.count)
topStackView.layoutIfNeeded()
} else if !active && topStackView.arrangedSubviews.contains(autocompleteManager.tableView) {
topStackView.removeArrangedSubview(autocompleteManager.tableView)
topStackView.layoutIfNeeded()
}
messageInputBar.invalidateIntrinsicContentSize()
// Optional
func autocompleteManager(_: AutocompleteManager, shouldComplete _: String, with _: String) -> Bool {
true
}
// MARK: - AutocompleteManagerDelegate Helper
func setAutocompleteManager(active: Bool) {
let topStackView = messageInputBar.topStackView
if active, !topStackView.arrangedSubviews.contains(autocompleteManager.tableView) {
topStackView.insertArrangedSubview(autocompleteManager.tableView, at: topStackView.arrangedSubviews.count)
topStackView.layoutIfNeeded()
} else if !active, topStackView.arrangedSubviews.contains(autocompleteManager.tableView) {
topStackView.removeArrangedSubview(autocompleteManager.tableView)
topStackView.layoutIfNeeded()
}
messageInputBar.invalidateIntrinsicContentSize()
}
}
// MARK: - MessagesDisplayDelegate
// MARK: MessagesDisplayDelegate
extension AutocompleteExampleViewController: MessagesDisplayDelegate {
// MARK: - Text Messages
// MARK: - Text Messages
func textColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
isFromCurrentSender(message: message) ? .white : .darkText
}
func textColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
return 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 detectorAttributes(for detector: DetectorType, and message: MessageType, at indexPath: 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]
}
func enabledDetectors(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> [DetectorType] {
return [.url, .address, .phoneNumber, .date, .transitInformation, .mention, .hashtag]
}
// MARK: - All Messages
// 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 backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
return isFromCurrentSender(message: message) ? .primaryColor : UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1)
}
func messageStyle(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MessageStyle {
.bubble
}
func messageStyle(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageStyle {
return .bubble
}
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 configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: 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() }
func configureAccessoryView(_ accessoryView: UIView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: 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() }
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)
}
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: IndexPath, in messagesCollectionView: MessagesCollectionView) {
if case MessageKind.photo(let media) = message.kind, let imageURL = media.url {
imageView.kf.setImage(with: imageURL)
} else {
imageView.kf.cancelDownloadTask()
}
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: - MessagesLayoutDelegate
// MARK: MessagesLayoutDelegate
extension AutocompleteExampleViewController: MessagesLayoutDelegate {
func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
if isTimeLabelVisible(at: indexPath) {
return 18
}
return 0
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: MessagesCollectionView) -> CGFloat {
if isFromCurrentSender(message: message) {
return !isPreviousMessageSameSender(at: indexPath) ? 20 : 0
} else {
return !isPreviousMessageSameSender(at: indexPath) ? 20 : 0
}
}
func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
return (!isNextMessageSameSender(at: indexPath) && isFromCurrentSender(message: message)) ? 16 : 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 : 0
}
}
func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> CGFloat {
(!isNextMessageSameSender(at: indexPath) && isFromCurrentSender(message: message)) ? 16 : 0
}
}