Browse Source

Merge pull request #83 from deltachat/ChatViewBeauty

Chat view beauty
nayooti 6 năm trước cách đây
mục cha
commit
823d8422b6
100 tập tin đã thay đổi với 4728 bổ sung928 xóa
  1. 1 1
      Podfile
  2. 20 20
      Podfile.lock
  3. 54 32
      Pods/InputBarAccessoryView/InputBarAccessoryView/Controls/InputBarButtonItem.swift
  4. 96 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Controls/InputBarSendButton.swift
  5. 98 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/NSMutableAttributedString+Extensions.swift
  6. 89 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/NSNotification+Extensions.swift
  7. 17 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/String+Extensions.swift
  8. 64 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/UITextView+Extensions.swift
  9. 96 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/UIView+AutoLayout.swift
  10. 251 125
      Pods/InputBarAccessoryView/InputBarAccessoryView/InputBarAccessoryView.swift
  11. 53 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/KeyboardManager/KeyboardEvent.swift
  12. 299 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/KeyboardManager/KeyboardManager.swift
  13. 66 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/KeyboardManager/KeyboardNotification.swift
  14. 16 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Models/HorizontalEdgeInsets.swift.swift
  15. 82 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Models/NSConstraintLayoutSet.swift
  16. 235 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/AttachmentManager.swift
  17. 41 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Protocols/AttachmentManagerDataSource.swift
  18. 81 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Protocols/AttachmentManagerDelegate.swift
  19. 132 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Views/AttachmentCell.swift
  20. 82 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Views/AttachmentsView.swift
  21. 67 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Views/ImageAttachmentCell.swift
  22. 503 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/AutocompleteManager.swift
  23. 48 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Models/AutocompleteCompletion.swift
  24. 52 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Models/AutocompleteSession.swift
  25. 67 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Protocols/AutocompleteManagerDataSource.swift
  26. 82 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Protocols/AutocompleteManagerDelegate.swift
  27. 97 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Views/AutocompleteCell.swift
  28. 41 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Views/AutocompleteTableView.swift
  29. 74 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Protocols/InputBarAccessoryViewDelegate.swift
  30. 50 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Protocols/InputItem.swift
  31. 43 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Protocols/InputPlugin.swift
  32. 185 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/RxInputBarAccessoryView/RxInputBarAccessoryView.swift
  33. 91 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Supporting Files/InputBarAccessoryView+Availability.swift
  34. 88 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/ViewControllers/InputBarViewController.swift
  35. 76 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Views/InputStackView.swift
  36. 62 50
      Pods/InputBarAccessoryView/InputBarAccessoryView/Views/InputTextView.swift
  37. 72 0
      Pods/InputBarAccessoryView/InputBarAccessoryView/Views/SeparatorLine.swift
  38. 3 2
      Pods/InputBarAccessoryView/LICENSE
  39. 129 0
      Pods/InputBarAccessoryView/README.md
  40. 20 20
      Pods/Manifest.lock
  41. 0 91
      Pods/MessageInputBar/README.md
  42. 0 73
      Pods/MessageInputBar/Sources/Extensions/NSMutableAttributedString+Extensions.swift
  43. 0 116
      Pods/MessageInputBar/Sources/Extensions/UIView+Extensions.swift
  44. 0 81
      Pods/MessageInputBar/Sources/Models/NSConstraintLayoutSet.swift
  45. 0 62
      Pods/MessageInputBar/Sources/Protocols/MessageInputBarDelegate.swift
  46. 0 72
      Pods/MessageInputBar/Sources/Views/InputStackView.swift
  47. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/disclouser.png
  48. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/disclouser@2x.png
  49. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/disclouser@3x.png
  50. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/pause.png
  51. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/pause@2x.png
  52. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/pause@3x.png
  53. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/play.png
  54. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/play@2x.png
  55. BIN
      Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/play@3x.png
  56. 1 1
      Pods/MessageKit/LICENSE.md
  57. 67 30
      Pods/MessageKit/README.md
  58. 5 5
      Pods/MessageKit/Sources/Controllers/MessagesViewController+Keyboard.swift
  59. 2 3
      Pods/MessageKit/Sources/Controllers/MessagesViewController+Menu.swift
  60. 114 9
      Pods/MessageKit/Sources/Controllers/MessagesViewController.swift
  61. 3 3
      Pods/MessageKit/Sources/Extensions/Bundle+Extensions.swift
  62. 3 3
      Pods/MessageKit/Sources/Extensions/CGRect+Extensions.swift
  63. 3 3
      Pods/MessageKit/Sources/Extensions/NSAttributedString+Extensions.swift
  64. 9 7
      Pods/MessageKit/Sources/Extensions/UIColor+Extensions.swift
  65. 6 4
      Pods/MessageKit/Sources/Extensions/UIEdgeInsets+Extensions.swift
  66. 14 15
      Pods/MessageKit/Sources/Extensions/UIImage+Extension.swift
  67. 20 6
      Pods/MessageKit/Sources/Extensions/UIView+Extensions.swift
  68. 43 0
      Pods/MessageKit/Sources/Layout/AudioMessageSizeCalculator.swift
  69. 1 1
      Pods/MessageKit/Sources/Layout/CellSizeCalculator.swift
  70. 73 0
      Pods/MessageKit/Sources/Layout/ContactMessageSizeCalculator.swift
  71. 1 1
      Pods/MessageKit/Sources/Layout/LocationMessageSizeCalculator.swift
  72. 1 1
      Pods/MessageKit/Sources/Layout/MediaMessageSizeCalculator.swift
  73. 42 7
      Pods/MessageKit/Sources/Layout/MessageSizeCalculator.swift
  74. 75 9
      Pods/MessageKit/Sources/Layout/MessagesCollectionViewFlowLayout.swift
  75. 16 3
      Pods/MessageKit/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift
  76. 1 1
      Pods/MessageKit/Sources/Layout/TextMessageSizeCalculator.swift
  77. 43 0
      Pods/MessageKit/Sources/Layout/TypingIndicatorCellSizeCalculator.swift
  78. 48 0
      Pods/MessageKit/Sources/Models/AccessoryPosition.swift
  79. 1 1
      Pods/MessageKit/Sources/Models/Avatar.swift
  80. 4 4
      Pods/MessageKit/Sources/Models/AvatarPosition.swift
  81. 33 7
      Pods/MessageKit/Sources/Models/DetectorType.swift
  82. 5 5
      Pods/MessageKit/Sources/Models/HorizontalEdgeInsets.swift
  83. 4 4
      Pods/MessageKit/Sources/Models/LabelAlignment.swift
  84. 1 1
      Pods/MessageKit/Sources/Models/LocationMessageSnapshotOptions.swift
  85. 7 3
      Pods/MessageKit/Sources/Models/MessageKind.swift
  86. 1 1
      Pods/MessageKit/Sources/Models/MessageKitDateFormatter.swift
  87. 1 1
      Pods/MessageKit/Sources/Models/MessageStyle.swift
  88. 1 1
      Pods/MessageKit/Sources/Models/NSConstraintLayoutSet.swift
  89. 14 14
      Pods/MessageKit/Sources/Models/Sender.swift
  90. 39 0
      Pods/MessageKit/Sources/Protocols/AudioItem.swift
  91. 12 11
      Pods/MessageKit/Sources/Protocols/ContactItem.swift
  92. 1 1
      Pods/MessageKit/Sources/Protocols/LocationItem.swift
  93. 1 1
      Pods/MessageKit/Sources/Protocols/MediaItem.swift
  94. 75 1
      Pods/MessageKit/Sources/Protocols/MessageCellDelegate.swift
  95. 26 1
      Pods/MessageKit/Sources/Protocols/MessageLabelDelegate.swift
  96. 2 2
      Pods/MessageKit/Sources/Protocols/MessageType.swift
  97. 34 8
      Pods/MessageKit/Sources/Protocols/MessagesDataSource.swift
  98. 71 3
      Pods/MessageKit/Sources/Protocols/MessagesDisplayDelegate.swift
  99. 43 1
      Pods/MessageKit/Sources/Protocols/MessagesLayoutDelegate.swift
  100. 38 0
      Pods/MessageKit/Sources/Protocols/SenderType.swift

+ 1 - 1
Podfile

@@ -16,7 +16,7 @@ target 'deltachat-ios' do
   pod 'JGProgressHUD'
   pod 'JGProgressHUD'
   pod 'SwiftyBeaver'
   pod 'SwiftyBeaver'
   pod 'DBDebugToolkit'
   pod 'DBDebugToolkit'
-  pod 'MessageKit', '2.0.0'
+  pod 'MessageKit'
 
 
   target 'deltachat-iosTests' do
   target 'deltachat-iosTests' do
     inherit! :search_paths
     inherit! :search_paths

+ 20 - 20
Podfile.lock

@@ -1,22 +1,22 @@
 PODS:
 PODS:
   - ALCameraViewController (3.0.3)
   - ALCameraViewController (3.0.3)
   - DBDebugToolkit (0.5.0)
   - DBDebugToolkit (0.5.0)
+  - InputBarAccessoryView (4.2.2)
   - JGProgressHUD (2.0.3)
   - JGProgressHUD (2.0.3)
-  - MessageInputBar/Core (0.4.1)
-  - MessageKit (2.0.0):
-    - MessageInputBar/Core
-  - QuickTableViewController (1.1.0)
-  - ReachabilitySwift (4.3.0)
-  - SwiftFormat/CLI (0.37.2)
-  - SwiftLint (0.29.3)
-  - SwiftyBeaver (1.6.1)
-  - UICircularProgressRing (6.1.0)
+  - MessageKit (3.0.0):
+    - InputBarAccessoryView (~> 4.2.2)
+  - QuickTableViewController (1.1.1)
+  - ReachabilitySwift (4.3.1)
+  - SwiftFormat/CLI (0.40.9)
+  - SwiftLint (0.32.0)
+  - SwiftyBeaver (1.7.0)
+  - UICircularProgressRing (6.2.0)
 
 
 DEPENDENCIES:
 DEPENDENCIES:
   - ALCameraViewController (from `https://github.com/dignifiedquire/ALCameraViewController`)
   - ALCameraViewController (from `https://github.com/dignifiedquire/ALCameraViewController`)
   - DBDebugToolkit
   - DBDebugToolkit
   - JGProgressHUD
   - JGProgressHUD
-  - MessageKit (= 2.0.0)
+  - MessageKit
   - QuickTableViewController
   - QuickTableViewController
   - ReachabilitySwift
   - ReachabilitySwift
   - SwiftFormat/CLI
   - SwiftFormat/CLI
@@ -27,8 +27,8 @@ DEPENDENCIES:
 SPEC REPOS:
 SPEC REPOS:
   https://github.com/cocoapods/specs.git:
   https://github.com/cocoapods/specs.git:
     - DBDebugToolkit
     - DBDebugToolkit
+    - InputBarAccessoryView
     - JGProgressHUD
     - JGProgressHUD
-    - MessageInputBar
     - MessageKit
     - MessageKit
     - QuickTableViewController
     - QuickTableViewController
     - ReachabilitySwift
     - ReachabilitySwift
@@ -49,16 +49,16 @@ CHECKOUT OPTIONS:
 SPEC CHECKSUMS:
 SPEC CHECKSUMS:
   ALCameraViewController: dd13cf0a5b44a4d542c73bbcbebd02bc09e929c7
   ALCameraViewController: dd13cf0a5b44a4d542c73bbcbebd02bc09e929c7
   DBDebugToolkit: c04bb6f618051d3de447a4b4323f37826116cfed
   DBDebugToolkit: c04bb6f618051d3de447a4b4323f37826116cfed
+  InputBarAccessoryView: 2b937602598e2fab3149f37f51dd7ad795653812
   JGProgressHUD: 12b20a8f4ffe05258f8635c1ab92816e451f904d
   JGProgressHUD: 12b20a8f4ffe05258f8635c1ab92816e451f904d
-  MessageInputBar: e81c7535347f1f7b923de7080409a535a004b6e4
-  MessageKit: 29c1c87e5a396d2ca7a3f712e5171dc9aba42a1e
-  QuickTableViewController: 129e71fc37a69991aff02bc946723d51a13d6aa5
-  ReachabilitySwift: 408477d1b6ed9779dba301953171e017c31241f3
-  SwiftFormat: abbfb26dde4c6d683923cde153c88e10da9d7881
-  SwiftLint: bfa7ca7b4d170cfaf0d236ca3ffd969e88a2f002
-  SwiftyBeaver: ccfcdf85a04d429f1633f668650b0ce8020bda3a
-  UICircularProgressRing: b3eba859a515e4d9d38503eec913f09c28450785
+  MessageKit: e892a9ca49ebe6d82684d3a93ad84021fa23e2c6
+  QuickTableViewController: 30f689d314a7c1fb726c6dab6fa5ad53c6bc9e94
+  ReachabilitySwift: 4032e2f59586e11e3b0ebe15b167abdd587a388b
+  SwiftFormat: 6b67b6e7fe73d664f0cbb4f13721f130462c86a5
+  SwiftLint: 009a898ef2a1c851f45e1b59349bf6ff2ddc990d
+  SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
+  UICircularProgressRing: 0ff679b05a17f15ad6301a7886686837b8c301a9
 
 
-PODFILE CHECKSUM: 8eb78a7985e96bf680a3423fa0fe74be1c95457e
+PODFILE CHECKSUM: 46d42246ca1255cc4af799d1aa792d60033b9fb8
 
 
 COCOAPODS: 1.6.1
 COCOAPODS: 1.6.1

+ 54 - 32
Pods/MessageInputBar/Sources/Controls/InputBarButtonItem.swift → Pods/InputBarAccessoryView/InputBarAccessoryView/Controls/InputBarButtonItem.swift

@@ -1,35 +1,38 @@
-/*
- MIT License
- 
- Copyright (c) 2017-2018 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.
- */
+//
+//  InputBarButtonItem.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/18/17.
+//
 
 
 import UIKit
 import UIKit
 
 
 /**
 /**
- A subclass of UIButton that conforms to InputItem
-
+ A InputItem that inherits from UIButton
+ 
  ## Important Notes ##
  ## Important Notes ##
  1. Intended to be used in an `InputStackView`
  1. Intended to be used in an `InputStackView`
-*/
+ */
 open class InputBarButtonItem: UIButton, InputItem {
 open class InputBarButtonItem: UIButton, InputItem {
     
     
     /// The spacing properties of the InputBarButtonItem
     /// The spacing properties of the InputBarButtonItem
@@ -47,8 +50,8 @@ open class InputBarButtonItem: UIButton, InputItem {
     
     
     // MARK: - Properties
     // MARK: - Properties
     
     
-    /// A weak reference to the MessageInputBar that the InputBarButtonItem used in
-    open weak var messageInputBar: MessageInputBar?
+    /// A weak reference to the InputBarAccessoryView that the InputBarButtonItem used in
+    open weak var inputBarAccessoryView: InputBarAccessoryView?
     
     
     /// The spacing property of the InputBarButtonItem that determines the contentHuggingPriority and any
     /// The spacing property of the InputBarButtonItem that determines the contentHuggingPriority and any
     /// additional space to the intrinsicContentSize
     /// additional space to the intrinsicContentSize
@@ -119,10 +122,10 @@ open class InputBarButtonItem: UIButton, InputItem {
             } else {
             } else {
                 onDeselectedAction?(self)
                 onDeselectedAction?(self)
             }
             }
-            
+
         }
         }
     }
     }
-    
+
     /// Calls the onEnabledAction or onDisabledAction when set
     /// Calls the onEnabledAction or onDisabledAction when set
     open override var isEnabled: Bool {
     open override var isEnabled: Bool {
         didSet {
         didSet {
@@ -139,6 +142,7 @@ open class InputBarButtonItem: UIButton, InputItem {
     private var onTouchUpInsideAction: InputBarButtonItemAction?
     private var onTouchUpInsideAction: InputBarButtonItemAction?
     private var onKeyboardEditingBeginsAction: InputBarButtonItemAction?
     private var onKeyboardEditingBeginsAction: InputBarButtonItemAction?
     private var onKeyboardEditingEndsAction: InputBarButtonItemAction?
     private var onKeyboardEditingEndsAction: InputBarButtonItemAction?
+    private var onKeyboardSwipeGestureAction: ((InputBarButtonItem, UISwipeGestureRecognizer) -> Void)?
     private var onTextViewDidChangeAction: ((InputBarButtonItem, InputTextView) -> Void)?
     private var onTextViewDidChangeAction: ((InputBarButtonItem, InputTextView) -> Void)?
     private var onSelectedAction: InputBarButtonItemAction?
     private var onSelectedAction: InputBarButtonItemAction?
     private var onDeselectedAction: InputBarButtonItemAction?
     private var onDeselectedAction: InputBarButtonItemAction?
@@ -189,8 +193,8 @@ open class InputBarButtonItem: UIButton, InputItem {
     open func setSize(_ newValue: CGSize?, animated: Bool) {
     open func setSize(_ newValue: CGSize?, animated: Bool) {
         size = newValue
         size = newValue
         if animated, let position = parentStackViewPosition {
         if animated, let position = parentStackViewPosition {
-            messageInputBar?.performLayout(animated) { [weak self] in
-                self?.messageInputBar?.layoutStackViews([position])
+            inputBarAccessoryView?.performLayout(animated) { [weak self] in
+                self?.inputBarAccessoryView?.layoutStackViews([position])
             }
             }
         }
         }
     }
     }
@@ -227,6 +231,17 @@ open class InputBarButtonItem: UIButton, InputItem {
         return self
         return self
     }
     }
     
     
+    
+    /// Sets the onKeyboardSwipeGestureAction
+    ///
+    /// - Parameter action: The new onKeyboardSwipeGestureAction
+    /// - Returns: Self
+    @discardableResult
+    open func onKeyboardSwipeGesture(_ action: @escaping (_ item: InputBarButtonItem, _ gesture: UISwipeGestureRecognizer) -> Void) -> Self {
+        onKeyboardSwipeGestureAction = action
+        return self
+    }
+    
     /// Sets the onTextViewDidChangeAction
     /// Sets the onTextViewDidChangeAction
     ///
     ///
     /// - Parameter action: The new onTextViewDidChangeAction
     /// - Parameter action: The new onTextViewDidChangeAction
@@ -296,6 +311,13 @@ open class InputBarButtonItem: UIButton, InputItem {
         onTextViewDidChangeAction?(self, textView)
         onTextViewDidChangeAction?(self, textView)
     }
     }
     
     
+    /// Executes the onKeyboardSwipeGestureAction with the given gesture
+    ///
+    /// - Parameter gesture: A reference to the gesture that was recognized
+    open func keyboardSwipeGestureAction(with gesture: UISwipeGestureRecognizer) {
+        onKeyboardSwipeGestureAction?(self, gesture)
+    }
+    
     /// Executes the onKeyboardEditingEndsAction
     /// Executes the onKeyboardEditingEndsAction
     open func keyboardEditingEndsAction() {
     open func keyboardEditingEndsAction() {
         onKeyboardEditingEndsAction?(self)
         onKeyboardEditingEndsAction?(self)

+ 96 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Controls/InputBarSendButton.swift

@@ -0,0 +1,96 @@
+//
+//  InputBarSendButton.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/18/17.
+//
+
+import UIKit
+
+open class InputBarSendButton: InputBarButtonItem {
+
+    /// A flag indicating the animation state of the `InputBarSendButton`
+    open private(set) var isAnimating: Bool = false
+
+    /// Accessor to modify the color of the activity view
+    open var activityViewColor: UIColor! {
+        get {
+            return activityView.color
+        }
+        set {
+            activityView.color = newValue
+        }
+    }
+
+    private let activityView: UIActivityIndicatorView = {
+        let view = UIActivityIndicatorView(style: .gray)
+        view.isUserInteractionEnabled = false
+        view.isHidden = true
+        return view
+    }()
+
+    public convenience init() {
+        self.init(frame: .zero)
+    }
+
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        setupSendButton()
+    }
+
+    required public init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        setupSendButton()
+    }
+
+    private func setupSendButton() {
+        addSubview(activityView)
+    }
+
+    open override func layoutSubviews() {
+        super.layoutSubviews()
+        activityView.frame = bounds
+    }
+
+    /// Starts the animation of the activity view, hiding other elements
+    open func startAnimating() {
+        guard !isAnimating else { return }
+        defer { isAnimating = true }
+        activityView.startAnimating()
+        activityView.isHidden = false
+        // Setting isHidden doesn't hide the elements
+        titleLabel?.alpha = 0
+        imageView?.layer.transform = CATransform3DMakeScale(0.0, 0.0, 0.0)
+    }
+
+    /// Stops the animation of the activity view, shows other elements
+    open func stopAnimating() {
+        guard isAnimating else { return }
+        defer { isAnimating = false }
+        activityView.stopAnimating()
+        activityView.isHidden = true
+        titleLabel?.alpha = 1
+        imageView?.layer.transform = CATransform3DIdentity
+    }
+
+}

+ 98 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/NSMutableAttributedString+Extensions.swift

@@ -0,0 +1,98 @@
+//
+//  NSMutableAttributedString+Extensions.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/25/17.
+//
+
+import UIKit
+
+internal extension NSMutableAttributedString {
+ 
+    @discardableResult
+    func bold(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor = .black) -> NSMutableAttributedString {
+        let attrs: [NSAttributedString.Key:AnyObject] = [
+            NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: fontSize),
+            NSAttributedString.Key.foregroundColor : textColor
+        ]
+        let boldString = NSMutableAttributedString(string: text, attributes: attrs)
+        self.append(boldString)
+        return self
+    }
+    
+    @discardableResult
+    func medium(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor = .black) -> NSMutableAttributedString {
+        let attrs: [NSAttributedString.Key:AnyObject] = [
+            NSAttributedString.Key.font : UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight.medium),
+            NSAttributedString.Key.foregroundColor : textColor
+        ]
+        let mediumString = NSMutableAttributedString(string: text, attributes: attrs)
+        self.append(mediumString)
+        return self
+    }
+    
+    @discardableResult
+    func italic(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor = .black) -> NSMutableAttributedString {
+        let attrs: [NSAttributedString.Key:AnyObject] = [
+            NSAttributedString.Key.font : UIFont.italicSystemFont(ofSize: fontSize),
+            NSAttributedString.Key.foregroundColor : textColor
+        ]
+        let italicString = NSMutableAttributedString(string: text, attributes: attrs)
+        self.append(italicString)
+        return self
+    }
+    
+    @discardableResult
+    func normal(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor = .black) -> NSMutableAttributedString {
+        let attrs:[NSAttributedString.Key:AnyObject] = [
+            NSAttributedString.Key.font : UIFont.systemFont(ofSize: fontSize),
+            NSAttributedString.Key.foregroundColor : textColor
+        ]
+        let normal =  NSMutableAttributedString(string: text, attributes: attrs)
+        self.append(normal)
+        return self
+    }
+
+}
+
+internal extension NSAttributedString {
+
+    func replacingCharacters(in range: NSRange, with attributedString: NSAttributedString) -> NSMutableAttributedString {
+        let ns = NSMutableAttributedString(attributedString: self)
+        ns.replaceCharacters(in: range, with: attributedString)
+        return ns
+    }
+    
+    static func += (lhs: inout NSAttributedString, rhs: NSAttributedString) {
+        let ns = NSMutableAttributedString(attributedString: lhs)
+        ns.append(rhs)
+        lhs = ns
+    }
+    
+    static func + (lhs: NSAttributedString, rhs: NSAttributedString) -> NSAttributedString {
+        let ns = NSMutableAttributedString(attributedString: lhs)
+        ns.append(rhs)
+        return NSAttributedString(attributedString: ns)
+    }
+    
+}

+ 89 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/NSNotification+Extensions.swift

@@ -0,0 +1,89 @@
+//
+//  NSNotification+Extensions.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/25/17.
+//
+
+import UIKit
+
+internal extension NSNotification {
+    
+    var event: KeyboardEvent {
+        switch self.name {
+        case UIResponder.keyboardWillShowNotification:
+            return .willShow
+        case UIResponder.keyboardDidShowNotification:
+            return .didShow
+        case UIResponder.keyboardWillHideNotification:
+            return .willHide
+        case UIResponder.keyboardDidHideNotification:
+            return .didHide
+        case UIResponder.keyboardWillChangeFrameNotification:
+            return .willChangeFrame
+        case UIResponder.keyboardDidChangeFrameNotification:
+            return .didChangeFrame
+        default:
+            return .unknown
+        }
+    }
+    
+    var timeInterval: TimeInterval? {
+        guard let value = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber else { return nil }
+        return TimeInterval(truncating: value)
+    }
+    
+    var animationCurve: UIView.AnimationCurve? {
+        guard let index = (userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.intValue else { return nil }
+        guard index >= 0 && index <= 3 else { return .linear }
+        return UIView.AnimationCurve.init(rawValue: index) ?? .linear
+    }
+    
+    var animationOptions: UIView.AnimationOptions {
+        guard let curve = animationCurve else { return [] }
+        switch curve {
+        case .easeIn:
+            return .curveEaseIn
+        case .easeOut:
+            return .curveEaseOut
+        case .easeInOut:
+            return .curveEaseInOut
+        case .linear:
+            return .curveLinear
+        }
+    }
+    
+    var startFrame: CGRect? {
+        return (userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
+    }
+    
+    var endFrame: CGRect? {
+        return (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
+    }
+    
+    var isForCurrentApp: Bool? {
+        return (userInfo?[UIResponder.keyboardIsLocalUserInfoKey] as? NSNumber)?.boolValue
+    }
+    
+}
+

+ 17 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/String+Extensions.swift

@@ -0,0 +1,17 @@
+//
+//  String+Extensions.swift
+//  InputBarAccessoryView
+//
+//  Created by Ryan Nystrom on 12/22/17.
+//  Modified by Nathan Tannar on 09/18/18
+//  Copyright © 2017 Ryan Nystrom. All rights reserved.
+//
+
+import Foundation
+
+internal extension Character {
+    
+    static var space: Character {
+        return " "
+    }
+}

+ 64 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/UITextView+Extensions.swift

@@ -0,0 +1,64 @@
+//
+//  UITextView+Extensions.swift
+//  InputBarAccessoryView
+//
+//  Created by Nathan Tannar on 09/18/18.
+//  Copyright © 2018 Nathan Tannar. All rights reserved.
+//
+
+import UIKit
+
+internal extension UITextView {
+
+    typealias Match = (prefix: String, word: String, range: NSRange)
+    
+    func find(prefixes: Set<String>, with delimiterSet: CharacterSet) -> Match? {
+        guard prefixes.count > 0 else { return nil }
+
+        for prefix in prefixes {
+            if let match = find(prefix: prefix, with: delimiterSet) {
+                return match
+            }
+        }
+        return nil
+    }
+    
+    func find(prefix: String, with delimiterSet: CharacterSet) -> Match? {
+        guard !prefix.isEmpty else { return nil }
+        guard let caretRange = self.caretRange else { return nil }
+        guard let cursorRange = Range(caretRange, in: text) else { return nil }
+        
+        let leadingText = text[..<cursorRange.upperBound]
+        var prefixStartIndex: String.Index!
+        for (i, char) in prefix.enumerated() {
+            guard let index = leadingText.lastIndex(of: char) else { return nil }
+            if i == 0 {
+                prefixStartIndex = index
+            } else if index.encodedOffset == prefixStartIndex.encodedOffset + 1 {
+                prefixStartIndex = index
+            } else {
+                return nil
+            }
+        }
+
+        let wordRange = prefixStartIndex..<cursorRange.upperBound
+        let word = leadingText[wordRange]
+        
+        let location = wordRange.lowerBound.encodedOffset
+        let length = wordRange.upperBound.encodedOffset - location
+        let range = NSRange(location: location, length: length)
+        
+        return (String(prefix), String(word), range)
+    }
+
+    var caretRange: NSRange? {
+        guard let selectedRange = self.selectedTextRange else { return nil }
+        return NSRange(
+            location: offset(from: beginningOfDocument, to: selectedRange.start),
+            length: offset(from: selectedRange.start, to: selectedRange.end)
+        )
+    }
+    
+}
+
+

+ 96 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Extensions/UIView+AutoLayout.swift

@@ -0,0 +1,96 @@
+//
+//  UIView+Autolayout.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 2/12/17.
+//
+
+import UIKit
+
+internal extension UIView {
+    
+    func fillSuperview() {
+        guard let superview = self.superview else {
+            return
+        }
+        translatesAutoresizingMaskIntoConstraints = false
+        leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true
+        rightAnchor.constraint(equalTo: superview.rightAnchor).isActive = true
+        topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
+        bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
+    }
+
+    @discardableResult
+    func addConstraints(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
+        
+        if self.superview == nil {
+            return []
+        }
+        translatesAutoresizingMaskIntoConstraints = false
+        
+        var constraints = [NSLayoutConstraint]()
+        
+        if let top = top {
+            let constraint = topAnchor.constraint(equalTo: top, constant: topConstant)
+            constraint.identifier = "top"
+            constraints.append(constraint)
+        }
+        
+        if let left = left {
+            let constraint = leftAnchor.constraint(equalTo: left, constant: leftConstant)
+            constraint.identifier = "left"
+            constraints.append(constraint)
+        }
+        
+        if let bottom = bottom {
+            let constraint = bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant)
+            constraint.identifier = "bottom"
+            constraints.append(constraint)
+        }
+        
+        if let right = right {
+            let constraint = rightAnchor.constraint(equalTo: right, constant: -rightConstant)
+            constraint.identifier = "right"
+            constraints.append(constraint)
+        }
+        
+        if widthConstant > 0 {
+            let constraint = widthAnchor.constraint(equalToConstant: widthConstant)
+            constraint.identifier = "width"
+            constraints.append(constraint)
+        }
+        
+        if heightConstant > 0 {
+            let constraint = heightAnchor.constraint(equalToConstant: heightConstant)
+            constraint.identifier = "height"
+            constraints.append(constraint)
+        }
+        
+        constraints.forEach { $0.isActive = true }
+        return constraints
+    }
+    
+    func removeAllConstraints() {
+        constraints.forEach { removeConstraint($0) }
+    }
+}

+ 251 - 125
Pods/MessageInputBar/Sources/MessageInputBar.swift → Pods/InputBarAccessoryView/InputBarAccessoryView/InputBarAccessoryView.swift

@@ -1,47 +1,51 @@
-/*
- MIT License
- 
- Copyright (c) 2017-2018 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.
- */
+//
+//  InputBarAccessoryView.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/18/17.
+//
 
 
 import UIKit
 import UIKit
 
 
 /// A powerful InputAccessoryView ideal for messaging applications
 /// A powerful InputAccessoryView ideal for messaging applications
-open class MessageInputBar: UIView {
+open class InputBarAccessoryView: UIView {
     
     
     // MARK: - Properties
     // MARK: - Properties
     
     
-    /// A delegate to broadcast notifications from the `MessageInputBar`
-    open weak var delegate: MessageInputBarDelegate?
+    /// A delegate to broadcast notifications from the `InputBarAccessoryView`
+    open weak var delegate: InputBarAccessoryViewDelegate?
     
     
-    /// The background UIView anchored to the bottom, left, and right of the MessageInputBar
+    /// The background UIView anchored to the bottom, left, and right of the InputBarAccessoryView
     /// with a top anchor equal to the bottom of the top InputStackView
     /// with a top anchor equal to the bottom of the top InputStackView
     open var backgroundView: UIView = {
     open var backgroundView: UIView = {
         let view = UIView()
         let view = UIView()
         view.translatesAutoresizingMaskIntoConstraints = false
         view.translatesAutoresizingMaskIntoConstraints = false
-        view.backgroundColor = UIColor(red: 247/255, green: 247/255, blue: 247/255, alpha: 1.0)
+        view.backgroundColor = .white
         return view
         return view
     }()
     }()
     
     
-    /// A content UIView that holds the left/right/bottom InputStackViews and InputTextView. Anchored to the bottom of the
+    /// A content UIView that holds the left/right/bottom InputStackViews
+    /// and the middleContentView. Anchored to the bottom of the
     /// topStackView and inset by the padding UIEdgeInsets
     /// topStackView and inset by the padding UIEdgeInsets
     open var contentView: UIView = {
     open var contentView: UIView = {
         let view = UIView()
         let view = UIView()
@@ -54,7 +58,7 @@ open class MessageInputBar: UIView {
      
      
      ## Important Notes ##
      ## Important Notes ##
      1. The blurView is initially not added to the backgroundView to improve performance when not needed. When `isTranslucent` is set to TRUE for the first time the blurView is added and anchored to the `backgroundView`s edge anchors
      1. The blurView is initially not added to the backgroundView to improve performance when not needed. When `isTranslucent` is set to TRUE for the first time the blurView is added and anchored to the `backgroundView`s edge anchors
-     */
+    */
     open var blurView: UIVisualEffectView = {
     open var blurView: UIVisualEffectView = {
         let blurEffect = UIBlurEffect(style: .light)
         let blurEffect = UIBlurEffect(style: .light)
         let view = UIVisualEffectView(effect: blurEffect)
         let view = UIVisualEffectView(effect: blurEffect)
@@ -62,7 +66,7 @@ open class MessageInputBar: UIView {
         return view
         return view
     }()
     }()
     
     
-    /// Determines if the MessageInputBar should have a translucent effect
+    /// Determines if the InputBarAccessoryView should have a translucent effect
     open var isTranslucent: Bool = false {
     open var isTranslucent: Bool = false {
         didSet {
         didSet {
             if isTranslucent && blurView.superview == nil {
             if isTranslucent && blurView.superview == nil {
@@ -70,12 +74,12 @@ open class MessageInputBar: UIView {
                 blurView.fillSuperview()
                 blurView.fillSuperview()
             }
             }
             blurView.isHidden = !isTranslucent
             blurView.isHidden = !isTranslucent
-            let color: UIColor = backgroundView.backgroundColor ?? UIColor(red: 247/255, green: 247/255, blue: 247/255, alpha: 1.0)
-            backgroundView.backgroundColor = isTranslucent ? color.withAlphaComponent(0.75) : color.withAlphaComponent(1.0)
+            let color: UIColor = backgroundView.backgroundColor ?? .white
+            backgroundView.backgroundColor = isTranslucent ? color.withAlphaComponent(0.75) : color
         }
         }
     }
     }
-    
-    /// A SeparatorLine that is anchored at the top of the MessageInputBar with a height of 1
+
+    /// A SeparatorLine that is anchored at the top of the InputBarAccessoryView
     public let separatorLine = SeparatorLine()
     public let separatorLine = SeparatorLine()
     
     
     /**
     /**
@@ -115,38 +119,78 @@ open class MessageInputBar: UIView {
      2. It's spacing is initially set to 15
      2. It's spacing is initially set to 15
      */
      */
     public let bottomStackView = InputStackView(axis: .horizontal, spacing: 15)
     public let bottomStackView = InputStackView(axis: .horizontal, spacing: 15)
+
+    /**
+     The main view component of the InputBarAccessoryView
+
+     The default value is the `InputTextView`.
+
+     ## Important Notes ##
+     1. This view should self-size with constraints or an
+        intrinsicContentSize to auto-size the InputBarAccessoryView
+     2. Override with `setMiddleContentView(view: UIView?, animated: Bool)`
+     */
+    public private(set) weak var middleContentView: UIView?
+
+    /// A view to wrap the `middleContentView` inside
+    private let middleContentViewWrapper: UIView = {
+        let view = UIView()
+        view.translatesAutoresizingMaskIntoConstraints = false
+        return view
+    }()
     
     
     /// The InputTextView a user can input a message in
     /// The InputTextView a user can input a message in
     open lazy var inputTextView: InputTextView = { [weak self] in
     open lazy var inputTextView: InputTextView = { [weak self] in
         let inputTextView = InputTextView()
         let inputTextView = InputTextView()
         inputTextView.translatesAutoresizingMaskIntoConstraints = false
         inputTextView.translatesAutoresizingMaskIntoConstraints = false
-        inputTextView.messageInputBar = self
+        inputTextView.inputBarAccessoryView = self
         return inputTextView
         return inputTextView
     }()
     }()
-
+    
     /// A InputBarButtonItem used as the send button and initially placed in the rightStackView
     /// A InputBarButtonItem used as the send button and initially placed in the rightStackView
-    open var sendButton: InputBarButtonItem = {
-        return InputBarButtonItem()
+    open var sendButton: InputBarSendButton = {
+        return InputBarSendButton()
             .configure {
             .configure {
                 $0.setSize(CGSize(width: 52, height: 36), animated: false)
                 $0.setSize(CGSize(width: 52, height: 36), animated: false)
                 $0.isEnabled = false
                 $0.isEnabled = false
                 $0.title = "Send"
                 $0.title = "Send"
                 $0.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .bold)
                 $0.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .bold)
             }.onTouchUpInside {
             }.onTouchUpInside {
-                $0.messageInputBar?.didSelectSendButton()
+                $0.inputBarAccessoryView?.didSelectSendButton()
         }
         }
     }()
     }()
+
+    /**
+     The anchor contants used to add horizontal inset from the InputBarAccessoryView and the
+     window. By default, an `inputAccessoryView` spans the entire width of the UIWindow. You
+     can manage these insets if you wish to implement designs that do not have the bar spanning
+     the entire width.
+
+     ## Important Notes ##
+
+     USE AT YOUR OWN RISK
+
+     ````
+     H:|-(frameInsets.left)-[InputBarAccessoryView]-(frameInsets.right)-|
+     ````
+
+     */
+    open var frameInsets: HorizontalEdgePadding = .zero {
+        didSet {
+            updateFrameInsets()
+        }
+    }
     
     
     /**
     /**
-     The anchor contants used by the InputStackView's and InputTextView to create padding
-     within the MessageInputBar
+     The anchor constants used by the InputStackView's and InputTextView to create padding
+     within the InputBarAccessoryView
      
      
      ## Important Notes ##
      ## Important Notes ##
      
      
      ````
      ````
      V:|...[InputStackView.top]-(padding.top)-[contentView]-(padding.bottom)-|
      V:|...[InputStackView.top]-(padding.top)-[contentView]-(padding.bottom)-|
      
      
-     H:|-(padding.left)-[contentView]-(padding.right)-|
+     H:|-(frameInsets.left)-(padding.left)-[contentView]-(padding.right)-(frameInsets.right)-|
      ````
      ````
      
      
      */
      */
@@ -163,9 +207,9 @@ open class MessageInputBar: UIView {
      1. The topStackViewPadding.bottom property is not used. Use padding.top
      1. The topStackViewPadding.bottom property is not used. Use padding.top
      
      
      ````
      ````
-     V:|-(topStackViewPadding.top)-[InputStackView.top]-(padding.top)-[InputTextView]-...|
+     V:|-(topStackViewPadding.top)-[InputStackView.top]-(padding.top)-[middleContentView]-...|
      
      
-     H:|-(topStackViewPadding.left)-[InputStackView.top]-(topStackViewPadding.right)-|
+     H:|-(frameInsets.left)-(topStackViewPadding.left)-[InputStackView.top]-(topStackViewPadding.right)-(frameInsets.right)-|
      ````
      ````
      
      
      */
      */
@@ -176,18 +220,18 @@ open class MessageInputBar: UIView {
     }
     }
     
     
     /**
     /**
-     The anchor constants used by the InputStackView
+     The anchor constants used by the middleContentView
      
      
      ````
      ````
-     V:|...-(padding.top)-(textViewPadding.top)-[InputTextView]-(textViewPadding.bottom)-[InputStackView.bottom]-...|
+     V:|...-(padding.top)-(middleContentViewPadding.top)-[middleContentView]-(middleContentViewPadding.bottom)-[InputStackView.bottom]-...|
      
      
-     H:|...-[InputStackView.left]-(textViewPadding.left)-[InputTextView]-(textViewPadding.right)-[InputStackView.right]-...|
+     H:|...-[InputStackView.left]-(middleContentViewPadding.left)-[middleContentView]-(middleContentViewPadding.right)-[InputStackView.right]-...|
      ````
      ````
      
      
      */
      */
-    open var textViewPadding: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8) {
+    open var middleContentViewPadding: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8) {
         didSet {
         didSet {
-            updateTextViewPadding()
+            updateMiddleContentViewPadding()
         }
         }
     }
     }
     
     
@@ -216,14 +260,13 @@ open class MessageInputBar: UIView {
     /// A boolean that determines if the `maxTextViewHeight` should be maintained automatically.
     /// A boolean that determines if the `maxTextViewHeight` should be maintained automatically.
     /// To control the maximum height of the view yourself, set this to `false`.
     /// To control the maximum height of the view yourself, set this to `false`.
     open var shouldAutoUpdateMaxTextViewHeight = true
     open var shouldAutoUpdateMaxTextViewHeight = true
-    
+
     /// The maximum height that the InputTextView can reach.
     /// The maximum height that the InputTextView can reach.
     /// This is set automatically when `shouldAutoUpdateMaxTextViewHeight` is true.
     /// This is set automatically when `shouldAutoUpdateMaxTextViewHeight` is true.
     /// To control the height yourself, make sure to set `shouldAutoUpdateMaxTextViewHeight` to false.
     /// To control the height yourself, make sure to set `shouldAutoUpdateMaxTextViewHeight` to false.
     open var maxTextViewHeight: CGFloat = 0 {
     open var maxTextViewHeight: CGFloat = 0 {
         didSet {
         didSet {
             textViewHeightAnchor?.constant = maxTextViewHeight
             textViewHeightAnchor?.constant = maxTextViewHeight
-            invalidateIntrinsicContentSize()
         }
         }
     }
     }
     
     
@@ -232,6 +275,9 @@ open class MessageInputBar: UIView {
     
     
     /// The height that will fit the current text in the InputTextView based on its current bounds
     /// The height that will fit the current text in the InputTextView based on its current bounds
     public var requiredInputTextViewHeight: CGFloat {
     public var requiredInputTextViewHeight: CGFloat {
+        guard middleContentView == inputTextView else {
+            return middleContentView?.intrinsicContentSize.height ?? 0
+        }
         let maxTextViewSize = CGSize(width: inputTextView.bounds.width, height: .greatestFiniteMagnitude)
         let maxTextViewSize = CGSize(width: inputTextView.bounds.width, height: .greatestFiniteMagnitude)
         return inputTextView.sizeThatFits(maxTextViewSize).height.rounded(.down)
         return inputTextView.sizeThatFits(maxTextViewSize).height.rounded(.down)
     }
     }
@@ -250,9 +296,9 @@ open class MessageInputBar: UIView {
         }
         }
     }
     }
     
     
-    /// Holds the InputPlugin plugins that can be used to extend the functionality of the MessageInputBar
-    open var plugins = [InputPlugin]()
-    
+    /// Holds the InputPlugin plugins that can be used to extend the functionality of the InputBarAccessoryView
+    open var inputPlugins = [InputPlugin]()
+
     /// The InputBarItems held in the leftStackView
     /// The InputBarItems held in the leftStackView
     public private(set) var leftStackViewItems: [InputItem] = []
     public private(set) var leftStackViewItems: [InputItem] = []
     
     
@@ -272,10 +318,10 @@ open class MessageInputBar: UIView {
     public var items: [InputItem] {
     public var items: [InputItem] {
         return [leftStackViewItems, rightStackViewItems, bottomStackViewItems, topStackViewItems, nonStackViewItems].flatMap { $0 }
         return [leftStackViewItems, rightStackViewItems, bottomStackViewItems, topStackViewItems, nonStackViewItems].flatMap { $0 }
     }
     }
-    
+
     // MARK: - Auto-Layout Constraint Sets
     // MARK: - Auto-Layout Constraint Sets
     
     
-    private var textViewLayoutSet: NSLayoutConstraintSet?
+    private var middleContentViewLayoutSet: NSLayoutConstraintSet?
     private var textViewHeightAnchor: NSLayoutConstraint?
     private var textViewHeightAnchor: NSLayoutConstraint?
     private var topStackViewLayoutSet: NSLayoutConstraintSet?
     private var topStackViewLayoutSet: NSLayoutConstraintSet?
     private var leftStackViewLayoutSet: NSLayoutConstraintSet?
     private var leftStackViewLayoutSet: NSLayoutConstraintSet?
@@ -283,7 +329,7 @@ open class MessageInputBar: UIView {
     private var bottomStackViewLayoutSet: NSLayoutConstraintSet?
     private var bottomStackViewLayoutSet: NSLayoutConstraintSet?
     private var contentViewLayoutSet: NSLayoutConstraintSet?
     private var contentViewLayoutSet: NSLayoutConstraintSet?
     private var windowAnchor: NSLayoutConstraint?
     private var windowAnchor: NSLayoutConstraint?
-    private var backgroundViewBottomAnchor: NSLayoutConstraint?
+    private var backgroundViewLayoutSet: NSLayoutConstraintSet?
     
     
     // MARK: - Initialization
     // MARK: - Initialization
     
     
@@ -304,7 +350,16 @@ open class MessageInputBar: UIView {
     deinit {
     deinit {
         NotificationCenter.default.removeObserver(self)
         NotificationCenter.default.removeObserver(self)
     }
     }
-    
+
+    open override func willMove(toSuperview newSuperview: UIView?) {
+        super.willMove(toSuperview: newSuperview)
+        guard newSuperview != nil else {
+            deactivateConstraints()
+            return
+        }
+        activateConstraints()
+    }
+
     open override func didMoveToWindow() {
     open override func didMoveToWindow() {
         super.didMoveToWindow()
         super.didMoveToWindow()
         setupConstraints(to: window)
         setupConstraints(to: window)
@@ -314,29 +369,42 @@ open class MessageInputBar: UIView {
     
     
     /// Sets up the default properties
     /// Sets up the default properties
     open func setup() {
     open func setup() {
-        
+
+        backgroundColor = .white
         autoresizingMask = [.flexibleHeight]
         autoresizingMask = [.flexibleHeight]
         setupSubviews()
         setupSubviews()
         setupConstraints()
         setupConstraints()
         setupObservers()
         setupObservers()
+        setupGestureRecognizers()
     }
     }
     
     
     /// Adds the required notification observers
     /// Adds the required notification observers
     private func setupObservers() {
     private func setupObservers() {
         NotificationCenter.default.addObserver(self,
         NotificationCenter.default.addObserver(self,
-                                               selector: #selector(MessageInputBar.orientationDidChange),
+                                               selector: #selector(InputBarAccessoryView.orientationDidChange),
                                                name: UIDevice.orientationDidChangeNotification, object: nil)
                                                name: UIDevice.orientationDidChangeNotification, object: nil)
         NotificationCenter.default.addObserver(self,
         NotificationCenter.default.addObserver(self,
-                                               selector: #selector(MessageInputBar.inputTextViewDidChange),
+                                               selector: #selector(InputBarAccessoryView.inputTextViewDidChange),
                                                name: UITextView.textDidChangeNotification, object: inputTextView)
                                                name: UITextView.textDidChangeNotification, object: inputTextView)
         NotificationCenter.default.addObserver(self,
         NotificationCenter.default.addObserver(self,
-                                               selector: #selector(MessageInputBar.inputTextViewDidBeginEditing),
+                                               selector: #selector(InputBarAccessoryView.inputTextViewDidBeginEditing),
                                                name: UITextView.textDidBeginEditingNotification, object: inputTextView)
                                                name: UITextView.textDidBeginEditingNotification, object: inputTextView)
         NotificationCenter.default.addObserver(self,
         NotificationCenter.default.addObserver(self,
-                                               selector: #selector(MessageInputBar.inputTextViewDidEndEditing),
+                                               selector: #selector(InputBarAccessoryView.inputTextViewDidEndEditing),
                                                name: UITextView.textDidEndEditingNotification, object: inputTextView)
                                                name: UITextView.textDidEndEditingNotification, object: inputTextView)
     }
     }
     
     
+    /// Adds a UISwipeGestureRecognizer for each direction to the InputTextView
+    private func setupGestureRecognizers() {
+        let directions: [UISwipeGestureRecognizer.Direction] = [.left, .right]
+        for direction in directions {
+            let gesture = UISwipeGestureRecognizer(target: self,
+                                                   action: #selector(InputBarAccessoryView.didSwipeTextView(_:)))
+            gesture.direction = direction
+            inputTextView.addGestureRecognizer(gesture)
+        }
+    }
+    
     /// Adds all of the subviews
     /// Adds all of the subviews
     private func setupSubviews() {
     private func setupSubviews() {
         
         
@@ -344,136 +412,150 @@ open class MessageInputBar: UIView {
         addSubview(topStackView)
         addSubview(topStackView)
         addSubview(contentView)
         addSubview(contentView)
         addSubview(separatorLine)
         addSubview(separatorLine)
-        contentView.addSubview(inputTextView)
+        contentView.addSubview(middleContentViewWrapper)
         contentView.addSubview(leftStackView)
         contentView.addSubview(leftStackView)
         contentView.addSubview(rightStackView)
         contentView.addSubview(rightStackView)
         contentView.addSubview(bottomStackView)
         contentView.addSubview(bottomStackView)
+        middleContentViewWrapper.addSubview(inputTextView)
+        middleContentView = inputTextView
         setStackViewItems([sendButton], forStack: .right, animated: false)
         setStackViewItems([sendButton], forStack: .right, animated: false)
     }
     }
     
     
     /// Sets up the initial constraints of each subview
     /// Sets up the initial constraints of each subview
     private func setupConstraints() {
     private func setupConstraints() {
         
         
-        // The constraints within the MessageInputBar
-        separatorLine.addConstraints(topAnchor, left: leftAnchor, right: rightAnchor, heightConstant: separatorLine.height)
-        backgroundViewBottomAnchor = backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor)
-        backgroundViewBottomAnchor?.isActive = true
-        backgroundView.addConstraints(topStackView.bottomAnchor, left: leftAnchor, right: rightAnchor)
+        // The constraints within the InputBarAccessoryView
+        separatorLine.addConstraints(topAnchor, left: backgroundView.leftAnchor, right: backgroundView.rightAnchor, heightConstant: separatorLine.height)
+
+        backgroundViewLayoutSet = NSLayoutConstraintSet(
+            top: backgroundView.topAnchor.constraint(equalTo: topStackView.bottomAnchor),
+            bottom: backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor),
+            left: backgroundView.leftAnchor.constraint(equalTo: leftAnchor, constant: frameInsets.left),
+            right: backgroundView.rightAnchor.constraint(equalTo: rightAnchor, constant: -frameInsets.right)
+        )
         
         
         topStackViewLayoutSet = NSLayoutConstraintSet(
         topStackViewLayoutSet = NSLayoutConstraintSet(
             top:    topStackView.topAnchor.constraint(equalTo: topAnchor, constant: topStackViewPadding.top),
             top:    topStackView.topAnchor.constraint(equalTo: topAnchor, constant: topStackViewPadding.top),
             bottom: topStackView.bottomAnchor.constraint(equalTo: contentView.topAnchor, constant: -padding.top),
             bottom: topStackView.bottomAnchor.constraint(equalTo: contentView.topAnchor, constant: -padding.top),
-            left:   topStackView.leftAnchor.constraint(equalTo: leftAnchor, constant: topStackViewPadding.left),
-            right:  topStackView.rightAnchor.constraint(equalTo: rightAnchor, constant: -topStackViewPadding.right)
+            left:   topStackView.leftAnchor.constraint(equalTo: leftAnchor, constant: topStackViewPadding.left + frameInsets.left),
+            right:  topStackView.rightAnchor.constraint(equalTo: rightAnchor, constant: -(topStackViewPadding.right + frameInsets.right))
         )
         )
         
         
         contentViewLayoutSet = NSLayoutConstraintSet(
         contentViewLayoutSet = NSLayoutConstraintSet(
             top:    contentView.topAnchor.constraint(equalTo: topStackView.bottomAnchor, constant: padding.top),
             top:    contentView.topAnchor.constraint(equalTo: topStackView.bottomAnchor, constant: padding.top),
             bottom: contentView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -padding.bottom),
             bottom: contentView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -padding.bottom),
-            left:   contentView.leftAnchor.constraint(equalTo: leftAnchor, constant: padding.left),
-            right:  contentView.rightAnchor.constraint(equalTo: rightAnchor, constant: -padding.right)
+            left:   contentView.leftAnchor.constraint(equalTo: leftAnchor, constant: padding.left + frameInsets.left),
+            right:  contentView.rightAnchor.constraint(equalTo: rightAnchor, constant: -(padding.right + frameInsets.right))
         )
         )
         
         
         if #available(iOS 11.0, *) {
         if #available(iOS 11.0, *) {
             // Switch to safeAreaLayoutGuide
             // Switch to safeAreaLayoutGuide
             contentViewLayoutSet?.bottom = contentView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -padding.bottom)
             contentViewLayoutSet?.bottom = contentView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -padding.bottom)
-            contentViewLayoutSet?.left = contentView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor, constant: padding.left)
-            contentViewLayoutSet?.right = contentView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor, constant: -padding.right)
+            contentViewLayoutSet?.left = contentView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor, constant: padding.left + frameInsets.left)
+            contentViewLayoutSet?.right = contentView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor, constant: -(padding.right + frameInsets.right))
             
             
-            topStackViewLayoutSet?.left = topStackView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor, constant: topStackViewPadding.left)
-            topStackViewLayoutSet?.right = topStackView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor, constant: -topStackViewPadding.right)
+            topStackViewLayoutSet?.left = topStackView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor, constant: topStackViewPadding.left + frameInsets.left)
+            topStackViewLayoutSet?.right = topStackView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor, constant: -(topStackViewPadding.right + frameInsets.right))
         }
         }
-        
+
         // Constraints Within the contentView
         // Constraints Within the contentView
-        textViewLayoutSet = NSLayoutConstraintSet(
-            top:    inputTextView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: textViewPadding.top),
-            bottom: inputTextView.bottomAnchor.constraint(equalTo: bottomStackView.topAnchor, constant: -textViewPadding.bottom),
-            left:   inputTextView.leftAnchor.constraint(equalTo: leftStackView.rightAnchor, constant: textViewPadding.left),
-            right:  inputTextView.rightAnchor.constraint(equalTo: rightStackView.leftAnchor, constant: -textViewPadding.right)
+        middleContentViewLayoutSet = NSLayoutConstraintSet(
+            top:    middleContentViewWrapper.topAnchor.constraint(equalTo: contentView.topAnchor, constant: middleContentViewPadding.top),
+            bottom: middleContentViewWrapper.bottomAnchor.constraint(equalTo: bottomStackView.topAnchor, constant: -middleContentViewPadding.bottom),
+            left:   middleContentViewWrapper.leftAnchor.constraint(equalTo: leftStackView.rightAnchor, constant: middleContentViewPadding.left),
+            right:  middleContentViewWrapper.rightAnchor.constraint(equalTo: rightStackView.leftAnchor, constant: -middleContentViewPadding.right)
         )
         )
+
+        inputTextView.fillSuperview()
         maxTextViewHeight = calculateMaxTextViewHeight()
         maxTextViewHeight = calculateMaxTextViewHeight()
         textViewHeightAnchor = inputTextView.heightAnchor.constraint(equalToConstant: maxTextViewHeight)
         textViewHeightAnchor = inputTextView.heightAnchor.constraint(equalToConstant: maxTextViewHeight)
         
         
         leftStackViewLayoutSet = NSLayoutConstraintSet(
         leftStackViewLayoutSet = NSLayoutConstraintSet(
             top:    leftStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
             top:    leftStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
-            bottom: leftStackView.bottomAnchor.constraint(equalTo: inputTextView.bottomAnchor, constant: 0),
+            bottom: leftStackView.bottomAnchor.constraint(equalTo: middleContentViewWrapper.bottomAnchor, constant: 0),
             left:   leftStackView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0),
             left:   leftStackView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0),
             width:  leftStackView.widthAnchor.constraint(equalToConstant: leftStackViewWidthConstant)
             width:  leftStackView.widthAnchor.constraint(equalToConstant: leftStackViewWidthConstant)
         )
         )
         
         
         rightStackViewLayoutSet = NSLayoutConstraintSet(
         rightStackViewLayoutSet = NSLayoutConstraintSet(
             top:    rightStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
             top:    rightStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
-            bottom: rightStackView.bottomAnchor.constraint(equalTo: inputTextView.bottomAnchor, constant: 0),
+            bottom: rightStackView.bottomAnchor.constraint(equalTo: middleContentViewWrapper.bottomAnchor, constant: 0),
             right:  rightStackView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0),
             right:  rightStackView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0),
             width:  rightStackView.widthAnchor.constraint(equalToConstant: rightStackViewWidthConstant)
             width:  rightStackView.widthAnchor.constraint(equalToConstant: rightStackViewWidthConstant)
         )
         )
         
         
         bottomStackViewLayoutSet = NSLayoutConstraintSet(
         bottomStackViewLayoutSet = NSLayoutConstraintSet(
-            top:    bottomStackView.topAnchor.constraint(equalTo: inputTextView.bottomAnchor, constant: textViewPadding.bottom),
+            top:    bottomStackView.topAnchor.constraint(equalTo: middleContentViewWrapper.bottomAnchor, constant: middleContentViewPadding.bottom),
             bottom: bottomStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
             bottom: bottomStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
             left:   bottomStackView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0),
             left:   bottomStackView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0),
             right:  bottomStackView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0)
             right:  bottomStackView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0)
         )
         )
-        activateConstraints()
     }
     }
     
     
-    /// Respect iPhone X safeAreaInsets
+    /// Respect window safeAreaInsets
     /// Adds a constraint to anchor the bottomAnchor of the contentView to the window's safeAreaLayoutGuide.bottomAnchor
     /// Adds a constraint to anchor the bottomAnchor of the contentView to the window's safeAreaLayoutGuide.bottomAnchor
     ///
     ///
     /// - Parameter window: The window to anchor to
     /// - Parameter window: The window to anchor to
     private func setupConstraints(to window: UIWindow?) {
     private func setupConstraints(to window: UIWindow?) {
         if #available(iOS 11.0, *) {
         if #available(iOS 11.0, *) {
-            guard UIScreen.main.nativeBounds.height == 2436 else { return }
             if let window = window {
             if let window = window {
+                guard window.safeAreaInsets.bottom > 0 else { return }
                 windowAnchor?.isActive = false
                 windowAnchor?.isActive = false
                 windowAnchor = contentView.bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1)
                 windowAnchor = contentView.bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1)
                 windowAnchor?.constant = -padding.bottom
                 windowAnchor?.constant = -padding.bottom
                 windowAnchor?.priority = UILayoutPriority(rawValue: 750)
                 windowAnchor?.priority = UILayoutPriority(rawValue: 750)
                 windowAnchor?.isActive = true
                 windowAnchor?.isActive = true
-                backgroundViewBottomAnchor?.constant = 34
+                backgroundViewLayoutSet?.bottom?.constant = window.safeAreaInsets.bottom
             }
             }
         }
         }
     }
     }
     
     
     // MARK: - Constraint Layout Updates
     // MARK: - Constraint Layout Updates
+
+    private func updateFrameInsets() {
+        backgroundViewLayoutSet?.left?.constant = frameInsets.left
+        backgroundViewLayoutSet?.right?.constant = -frameInsets.right
+        updatePadding()
+        updateTopStackViewPadding()
+    }
     
     
     /// Updates the constraint constants that correspond to the padding UIEdgeInsets
     /// Updates the constraint constants that correspond to the padding UIEdgeInsets
     private func updatePadding() {
     private func updatePadding() {
         topStackViewLayoutSet?.bottom?.constant = -padding.top
         topStackViewLayoutSet?.bottom?.constant = -padding.top
         contentViewLayoutSet?.top?.constant = padding.top
         contentViewLayoutSet?.top?.constant = padding.top
-        contentViewLayoutSet?.left?.constant = padding.left
-        contentViewLayoutSet?.right?.constant = -padding.right
+        contentViewLayoutSet?.left?.constant = padding.left + frameInsets.left
+        contentViewLayoutSet?.right?.constant = -(padding.right + frameInsets.right)
         contentViewLayoutSet?.bottom?.constant = -padding.bottom
         contentViewLayoutSet?.bottom?.constant = -padding.bottom
         windowAnchor?.constant = -padding.bottom
         windowAnchor?.constant = -padding.bottom
     }
     }
     
     
-    /// Updates the constraint constants that correspond to the textViewPadding UIEdgeInsets
-    private func updateTextViewPadding() {
-        textViewLayoutSet?.top?.constant = textViewPadding.top
-        textViewLayoutSet?.left?.constant = textViewPadding.left
-        textViewLayoutSet?.right?.constant = -textViewPadding.right
-        textViewLayoutSet?.bottom?.constant = -textViewPadding.bottom
-        bottomStackViewLayoutSet?.top?.constant = textViewPadding.bottom
+    /// Updates the constraint constants that correspond to the middleContentViewPadding UIEdgeInsets
+    private func updateMiddleContentViewPadding() {
+        middleContentViewLayoutSet?.top?.constant = middleContentViewPadding.top
+        middleContentViewLayoutSet?.left?.constant = middleContentViewPadding.left
+        middleContentViewLayoutSet?.right?.constant = -middleContentViewPadding.right
+        middleContentViewLayoutSet?.bottom?.constant = -middleContentViewPadding.bottom
+        bottomStackViewLayoutSet?.top?.constant = middleContentViewPadding.bottom
     }
     }
     
     
     /// Updates the constraint constants that correspond to the topStackViewPadding UIEdgeInsets
     /// Updates the constraint constants that correspond to the topStackViewPadding UIEdgeInsets
     private func updateTopStackViewPadding() {
     private func updateTopStackViewPadding() {
         topStackViewLayoutSet?.top?.constant = topStackViewPadding.top
         topStackViewLayoutSet?.top?.constant = topStackViewPadding.top
-        topStackViewLayoutSet?.left?.constant = topStackViewPadding.left
-        topStackViewLayoutSet?.right?.constant = -topStackViewPadding.right
+        topStackViewLayoutSet?.left?.constant = topStackViewPadding.left + frameInsets.left
+        topStackViewLayoutSet?.right?.constant = -(topStackViewPadding.right + frameInsets.right)
     }
     }
-    
+
     /// Invalidates the view’s intrinsic content size
     /// Invalidates the view’s intrinsic content size
     open override func invalidateIntrinsicContentSize() {
     open override func invalidateIntrinsicContentSize() {
         super.invalidateIntrinsicContentSize()
         super.invalidateIntrinsicContentSize()
         cachedIntrinsicContentSize = calculateIntrinsicContentSize()
         cachedIntrinsicContentSize = calculateIntrinsicContentSize()
         if previousIntrinsicContentSize != cachedIntrinsicContentSize {
         if previousIntrinsicContentSize != cachedIntrinsicContentSize {
-            delegate?.messageInputBar(self, didChangeIntrinsicContentTo: cachedIntrinsicContentSize)
+            delegate?.inputBar(self, didChangeIntrinsicContentTo: cachedIntrinsicContentSize)
             previousIntrinsicContentSize = cachedIntrinsicContentSize
             previousIntrinsicContentSize = cachedIntrinsicContentSize
         }
         }
     }
     }
     
     
-    /// Calculates the correct intrinsicContentSize of the MessageInputBar
+    /// Calculates the correct intrinsicContentSize of the InputBarAccessoryView
     ///
     ///
     /// - Returns: The required intrinsicContentSize
     /// - Returns: The required intrinsicContentSize
     open func calculateIntrinsicContentSize() -> CGSize {
     open func calculateIntrinsicContentSize() -> CGSize {
@@ -484,7 +566,6 @@ open class MessageInputBar: UIView {
                 textViewHeightAnchor?.isActive = true
                 textViewHeightAnchor?.isActive = true
                 inputTextView.isScrollEnabled = true
                 inputTextView.isScrollEnabled = true
                 isOverMaxTextViewHeight = true
                 isOverMaxTextViewHeight = true
-                inputTextView.layoutIfNeeded()
             }
             }
             inputTextViewHeight = maxTextViewHeight
             inputTextViewHeight = maxTextViewHeight
         } else {
         } else {
@@ -497,14 +578,28 @@ open class MessageInputBar: UIView {
         }
         }
         
         
         // Calculate the required height
         // Calculate the required height
-        let totalPadding = padding.top + padding.bottom + topStackViewPadding.top + textViewPadding.top + textViewPadding.bottom
+        let totalPadding = padding.top + padding.bottom + topStackViewPadding.top + middleContentViewPadding.top + middleContentViewPadding.bottom
         let topStackViewHeight = topStackView.arrangedSubviews.count > 0 ? topStackView.bounds.height : 0
         let topStackViewHeight = topStackView.arrangedSubviews.count > 0 ? topStackView.bounds.height : 0
         let bottomStackViewHeight = bottomStackView.arrangedSubviews.count > 0 ? bottomStackView.bounds.height : 0
         let bottomStackViewHeight = bottomStackView.arrangedSubviews.count > 0 ? bottomStackView.bounds.height : 0
         let verticalStackViewHeight = topStackViewHeight + bottomStackViewHeight
         let verticalStackViewHeight = topStackViewHeight + bottomStackViewHeight
         let requiredHeight = inputTextViewHeight + totalPadding + verticalStackViewHeight
         let requiredHeight = inputTextViewHeight + totalPadding + verticalStackViewHeight
-        return CGSize(width: bounds.width, height: requiredHeight)
+        return CGSize(width: UIView.noIntrinsicMetric, height: requiredHeight)
+    }
+
+    open override func layoutIfNeeded() {
+        super.layoutIfNeeded()
+        inputTextView.layoutIfNeeded()
+    }
+
+    open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
+        guard frameInsets.left != 0 || frameInsets.right != 0 else {
+            return super.point(inside: point, with: event)
+        }
+        // Allow touches to pass through base view
+        return subviews.contains {
+            !$0.isHidden && $0.point(inside: convert(point, to: $0), with: event)
+        }
     }
     }
-    
     
     
     /// Returns the max height the InputTextView can grow to based on the UIScreen
     /// Returns the max height the InputTextView can grow to based on the UIScreen
     ///
     ///
@@ -542,7 +637,7 @@ open class MessageInputBar: UIView {
         }
         }
     }
     }
     
     
-    /// Performs layout changes over the main thread
+    /// Performs a layout over the main thread
     ///
     ///
     /// - Parameters:
     /// - Parameters:
     ///   - animated: If the layout should be animated
     ///   - animated: If the layout should be animated
@@ -561,8 +656,9 @@ open class MessageInputBar: UIView {
     
     
     /// Activates the NSLayoutConstraintSet's
     /// Activates the NSLayoutConstraintSet's
     private func activateConstraints() {
     private func activateConstraints() {
+        backgroundViewLayoutSet?.activate()
         contentViewLayoutSet?.activate()
         contentViewLayoutSet?.activate()
-        textViewLayoutSet?.activate()
+        middleContentViewLayoutSet?.activate()
         leftStackViewLayoutSet?.activate()
         leftStackViewLayoutSet?.activate()
         rightStackViewLayoutSet?.activate()
         rightStackViewLayoutSet?.activate()
         bottomStackViewLayoutSet?.activate()
         bottomStackViewLayoutSet?.activate()
@@ -571,16 +667,38 @@ open class MessageInputBar: UIView {
     
     
     /// Deactivates the NSLayoutConstraintSet's
     /// Deactivates the NSLayoutConstraintSet's
     private func deactivateConstraints() {
     private func deactivateConstraints() {
+        backgroundViewLayoutSet?.deactivate()
         contentViewLayoutSet?.deactivate()
         contentViewLayoutSet?.deactivate()
-        textViewLayoutSet?.deactivate()
+        middleContentViewLayoutSet?.deactivate()
         leftStackViewLayoutSet?.deactivate()
         leftStackViewLayoutSet?.deactivate()
         rightStackViewLayoutSet?.deactivate()
         rightStackViewLayoutSet?.deactivate()
         bottomStackViewLayoutSet?.deactivate()
         bottomStackViewLayoutSet?.deactivate()
         topStackViewLayoutSet?.deactivate()
         topStackViewLayoutSet?.deactivate()
     }
     }
+
+    /// Removes the current `middleContentView` and assigns a new one.
+    ///
+    /// WARNING: This will remove the `InputTextView`
+    ///
+    /// - Parameters:
+    ///   - view: New view
+    ///   - animated: If the layout should be animated
+    open func setMiddleContentView(_ view: UIView?, animated: Bool) {
+        middleContentView?.removeFromSuperview()
+        middleContentView = view
+        guard let view = view else { return }
+        middleContentViewWrapper.addSubview(view)
+        view.fillSuperview()
+
+        performLayout(animated) { [weak self] in
+            guard self?.superview != nil else { return }
+            self?.middleContentViewWrapper.layoutIfNeeded()
+            self?.invalidateIntrinsicContentSize()
+        }
+    }
     
     
     /// Removes all of the arranged subviews from the InputStackView and adds the given items.
     /// Removes all of the arranged subviews from the InputStackView and adds the given items.
-    /// Sets the messageInputBar property of the InputBarButtonItem
+    /// Sets the inputBarAccessoryView property of the InputBarButtonItem
     ///
     ///
     /// - Parameters:
     /// - Parameters:
     ///   - items: New InputStackView arranged views
     ///   - items: New InputStackView arranged views
@@ -594,7 +712,7 @@ open class MessageInputBar: UIView {
                 leftStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
                 leftStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
                 leftStackViewItems = items
                 leftStackViewItems = items
                 leftStackViewItems.forEach {
                 leftStackViewItems.forEach {
-                    $0.messageInputBar = self
+                    $0.inputBarAccessoryView = self
                     $0.parentStackViewPosition = position
                     $0.parentStackViewPosition = position
                     if let view = $0 as? UIView {
                     if let view = $0 as? UIView {
                         leftStackView.addArrangedSubview(view)
                         leftStackView.addArrangedSubview(view)
@@ -606,7 +724,7 @@ open class MessageInputBar: UIView {
                 rightStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
                 rightStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
                 rightStackViewItems = items
                 rightStackViewItems = items
                 rightStackViewItems.forEach {
                 rightStackViewItems.forEach {
-                    $0.messageInputBar = self
+                    $0.inputBarAccessoryView = self
                     $0.parentStackViewPosition = position
                     $0.parentStackViewPosition = position
                     if let view = $0 as? UIView {
                     if let view = $0 as? UIView {
                         rightStackView.addArrangedSubview(view)
                         rightStackView.addArrangedSubview(view)
@@ -618,7 +736,7 @@ open class MessageInputBar: UIView {
                 bottomStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
                 bottomStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
                 bottomStackViewItems = items
                 bottomStackViewItems = items
                 bottomStackViewItems.forEach {
                 bottomStackViewItems.forEach {
-                    $0.messageInputBar = self
+                    $0.inputBarAccessoryView = self
                     $0.parentStackViewPosition = position
                     $0.parentStackViewPosition = position
                     if let view = $0 as? UIView {
                     if let view = $0 as? UIView {
                         bottomStackView.addArrangedSubview(view)
                         bottomStackView.addArrangedSubview(view)
@@ -630,7 +748,7 @@ open class MessageInputBar: UIView {
                 topStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
                 topStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
                 topStackViewItems = items
                 topStackViewItems = items
                 topStackViewItems.forEach {
                 topStackViewItems.forEach {
-                    $0.messageInputBar = self
+                    $0.inputBarAccessoryView = self
                     $0.parentStackViewPosition = position
                     $0.parentStackViewPosition = position
                     if let view = $0 as? UIView {
                     if let view = $0 as? UIView {
                         topStackView.addArrangedSubview(view)
                         topStackView.addArrangedSubview(view)
@@ -653,7 +771,7 @@ open class MessageInputBar: UIView {
     ///   - newValue: New widthAnchor constant
     ///   - newValue: New widthAnchor constant
     ///   - animated: If the layout should be animated
     ///   - animated: If the layout should be animated
     open func setLeftStackViewWidthConstant(to newValue: CGFloat, animated: Bool) {
     open func setLeftStackViewWidthConstant(to newValue: CGFloat, animated: Bool) {
-        performLayout(animated) {
+        performLayout(animated) { 
             self.leftStackViewWidthConstant = newValue
             self.leftStackViewWidthConstant = newValue
             self.layoutStackViews([.left])
             self.layoutStackViews([.left])
             guard self.superview?.superview != nil else { return }
             guard self.superview?.superview != nil else { return }
@@ -667,7 +785,7 @@ open class MessageInputBar: UIView {
     ///   - newValue: New widthAnchor constant
     ///   - newValue: New widthAnchor constant
     ///   - animated: If the layout should be animated
     ///   - animated: If the layout should be animated
     open func setRightStackViewWidthConstant(to newValue: CGFloat, animated: Bool) {
     open func setRightStackViewWidthConstant(to newValue: CGFloat, animated: Bool) {
-        performLayout(animated) {
+        performLayout(animated) { 
             self.rightStackViewWidthConstant = newValue
             self.rightStackViewWidthConstant = newValue
             self.layoutStackViews([.right])
             self.layoutStackViews([.right])
             guard self.superview?.superview != nil else { return }
             guard self.superview?.superview != nil else { return }
@@ -711,7 +829,7 @@ open class MessageInputBar: UIView {
         }
         }
         invalidateIntrinsicContentSize()
         invalidateIntrinsicContentSize()
     }
     }
-    
+
     /// Enables/Disables the sendButton based on the InputTextView's text being empty
     /// Enables/Disables the sendButton based on the InputTextView's text being empty
     /// Calls each items `textViewDidChangeAction` method
     /// Calls each items `textViewDidChangeAction` method
     /// Calls the delegates `textViewTextDidChangeTo` method
     /// Calls the delegates `textViewTextDidChangeTo` method
@@ -734,7 +852,7 @@ open class MessageInputBar: UIView {
         let shouldInvalidateIntrinsicContentSize = requiredInputTextViewHeight != inputTextView.bounds.height
         let shouldInvalidateIntrinsicContentSize = requiredInputTextViewHeight != inputTextView.bounds.height
         
         
         items.forEach { $0.textViewDidChangeAction(with: self.inputTextView) }
         items.forEach { $0.textViewDidChangeAction(with: self.inputTextView) }
-        delegate?.messageInputBar(self, textViewTextDidChangeTo: trimmedText)
+        delegate?.inputBar(self, textViewTextDidChangeTo: trimmedText)
         
         
         if shouldInvalidateIntrinsicContentSize {
         if shouldInvalidateIntrinsicContentSize {
             // Prevent un-needed content size invalidation
             // Prevent un-needed content size invalidation
@@ -758,20 +876,28 @@ open class MessageInputBar: UIView {
     
     
     /// Reloads each of the plugins
     /// Reloads each of the plugins
     open func reloadPlugins() {
     open func reloadPlugins() {
-        plugins.forEach { $0.reloadData() }
+        inputPlugins.forEach { $0.reloadData() }
     }
     }
     
     
     /// Invalidates each of the plugins
     /// Invalidates each of the plugins
     open func invalidatePlugins() {
     open func invalidatePlugins() {
-        plugins.forEach { $0.invalidate() }
+        inputPlugins.forEach { $0.invalidate() }
     }
     }
     
     
     // MARK: - User Actions
     // MARK: - User Actions
-
+    
+    /// Calls each items `keyboardSwipeGestureAction` method
+    /// Calls the delegates `didSwipeTextViewWith` method
+    @objc
+    open func didSwipeTextView(_ gesture: UISwipeGestureRecognizer) {
+        items.forEach { $0.keyboardSwipeGestureAction(with: gesture) }
+        delegate?.inputBar(self, didSwipeTextViewWith: gesture)
+    }
+    
     /// Calls the delegates `didPressSendButtonWith` method
     /// Calls the delegates `didPressSendButtonWith` method
     /// Assumes that the InputTextView's text has been set to empty and calls `inputTextViewDidChange()`
     /// Assumes that the InputTextView's text has been set to empty and calls `inputTextViewDidChange()`
     /// Invalidates each of the InputPlugins
     /// Invalidates each of the InputPlugins
     open func didSelectSendButton() {
     open func didSelectSendButton() {
-        delegate?.messageInputBar(self, didPressSendButtonWith: inputTextView.text)
+        delegate?.inputBar(self, didPressSendButtonWith: inputTextView.text)
     }
     }
 }
 }

+ 53 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/KeyboardManager/KeyboardEvent.swift

@@ -0,0 +1,53 @@
+//
+//  KeyboardEvent.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/18/17.
+//
+import Foundation
+
+/// Keyboard events that can happen. Translates directly to `UIKeyboard` notifications from UIKit.
+public enum KeyboardEvent {
+    
+    /// Event raised by UIKit's `.UIKeyboardWillShow`.
+    case willShow
+    
+    /// Event raised by UIKit's `.UIKeyboardDidShow`.
+    case didShow
+    
+    /// Event raised by UIKit's `.UIKeyboardWillShow`.
+    case willHide
+    
+    /// Event raised by UIKit's `.UIKeyboardDidHide`.
+    case didHide
+    
+    /// Event raised by UIKit's `.UIKeyboardWillChangeFrame`.
+    case willChangeFrame
+    
+    /// Event raised by UIKit's `.UIKeyboardDidChangeFrame`.
+    case didChangeFrame
+    
+    /// Non-keyboard based event raised by UIKit
+    case unknown
+    
+}

+ 299 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/KeyboardManager/KeyboardManager.swift

@@ -0,0 +1,299 @@
+//
+//  KeyboardManager.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/18/17.
+//
+
+import UIKit
+
+/// An object that observes keyboard notifications such that event callbacks can be set for each notification
+open class KeyboardManager: NSObject, UIGestureRecognizerDelegate {
+    
+    /// A callback that passes a `KeyboardNotification` as an input
+    public typealias EventCallback = (KeyboardNotification)->Void
+    
+    // MARK: - Properties [Public]
+    
+    /// A weak reference to a view bounded to the top of the keyboard to act as an `InputAccessoryView`
+    /// but kept within the bounds of the `UIViewController`s view
+    open weak var inputAccessoryView: UIView?
+    
+    /// A flag that indicates if a portion of the keyboard is visible on the screen
+    private(set) public var isKeyboardHidden: Bool = true
+    
+    // MARK: - Properties [Private]
+    
+    /// The `NSLayoutConstraintSet` that holds the `inputAccessoryView` to the bottom if its superview
+    private var constraints: NSLayoutConstraintSet?
+    
+    /// A weak reference to a `UIScrollView` that has been attached for interactive keyboard dismissal
+    private weak var scrollView: UIScrollView?
+    
+    /// The `EventCallback` actions for each `KeyboardEvent`. Default value is EMPTY
+    private var callbacks: [KeyboardEvent: EventCallback] = [:]
+    
+    /// The pan gesture that handles dragging on the `scrollView`
+    private var panGesture: UIPanGestureRecognizer?
+
+    /// A cached notification used as a starting point when a user dragging the `scrollView` down
+    /// to interactively dismiss the keyboard
+    private var cachedNotification: KeyboardNotification?
+    
+    // MARK: - Initialization
+    
+    /// Creates a `KeyboardManager` object an binds the view as fake `InputAccessoryView`
+    ///
+    /// - Parameter inputAccessoryView: The view to bind to the top of the keyboard but within its superview
+    public convenience init(inputAccessoryView: UIView) {
+        self.init()
+        self.bind(inputAccessoryView: inputAccessoryView)
+    }
+    
+    /// Creates a `KeyboardManager` object that observes the state of the keyboard
+    public override init() {
+        super.init()
+        addObservers()
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    // MARK: - De-Initialization
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+    
+    // MARK: - Keyboard Observer
+    
+    /// Add an observer for each keyboard notification
+    private func addObservers() {
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(keyboardWillShow(notification:)),
+                                               name: UIResponder.keyboardWillShowNotification,
+                                               object: nil)
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(keyboardDidShow(notification:)),
+                                               name: UIResponder.keyboardDidShowNotification,
+                                               object: nil)
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(keyboardWillHide(notification:)),
+                                               name: UIResponder.keyboardWillHideNotification,
+                                               object: nil)
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(keyboardDidHide(notification:)),
+                                               name: UIResponder.keyboardDidHideNotification,
+                                               object: nil)
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(keyboardWillChangeFrame(notification:)),
+                                               name: UIResponder.keyboardWillChangeFrameNotification,
+                                               object: nil)
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(keyboardDidChangeFrame(notification:)),
+                                               name: UIResponder.keyboardDidChangeFrameNotification,
+                                               object: nil)
+    }
+    
+    // MARK: - Mutate Callback Dictionary
+    
+    /// Sets the `EventCallback` for a `KeyboardEvent`
+    ///
+    /// - Parameters:
+    ///   - event: KeyboardEvent
+    ///   - callback: EventCallback
+    /// - Returns: Self
+    @discardableResult
+    open func on(event: KeyboardEvent, do callback: EventCallback?) -> Self {
+        callbacks[event] = callback
+        return self
+    }
+    
+    /// Constrains the `inputAccessoryView` to the bottom of its superview and sets the
+    /// `.willChangeFrame` and `.willHide` event callbacks such that it mimics an `InputAccessoryView`
+    /// that is bound to the top of the keyboard
+    ///
+    /// - Parameter inputAccessoryView: The view to bind to the top of the keyboard but within its superview
+    /// - Returns: Self
+    @discardableResult
+    open func bind(inputAccessoryView: UIView) -> Self {
+        
+        guard let superview = inputAccessoryView.superview else {
+            fatalError("`inputAccessoryView` must have a superview")
+        }
+        self.inputAccessoryView = inputAccessoryView
+        inputAccessoryView.translatesAutoresizingMaskIntoConstraints = false
+        constraints = NSLayoutConstraintSet(
+            bottom: inputAccessoryView.bottomAnchor.constraint(equalTo: superview.bottomAnchor),
+            left: inputAccessoryView.leftAnchor.constraint(equalTo: superview.leftAnchor),
+            right: inputAccessoryView.rightAnchor.constraint(equalTo: superview.rightAnchor)
+        ).activate()
+        
+        callbacks[.willShow] = { [weak self] (notification) in
+            let keyboardHeight = notification.endFrame.height
+            guard
+                self?.isKeyboardHidden == false,
+                self?.constraints?.bottom?.constant == 0,
+                notification.isForCurrentApp else { return }
+            self?.animateAlongside(notification) {
+                self?.constraints?.bottom?.constant = -keyboardHeight
+                self?.inputAccessoryView?.superview?.layoutIfNeeded()
+            }
+        }
+        callbacks[.willChangeFrame] = { [weak self] (notification) in
+            let keyboardHeight = notification.endFrame.height
+            guard
+                self?.isKeyboardHidden == false,
+                notification.isForCurrentApp else { return }
+            self?.animateAlongside(notification) {
+                self?.constraints?.bottom?.constant = -keyboardHeight
+                self?.inputAccessoryView?.superview?.layoutIfNeeded()
+            }
+        }
+        callbacks[.willHide] = { [weak self] (notification) in
+            guard notification.isForCurrentApp else { return }
+            self?.animateAlongside(notification) { [weak self] in
+                self?.constraints?.bottom?.constant = 0
+                self?.inputAccessoryView?.superview?.layoutIfNeeded()
+            }
+        }
+        return self
+    }
+    
+    /// Adds a `UIPanGestureRecognizer` to the `scrollView` to enable interactive dismissal`
+    ///
+    /// - Parameter scrollView: UIScrollView
+    /// - Returns: Self
+    @discardableResult
+    open func bind(to scrollView: UIScrollView) -> Self {
+        self.scrollView = scrollView
+        self.scrollView?.keyboardDismissMode = .interactive // allows dismissing keyboard interactively
+        let recognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestureRecognizer))
+        recognizer.delegate = self
+        self.panGesture = recognizer
+        self.scrollView?.addGestureRecognizer(recognizer)
+        return self
+    }
+    
+    // MARK: - Keyboard Notifications
+    
+    /// An observer method called last in the lifecycle of a keyboard becoming visible
+    ///
+    /// - Parameter notification: NSNotification
+    @objc
+    open func keyboardDidShow(notification: NSNotification) {
+        guard let keyboardNotification = KeyboardNotification(from: notification) else { return }
+        callbacks[.didShow]?(keyboardNotification)
+    }
+    
+    /// An observer method called last in the lifecycle of a keyboard becoming hidden
+    ///
+    /// - Parameter notification: NSNotification
+    @objc
+    open func keyboardDidHide(notification: NSNotification) {
+        isKeyboardHidden = true
+        guard let keyboardNotification = KeyboardNotification(from: notification) else { return }
+        callbacks[.didHide]?(keyboardNotification)
+    }
+    
+    /// An observer method called third in the lifecycle of a keyboard becoming visible/hidden
+    ///
+    /// - Parameter notification: NSNotification
+    @objc
+    open func keyboardDidChangeFrame(notification: NSNotification) {
+        guard let keyboardNotification = KeyboardNotification(from: notification) else { return }
+        callbacks[.didChangeFrame]?(keyboardNotification)
+        cachedNotification = keyboardNotification
+    }
+    
+    /// An observer method called first in the lifecycle of a keyboard becoming visible/hidden
+    ///
+    /// - Parameter notification: NSNotification
+    @objc
+    open func keyboardWillChangeFrame(notification: NSNotification) {
+        guard let keyboardNotification = KeyboardNotification(from: notification) else { return }
+        callbacks[.willChangeFrame]?(keyboardNotification)
+        cachedNotification = keyboardNotification
+    }
+    
+    /// An observer method called second in the lifecycle of a keyboard becoming visible
+    ///
+    /// - Parameter notification: NSNotification
+    @objc
+    open func keyboardWillShow(notification: NSNotification) {
+        isKeyboardHidden = false
+        guard let keyboardNotification = KeyboardNotification(from: notification) else { return }
+        callbacks[.willShow]?(keyboardNotification)
+    }
+    
+    /// An observer method called second in the lifecycle of a keyboard becoming hidden
+    ///
+    /// - Parameter notification: NSNotification
+    @objc
+    open func keyboardWillHide(notification: NSNotification) {
+        guard let keyboardNotification = KeyboardNotification(from: notification) else { return }
+        callbacks[.willHide]?(keyboardNotification)
+    }
+    
+    // MARK: - Helper Methods
+    
+    private func animateAlongside(_ notification: KeyboardNotification, animations: @escaping ()->Void) {
+        UIView.animate(withDuration: notification.timeInterval, delay: 0, options: [notification.animationOptions, .allowAnimatedContent, .beginFromCurrentState], animations: animations, completion: nil)
+    }
+    
+    // MARK: - UIGestureRecognizerDelegate
+    
+    /// Starts with the cached `KeyboardNotification` and calculates a new `endFrame` based
+    /// on the `UIPanGestureRecognizer` then calls the `.willChangeFrame` `EventCallback` action
+    ///
+    /// - Parameter recognizer: UIPanGestureRecognizer
+    @objc
+    open func handlePanGestureRecognizer(recognizer: UIPanGestureRecognizer) {
+        guard
+            var keyboardNotification = cachedNotification,
+            case .changed = recognizer.state,
+            let view = recognizer.view,
+            let window = UIApplication.shared.windows.first
+            else { return }
+        
+        let location = recognizer.location(in: view)
+        let absoluteLocation = view.convert(location, to: window)
+        var frame = keyboardNotification.endFrame
+        frame.origin.y = max(absoluteLocation.y, window.bounds.height - frame.height)
+        frame.size.height = window.bounds.height - frame.origin.y
+        keyboardNotification.endFrame = frame
+        callbacks[.willChangeFrame]?(keyboardNotification)
+    }
+    
+    /// Only receive a `UITouch` event when the `scrollView`'s keyboard dismiss mode is interactive
+    open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
+        return scrollView?.keyboardDismissMode == .interactive
+    }
+    
+    /// Only recognice simultaneous gestures when its the `panGesture`
+    open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        return gestureRecognizer === panGesture
+    }
+    
+}

+ 66 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/KeyboardManager/KeyboardNotification.swift

@@ -0,0 +1,66 @@
+//
+//  KeyboardNotification.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/18/17.
+//
+
+import UIKit
+
+/// An object containing the key animation properties from NSNotification
+public struct KeyboardNotification {
+    
+    // MARK: - Properties
+    
+    /// The event that triggered the transition
+    public let event: KeyboardEvent
+    
+    /// The animation length the keyboards transition
+    public let timeInterval: TimeInterval
+    
+    /// The animation properties of the keyboards transition
+    public let animationOptions: UIView.AnimationOptions
+    
+    /// iPad supports split-screen apps, this indicates if the notification was for the current app
+    public let isForCurrentApp: Bool
+    
+    /// The keyboards frame at the start of its transition
+    public var startFrame: CGRect
+    
+    /// The keyboards frame at the beginning of its transition
+    public var endFrame: CGRect
+    
+    /// Requires that the `NSNotification` is based on a `UIKeyboard...` event
+    ///
+    /// - Parameter notification: `KeyboardNotification`
+    public init?(from notification: NSNotification) {
+        guard notification.event != .unknown else { return nil }
+        self.event = notification.event
+        self.timeInterval = notification.timeInterval ?? 0.25
+        self.animationOptions = notification.animationOptions
+        self.isForCurrentApp = notification.isForCurrentApp ?? true
+        self.startFrame = notification.startFrame ?? .zero
+        self.endFrame = notification.endFrame ?? .zero
+    }
+    
+}

+ 16 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Models/HorizontalEdgeInsets.swift.swift

@@ -0,0 +1,16 @@
+//
+//  HorizontalEdgePadding.swift
+//  InputBarAccessoryView
+//
+//  Created by Nathan Tannar on 2018-11-07.
+//  Copyright © 2018 Nathan Tannar. All rights reserved.
+//
+
+import CoreGraphics
+
+public struct HorizontalEdgePadding {
+    public let left: CGFloat
+    public let right: CGFloat
+
+    static let zero = HorizontalEdgePadding(left: 0, right: 0)
+}

+ 82 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Models/NSConstraintLayoutSet.swift

@@ -0,0 +1,82 @@
+//
+//  NSConstraintLayoutSet.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/25/17.
+//
+
+import Foundation
+import UIKit
+
+class NSLayoutConstraintSet {
+    
+    var top: NSLayoutConstraint?
+    var bottom: NSLayoutConstraint?
+    var left: NSLayoutConstraint?
+    var right: NSLayoutConstraint?
+    var centerX: NSLayoutConstraint?
+    var centerY: NSLayoutConstraint?
+    var width: NSLayoutConstraint?
+    var height: NSLayoutConstraint?
+    
+    public init(top: NSLayoutConstraint? = nil, bottom: NSLayoutConstraint? = nil,
+                left: NSLayoutConstraint? = nil, right: NSLayoutConstraint? = nil,
+                centerX: NSLayoutConstraint? = nil, centerY: NSLayoutConstraint? = nil,
+                width: NSLayoutConstraint? = nil, height: NSLayoutConstraint? = nil) {
+        self.top = top
+        self.bottom = bottom
+        self.left = left
+        self.right = right
+        self.centerX = centerX
+        self.centerY = centerY
+        self.width = width
+        self.height = height
+    }
+    
+    /// All of the currently configured constraints
+    private var availableConstraints: [NSLayoutConstraint] {
+        #if swift(>=4.1)
+            return [top, bottom, left, right, centerX, centerY, width, height].compactMap {$0}
+        #else
+            return [top, bottom, left, right, centerX, centerY, width, height].flatMap {$0}
+        #endif
+    }
+    
+    /// Activates all of the non-nil constraints
+    ///
+    /// - Returns: Self
+    @discardableResult
+    func activate() -> Self {
+        NSLayoutConstraint.activate(availableConstraints)
+        return self
+    }
+    
+    /// Deactivates all of the non-nil constraints
+    ///
+    /// - Returns: Self
+    @discardableResult
+    func deactivate() -> Self {
+        NSLayoutConstraint.deactivate(availableConstraints)
+        return self
+    }
+}

+ 235 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/AttachmentManager.swift

@@ -0,0 +1,235 @@
+//
+//  AttachmentManager.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import UIKit
+
+open class AttachmentManager: NSObject, InputPlugin {
+    
+    public enum Attachment {
+        case image(UIImage)
+        case url(URL)
+        case data(Data)
+        
+        @available(*, deprecated, message: ".other(AnyObject) has been depricated as of 2.0.0")
+        case other(AnyObject)
+    }
+    
+    // MARK: - Properties [Public]
+    
+    /// A protocol that can recieve notifications from the `AttachmentManager`
+    open weak var delegate: AttachmentManagerDelegate?
+    
+    /// A protocol to passes data to the `AttachmentManager`
+    open weak var dataSource: AttachmentManagerDataSource?
+    
+    open lazy var attachmentView: AttachmentCollectionView = { [weak self] in
+        let attachmentView = AttachmentCollectionView()
+        attachmentView.dataSource = self
+        attachmentView.delegate = self
+        return attachmentView
+    }()
+    
+    /// The attachments that the managers holds
+    private(set) public var attachments = [Attachment]() { didSet { reloadData() } }
+    
+    /// A flag you can use to determine if you want the manager to be always visible
+    open var isPersistent = false { didSet { attachmentView.reloadData() } }
+    
+    /// A flag to determine if the AddAttachmentCell is visible
+    open var showAddAttachmentCell = true { didSet { attachmentView.reloadData() } }
+    
+    /// The color applied to the backgroundColor of the deleteButton in each `AttachmentCell`
+    open var tintColor: UIColor = UIColor(red: 0, green: 0.5, blue: 1, alpha: 1)
+    
+    // MARK: - Initialization
+    
+    public override init() {
+        super.init()
+    }
+    
+    // MARK: - InputPlugin
+    
+    open func reloadData() {
+        attachmentView.reloadData()
+        delegate?.attachmentManager(self, didReloadTo: attachments)
+        delegate?.attachmentManager(self, shouldBecomeVisible: attachments.count > 0 || isPersistent)
+    }
+    
+    /// Invalidates the `AttachmentManagers` session by removing all attachments
+    open func invalidate() {
+        attachments = []
+    }
+    
+    /// Appends the object to the attachments
+    ///
+    /// - Parameter object: The object to append
+    @discardableResult
+    open func handleInput(of object: AnyObject) -> Bool {
+        let attachment: Attachment
+        if let image = object as? UIImage {
+            attachment = .image(image)
+        } else if let url = object as? URL {
+            attachment = .url(url)
+        } else if let data = object as? Data {
+            attachment = .data(data)
+        } else {
+            return false
+        }
+        
+        insertAttachment(attachment, at: attachments.count)
+        return true
+    }
+    
+    // MARK: - API [Public]
+    
+    /// Performs an animated insertion of an attachment at an index
+    ///
+    /// - Parameter index: The index to insert the attachment at
+    open func insertAttachment(_ attachment: Attachment, at index: Int) {
+        
+        attachmentView.performBatchUpdates({
+            self.attachments.insert(attachment, at: index)
+            self.attachmentView.insertItems(at: [IndexPath(row: index, section: 0)])
+        }, completion: { success in
+            self.attachmentView.reloadData()
+            self.delegate?.attachmentManager(self, didInsert: attachment, at: index)
+            self.delegate?.attachmentManager(self, shouldBecomeVisible: self.attachments.count > 0 || self.isPersistent)
+        })
+    }
+    
+    /// Performs an animated removal of an attachment at an index
+    ///
+    /// - Parameter index: The index to remove the attachment at
+    open func removeAttachment(at index: Int) {
+        
+        let attachment = attachments[index]
+        attachmentView.performBatchUpdates({
+            self.attachments.remove(at: index)
+            self.attachmentView.deleteItems(at: [IndexPath(row: index, section: 0)])
+        }, completion: { success in
+            self.attachmentView.reloadData()
+            self.delegate?.attachmentManager(self, didRemove: attachment, at: index)
+            self.delegate?.attachmentManager(self, shouldBecomeVisible: self.attachments.count > 0 || self.isPersistent)
+        })
+    }
+    
+}
+
+extension AttachmentManager: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
+    
+    // MARK: - UICollectionViewDelegate
+    
+    final public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        if indexPath.row == attachments.count {
+            delegate?.attachmentManager(self, didSelectAddAttachmentAt: indexPath.row)
+            delegate?.attachmentManager(self, shouldBecomeVisible: attachments.count > 0 || isPersistent)
+        }
+    }
+    
+    // MARK: - UICollectionViewDataSource
+    
+    final public func numberOfItems(inSection section: Int) -> Int {
+        return 1
+    }
+    
+    final public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return attachments.count + (showAddAttachmentCell ? 1 : 0)
+    }
+    
+    final public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        
+        if indexPath.row == attachments.count && showAddAttachmentCell {
+            return createAttachmentCell(in: collectionView, at: indexPath)
+        }
+        
+        let attachment = attachments[indexPath.row]
+        
+        if let cell = dataSource?.attachmentManager(self, cellFor: attachment, at: indexPath.row) {
+            return cell
+        } else {
+            
+            // Only images are supported by default
+            switch attachment {
+            case .image(let image):
+                guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageAttachmentCell.reuseIdentifier, for: indexPath) as? ImageAttachmentCell else {
+                    fatalError()
+                }
+                cell.attachment = attachment
+                cell.indexPath = indexPath
+                cell.manager = self
+                cell.imageView.image = image
+                cell.imageView.tintColor = tintColor
+                cell.deleteButton.backgroundColor = tintColor
+                return cell
+            default:
+                return collectionView.dequeueReusableCell(withReuseIdentifier: AttachmentCell.reuseIdentifier, for: indexPath) as! AttachmentCell
+            }
+            
+        }
+    }
+    
+    // MARK: - UICollectionViewDelegateFlowLayout
+    
+    final public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        
+        var height = attachmentView.intrinsicContentHeight
+        if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
+            height -= (layout.sectionInset.bottom + layout.sectionInset.top + collectionView.contentInset.top + collectionView.contentInset.bottom)
+        }
+        return CGSize(width: height, height: height)
+    }
+    
+    open func createAttachmentCell(in collectionView: UICollectionView, at indexPath: IndexPath) -> AttachmentCell {
+        
+        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AttachmentCell.reuseIdentifier, for: indexPath) as? AttachmentCell else {
+            fatalError()
+        }
+        cell.deleteButton.isHidden = true
+        // Draw a plus
+        let frame = CGRect(origin: CGPoint(x: cell.bounds.origin.x,
+                                           y: cell.bounds.origin.y),
+                           size: CGSize(width: cell.bounds.width - cell.padding.left - cell.padding.right,
+                                        height: cell.bounds.height - cell.padding.top - cell.padding.bottom))
+        let strokeWidth: CGFloat = 3
+        let length: CGFloat = frame.width / 2
+        let vLayer = CAShapeLayer()
+        vLayer.path = UIBezierPath(roundedRect: CGRect(x: frame.midX - (strokeWidth / 2),
+                                                       y: frame.midY - (length / 2),
+                                                       width: strokeWidth,
+                                                       height: length), cornerRadius: 5).cgPath
+        vLayer.fillColor = UIColor.lightGray.cgColor
+        let hLayer = CAShapeLayer()
+        hLayer.path = UIBezierPath(roundedRect: CGRect(x: frame.midX - (length / 2),
+                                                       y: frame.midY - (strokeWidth / 2),
+                                                       width: length,
+                                                       height: strokeWidth), cornerRadius: 5).cgPath
+        hLayer.fillColor = UIColor.lightGray.cgColor
+        cell.containerView.layer.addSublayer(vLayer)
+        cell.containerView.layer.addSublayer(hLayer)
+        return cell
+    }
+}

+ 41 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Protocols/AttachmentManagerDataSource.swift

@@ -0,0 +1,41 @@
+//
+//  AttachmentManagerDataSource.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/6/17.
+//
+
+import Foundation
+
+/// AttachmentManagerDataSource is a protocol to passes data to the AttachmentManager
+public protocol AttachmentManagerDataSource: AnyObject {
+    
+    /// The AttachmentCell for the attachment that is to be inserted into the AttachmentView
+    ///
+    /// - Parameters:
+    ///   - manager: The AttachmentManager
+    ///   - attachment: The object
+    ///   - index: The index in the AttachmentView
+    /// - Returns: An AttachmentCell
+    func attachmentManager(_ manager: AttachmentManager, cellFor attachment: AttachmentManager.Attachment, at index: Int) -> AttachmentCell
+}

+ 81 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Protocols/AttachmentManagerDelegate.swift

@@ -0,0 +1,81 @@
+//
+//  AttachmentManagerDelegate.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/6/17.
+//
+
+import UIKit
+
+/// AttachmentManagerDelegate is a protocol that can recieve notifications from the AttachmentManager
+public protocol AttachmentManagerDelegate: AnyObject {
+    
+    /// Can be used to determine if the AttachmentManager should be inserted into an InputStackView
+    ///
+    /// - Parameters:
+    ///   - manager: The AttachmentManager
+    ///   - shouldBecomeVisible: If the AttachmentManager should be presented or dismissed
+    func attachmentManager(_ manager: AttachmentManager, shouldBecomeVisible: Bool)
+    
+    
+    /// Notifys when an attachment has been inserted into the AttachmentManager
+    ///
+    /// - Parameters:
+    ///   - manager: The AttachmentManager
+    ///   - attachment: The attachment that was inserted
+    ///   - index: The index of the attachment in the AttachmentManager's attachments array
+    func attachmentManager(_ manager: AttachmentManager, didInsert attachment: AttachmentManager.Attachment, at index: Int)
+    
+    /// Notifys when an attachment has been removed from the AttachmentManager
+    ///
+    /// - Parameters:
+    ///   - manager: The AttachmentManager
+    ///   - attachment: The attachment that was removed
+    ///   - index: The index of the attachment in the AttachmentManager's attachments array
+    func attachmentManager(_ manager: AttachmentManager, didRemove attachment: AttachmentManager.Attachment, at index: Int)
+    
+    /// Notifys when the AttachmentManager was reloaded
+    ///
+    /// - Parameters:
+    ///   - manager: The AttachmentManager
+    ///   - attachments: The AttachmentManager's attachments array
+    func attachmentManager(_ manager: AttachmentManager, didReloadTo attachments: [AttachmentManager.Attachment])
+    
+    /// Notifys when the AddAttachmentCell was selected
+    ///
+    /// - Parameters:
+    ///   - manager: The AttachmentManager
+    ///   - attachments: The index of the AddAttachmentCell
+    func attachmentManager(_ manager: AttachmentManager, didSelectAddAttachmentAt index: Int)
+}
+
+public extension AttachmentManagerDelegate {
+    
+    func attachmentManager(_ manager: AttachmentManager, didInsert attachment: AttachmentManager.Attachment, at index: Int) {}
+    
+    func attachmentManager(_ manager: AttachmentManager, didRemove attachment: AttachmentManager.Attachment, at index: Int) {}
+    
+    func attachmentManager(_ manager: AttachmentManager, didReloadTo attachments: [AttachmentManager.Attachment]) {}
+    
+    func attachmentManager(_ manager: AttachmentManager, didSelectAddAttachmentAt index: Int) {}
+}

+ 132 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Views/AttachmentCell.swift

@@ -0,0 +1,132 @@
+//
+//  AttachmentCell.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import UIKit
+
+open class AttachmentCell: UICollectionViewCell {
+    
+    // MARK: - Properties
+    
+    class var reuseIdentifier: String {
+        return "AttachmentCell"
+    }
+    
+    public let containerView: UIView = {
+        let view = UIView()
+        view.translatesAutoresizingMaskIntoConstraints = false
+        view.backgroundColor = .groupTableViewBackground
+        view.layer.cornerRadius = 8
+        view.clipsToBounds = true
+        return view
+    }()
+    
+    open var padding: UIEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5) {
+        didSet {
+            updateContainerPadding()
+        }
+    }
+    
+    open lazy var deleteButton: UIButton = { [weak self] in
+        let button = UIButton()
+        button.setAttributedTitle(NSMutableAttributedString().bold("X", fontSize: 15, textColor: .white), for: .normal)
+        button.setAttributedTitle(NSMutableAttributedString().bold("X", fontSize: 15, textColor: UIColor.white.withAlphaComponent(0.5)), for: .highlighted)
+        button.layer.cornerRadius = 10
+        button.clipsToBounds = true
+        button.backgroundColor = UIColor(red: 0, green: 122/255, blue: 1, alpha: 1)
+        button.addTarget(self, action: #selector(deleteAttachment), for: .touchUpInside)
+        return button
+    }()
+    
+    open var attachment: AttachmentManager.Attachment?
+    
+    open var indexPath: IndexPath?
+    
+    open weak var manager: AttachmentManager?
+    
+    private var containerViewLayoutSet: NSLayoutConstraintSet?
+    
+    // MARK: - Initialization
+    
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        setup()
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        setup()
+    }
+    
+    open override func prepareForReuse() {
+        super.prepareForReuse()
+        indexPath = nil
+        manager = nil
+        attachment = nil
+    }
+    
+    // MARK: - Setup
+    
+    private func setup() {
+        
+        setupSubviews()
+        setupConstraints()
+    }
+    
+    private func setupSubviews() {
+        
+        contentView.addSubview(containerView)
+        contentView.addSubview(deleteButton)
+    }
+
+    private func setupConstraints() {
+        
+        containerViewLayoutSet = NSLayoutConstraintSet(
+            top:    containerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding.top),
+            bottom: containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding.bottom),
+            left:   containerView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: padding.left),
+            right:  containerView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -padding.right)
+        ).activate()
+        deleteButton.addConstraints(contentView.topAnchor, right: contentView.rightAnchor, widthConstant: 20, heightConstant: 20)
+    }
+    
+    private func updateContainerPadding() {
+        
+        containerViewLayoutSet?.top?.constant = padding.top
+        containerViewLayoutSet?.bottom?.constant = -padding.bottom
+        containerViewLayoutSet?.left?.constant = padding.left
+        containerViewLayoutSet?.right?.constant = -padding.right
+    }
+    
+    // MARK: - User Actions
+    
+    @objc
+    func deleteAttachment() {
+        
+        guard let index = indexPath?.row else { return }
+        manager?.removeAttachment(at: index)
+    }
+}

+ 82 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Views/AttachmentsView.swift

@@ -0,0 +1,82 @@
+//
+//  AttachmentCollectionView.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import UIKit
+
+@available(*, deprecated, message: "AttachmentsView has been renamed to AttachmentCollectionView")
+public typealias AttachmentsView = AttachmentCollectionView
+
+open class AttachmentCollectionView: UICollectionView {
+    
+    // MARK: - Properties
+    
+    open var intrinsicContentHeight: CGFloat = 100 {
+        didSet {
+            invalidateIntrinsicContentSize()
+        }
+    }
+    
+    open override var intrinsicContentSize: CGSize {
+        return CGSize(width: 0, height: intrinsicContentHeight)
+    }
+    
+    // MARK: - Initialization
+    
+    public init() {
+        let layout = UICollectionViewFlowLayout()
+        layout.scrollDirection = .horizontal
+        layout.minimumLineSpacing = 0
+        layout.sectionInset.top = 5
+        layout.sectionInset.bottom = 5
+        layout.headerReferenceSize = CGSize(width: 12, height: 0)
+        layout.footerReferenceSize = CGSize(width: 12, height: 0)
+        super.init(frame: .zero, collectionViewLayout: layout)
+        setup()
+    }
+    
+    public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
+        super.init(frame: frame, collectionViewLayout: layout)
+        setup()
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        setup()
+    }
+    
+    // MARK: - Setup
+    
+    private func setup() {
+        
+        backgroundColor = .white
+        alwaysBounceHorizontal = true
+        showsHorizontalScrollIndicator = true
+        setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .vertical)
+        register(AttachmentCell.self, forCellWithReuseIdentifier: AttachmentCell.reuseIdentifier)
+        register(ImageAttachmentCell.self, forCellWithReuseIdentifier: ImageAttachmentCell.reuseIdentifier)
+    }
+}

+ 67 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AttachmentManager/Views/ImageAttachmentCell.swift

@@ -0,0 +1,67 @@
+//
+//  ImageAttachmentCell.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/6/17.
+//
+
+import UIKit
+
+open class ImageAttachmentCell: AttachmentCell {
+    
+    // MARK: - Properties
+    
+    override class var reuseIdentifier: String {
+        return "ImageAttachmentCell"
+    }
+    
+    public let imageView: UIImageView = {
+        let imageView = UIImageView()
+        imageView.contentMode = .scaleAspectFill
+        return imageView
+    }()
+    
+    // MARK: - Initialization
+    
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        setup()
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        setup()
+    }
+    
+    open override func prepareForReuse() {
+        super.prepareForReuse()
+        imageView.image = nil
+    }
+    
+    // MARK: - Setup
+    
+    private func setup() {
+        containerView.addSubview(imageView)
+        imageView.fillSuperview()
+    }
+}

+ 503 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/AutocompleteManager.swift

@@ -0,0 +1,503 @@
+//
+//  AttachmentManager.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import UIKit
+
+public extension NSAttributedString.Key {
+    
+    /// A key used for referencing which substrings were autocompleted
+    /// by InputBarAccessoryView.AutocompleteManager
+    static let autocompleted = NSAttributedString.Key("com.system.autocompletekey")
+    
+    /// A key used for referencing the context of autocompleted substrings
+    /// by InputBarAccessoryView.AutocompleteManager
+    static let autocompletedContext = NSAttributedString.Key("com.system.autocompletekey.context")
+}
+
+open class AutocompleteManager: NSObject, InputPlugin, UITextViewDelegate, UITableViewDelegate, UITableViewDataSource {
+    
+    // MARK: - Properties [Public]
+    
+    /// A protocol that passes data to the `AutocompleteManager`
+    open weak var dataSource: AutocompleteManagerDataSource?
+    
+    /// A protocol that more precisely defines `AutocompleteManager` logic
+    open weak var delegate: AutocompleteManagerDelegate?
+    
+    /// A reference to the `InputTextView` that the `AutocompleteManager` is using
+    private(set) public weak var textView: UITextView?
+    
+    @available(*, deprecated, message: "`inputTextView` has been renamed to `textView` of type `UITextView`")
+    public var inputTextView: InputTextView? { return textView as? InputTextView }
+    
+    /// An ongoing session reference that holds the prefix, range and text to complete with
+    private(set) public var currentSession: AutocompleteSession?
+    
+    /// The `AutocompleteTableView` that renders available autocompletes for the `currentSession`
+    open lazy var tableView: AutocompleteTableView = { [weak self] in
+        let tableView = AutocompleteTableView()
+        tableView.register(AutocompleteCell.self, forCellReuseIdentifier: AutocompleteCell.reuseIdentifier)
+        tableView.separatorStyle = .none
+        tableView.backgroundColor = .white
+        tableView.rowHeight = 44
+        tableView.delegate = self
+        tableView.dataSource = self
+        return tableView
+    }()
+    
+    /// Adds an additional space after the autocompleted text when true.
+    /// Default value is `TRUE`
+    open var appendSpaceOnCompletion = true
+    
+    /// Keeps the prefix typed when text is autocompleted.
+    /// Default value is `TRUE`
+    open var keepPrefixOnCompletion = true
+    
+    /// Allows a single space character to be entered mid autocompletion.
+    ///
+    /// For example, your autocomplete is "Nathan Tannar", the .whitespace deliminater
+    /// set would terminate the session after "Nathan". By setting `maxSpaceCountDuringCompletion`
+    /// the session termination will disregard that number of spaces
+    ///
+    /// Default value is `0`
+    open var maxSpaceCountDuringCompletion: Int = 0
+
+    /// When enabled, autocomplete completions that contain whitespace will be deleted in parts.
+    /// This meands backspacing on "@Nathan Tannar" will result in " Tannar" being removed first
+    /// with a second backspace action required to delete "@Nathan"
+    ///
+    /// Default value is `TRUE`
+    open var deleteCompletionByParts = true
+    
+    /// The default text attributes
+    open var defaultTextAttributes: [NSAttributedString.Key: Any] =
+        [.font: UIFont.preferredFont(forTextStyle: .body), .foregroundColor: UIColor.black]
+    
+    /// The NSAttributedString.Key.paragraphStyle value applied to attributed strings
+    public let paragraphStyle: NSMutableParagraphStyle = {
+        let style = NSMutableParagraphStyle()
+        style.paragraphSpacingBefore = 2
+        style.lineHeightMultiple = 1
+        return style
+    }()
+
+    /// A block that filters the `AutocompleteCompletion`'s sourced
+    /// from the `dataSource`, based on the `AutocompleteSession`.
+    /// The default function requires the `AutocompleteCompletion.text`
+    /// string contains the `AutocompleteSession.filter`
+    /// string ignoring case
+    open var filterBlock: (AutocompleteSession, AutocompleteCompletion) -> (Bool) = { session, completion in completion.text.lowercased().contains(session.filter.lowercased())
+    }
+    
+    // MARK: - Properties [Private]
+    
+    /// The prefices that the manager will recognize
+    public private(set) var autocompletePrefixes = Set<String>()
+    
+    /// The delimiters that the manager will terminate a session with
+    /// The default value is: [.whitespaces, .newlines]
+    public private(set) var autocompleteDelimiterSets: Set<CharacterSet> = [.whitespaces, .newlines]
+    
+    /// The text attributes applied to highlighted substrings for each prefix
+    public private(set) var autocompleteTextAttributes = [String: [NSAttributedString.Key: Any]]()
+    
+    /// A reference to `defaultTextAttributes` that adds the NSAttributedAutocompleteKey
+    private var typingTextAttributes: [NSAttributedString.Key: Any] {
+        var attributes = defaultTextAttributes
+        attributes[.autocompleted] = false
+        attributes[.autocompletedContext] = nil
+        attributes[.paragraphStyle] = paragraphStyle
+        return attributes
+    }
+    
+    /// The current autocomplete text options filtered by the text after the prefix
+    private var currentAutocompleteOptions: [AutocompleteCompletion] {
+        
+        guard let session = currentSession, let completions = dataSource?.autocompleteManager(self, autocompleteSourceFor: session.prefix) else { return [] }
+        guard !session.filter.isEmpty else { return completions }
+        return completions.filter { completion in
+            return filterBlock(session, completion)
+        }
+    }
+    
+    // MARK: - Initialization
+    
+    public init(for textView: UITextView) {
+        super.init()
+        self.textView = textView
+        self.textView?.delegate = self
+    }
+    
+    // MARK: - InputPlugin
+    
+    /// Reloads the InputPlugin's session
+    open func reloadData() {
+
+        var delimiterSet = autocompleteDelimiterSets.reduce(CharacterSet()) { result, set in
+            return result.union(set)
+        }
+        let query = textView?.find(prefixes: autocompletePrefixes, with: delimiterSet)
+        
+        guard let result = query else {
+            if let session = currentSession, session.spaceCounter <= maxSpaceCountDuringCompletion {
+                delimiterSet = delimiterSet.subtracting(.whitespaces)
+                guard let result = textView?.find(prefixes: [session.prefix], with: delimiterSet) else {
+                    unregisterCurrentSession()
+                    return
+                }
+                let wordWithoutPrefix = (result.word as NSString).substring(from: result.prefix.utf16.count)
+                updateCurrentSession(to: wordWithoutPrefix)
+            } else {
+                unregisterCurrentSession()
+            }
+            return
+        }
+        let wordWithoutPrefix = (result.word as NSString).substring(from: result.prefix.utf16.count)
+        guard let session = AutocompleteSession(prefix: result.prefix, range: result.range, filter: wordWithoutPrefix) else { return }
+        guard let currentSession = currentSession else {
+            registerCurrentSession(to: session)
+            return
+        }
+        if currentSession == session {
+            updateCurrentSession(to: wordWithoutPrefix)
+        } else {
+            registerCurrentSession(to: session)
+        }
+    }
+    
+    /// Invalidates the InputPlugin's session
+    open func invalidate() {
+        unregisterCurrentSession()
+    }
+    
+    /// Passes an object into the InputPlugin's session to handle
+    ///
+    /// - Parameter object: A string to append
+    @discardableResult
+    open func handleInput(of object: AnyObject) -> Bool {
+        guard let newText = object as? String, let textView = textView else { return false }
+        let attributedString = NSMutableAttributedString(attributedString: textView.attributedText)
+        let newAttributedString = NSAttributedString(string: newText, attributes: typingTextAttributes)
+        attributedString.append(newAttributedString)
+        textView.attributedText = attributedString
+        reloadData()
+        return true
+    }
+    
+    // MARK: - API [Public]
+    
+    /// Registers a prefix and its the attributes to apply to its autocompleted strings
+    ///
+    /// - Parameters:
+    ///   - prefix: The prefix such as: @, # or !
+    ///   - attributedTextAttributes: The attributes to apply to the NSAttributedString
+    open func register(prefix: String, with attributedTextAttributes: [NSAttributedString.Key:Any]? = nil) {
+        autocompletePrefixes.insert(prefix)
+        autocompleteTextAttributes[prefix] = attributedTextAttributes
+        autocompleteTextAttributes[prefix]?[.paragraphStyle] = paragraphStyle
+    }
+    
+    /// Unregisters a prefix and removes its associated cached attributes
+    ///
+    /// - Parameter prefix: The prefix such as: @, # or !
+    open func unregister(prefix: String) {
+        autocompletePrefixes.remove(prefix)
+        autocompleteTextAttributes[prefix] = nil
+    }
+    
+    /// Registers a CharacterSet as a delimiter
+    ///
+    /// - Parameter delimiterSet: The `CharacterSet` to recognize as a delimiter
+    open func register(delimiterSet set: CharacterSet) {
+        autocompleteDelimiterSets.insert(set)
+    }
+    
+    /// Unregisters a CharacterSet
+    ///
+    /// - Parameter delimiterSet: The `CharacterSet` to recognize as a delimiter
+    open func unregister(delimiterSet set: CharacterSet) {
+        autocompleteDelimiterSets.remove(set)
+    }
+    
+    /// Replaces the current prefix and filter text with the supplied text
+    ///
+    /// - Parameters:
+    ///   - text: The replacement text
+    open func autocomplete(with session: AutocompleteSession) {
+        
+        guard let textView = textView else { return }
+        guard delegate?.autocompleteManager(self, shouldComplete: session.prefix, with: session.filter) != false else { return }
+        
+        // Create a range that overlaps the prefix
+        let prefixLength = session.prefix.utf16.count
+        let insertionRange = NSRange(
+            location: session.range.location + (keepPrefixOnCompletion ? prefixLength : 0),
+            length: session.filter.utf16.count + (!keepPrefixOnCompletion ? prefixLength : 0)
+        )
+        
+        // Transform range
+        guard let range = Range(insertionRange, in: textView.text) else { return }
+        let nsrange = NSRange(range, in: textView.text)
+        
+        // Replace the attributedText with a modified version
+        let autocomplete = session.completion?.text ?? ""
+        insertAutocomplete(autocomplete, at: session, for: nsrange)
+        
+        // Move Cursor to the end of the inserted text
+        let selectedLocation = insertionRange.location + autocomplete.utf16.count + (appendSpaceOnCompletion ? 1 : 0)
+        textView.selectedRange = NSRange(
+            location: selectedLocation,
+            length: 0
+        )
+        
+        // End the session
+        unregisterCurrentSession()
+    }
+    
+    /// Returns an attributed string with bolded characters matching the characters typed in the session
+    ///
+    /// - Parameter session: The `AutocompleteSession` to form an `NSMutableAttributedString` with
+    /// - Returns: An `NSMutableAttributedString`
+    open func attributedText(matching session: AutocompleteSession,
+                             fontSize: CGFloat = 15,
+                             keepPrefix: Bool = true) -> NSMutableAttributedString {
+        
+        guard let completion = session.completion else {
+            return NSMutableAttributedString()
+        }
+        
+        // Bolds the text that currently matches the filter
+        let matchingRange = (completion.text as NSString).range(of: session.filter, options: .caseInsensitive)
+        let attributedString = NSMutableAttributedString().normal(completion.text, fontSize: fontSize)
+        attributedString.addAttributes([.font: UIFont.boldSystemFont(ofSize: fontSize)], range: matchingRange)
+        
+        guard keepPrefix else { return attributedString }
+        let stringWithPrefix = NSMutableAttributedString().normal(String(session.prefix), fontSize: fontSize)
+        stringWithPrefix.append(attributedString)
+        return stringWithPrefix
+    }
+    
+    // MARK: - API [Private]
+    
+    /// Resets the `InputTextView`'s typingAttributes to `defaultTextAttributes`
+    private func preserveTypingAttributes() {
+        textView?.typingAttributes = typingTextAttributes
+    }
+    
+    
+    /// Inserts an autocomplete for a given selection
+    ///
+    /// - Parameters:
+    ///   - autocomplete: The 'String' to autocomplete to
+    ///   - sesstion: The 'AutocompleteSession'
+    ///   - range: The 'NSRange' to insert over
+    private func insertAutocomplete(_ autocomplete: String, at session: AutocompleteSession, for range: NSRange) {
+        
+        guard let textView = textView else { return }
+        
+        // Apply the autocomplete attributes
+        var attrs = autocompleteTextAttributes[session.prefix] ?? defaultTextAttributes
+        attrs[.autocompleted] = true
+        attrs[.autocompletedContext] = session.completion?.context
+        let newString = (keepPrefixOnCompletion ? session.prefix : "") + autocomplete
+        let newAttributedString = NSAttributedString(string: newString, attributes: attrs)
+        
+        // Modify the NSRange to include the prefix length
+        let rangeModifier = keepPrefixOnCompletion ? session.prefix.count : 0
+        let highlightedRange = NSRange(location: range.location - rangeModifier, length: range.length + rangeModifier)
+        
+        // Replace the attributedText with a modified version including the autocompete
+        let newAttributedText = textView.attributedText.replacingCharacters(in: highlightedRange, with: newAttributedString)
+        if appendSpaceOnCompletion {
+            newAttributedText.append(NSAttributedString(string: " ", attributes: typingTextAttributes))
+        }
+        
+        // Set to a blank attributed string to prevent keyboard autocorrect from cloberring the insert
+        textView.attributedText = NSAttributedString()
+
+        textView.attributedText = newAttributedText
+    }
+    
+    /// Initializes a session with a new `AutocompleteSession` object
+    ///
+    /// - Parameters:
+    ///   - session: The session to register
+    private func registerCurrentSession(to session: AutocompleteSession) {
+        
+        guard delegate?.autocompleteManager(self, shouldRegister: session.prefix, at: session.range) != false else { return }
+        currentSession = session
+        layoutIfNeeded()
+        delegate?.autocompleteManager(self, shouldBecomeVisible: true)
+    }
+    
+    /// Updates the session to a new String to filter results with
+    ///
+    /// - Parameters:
+    ///   - filterText: The String to filter `AutocompleteCompletion`s
+    private func updateCurrentSession(to filterText: String) {
+        
+        currentSession?.filter = filterText
+        layoutIfNeeded()
+        delegate?.autocompleteManager(self, shouldBecomeVisible: true)
+    }
+    
+    /// Invalidates the `currentSession` session if it existed
+    private func unregisterCurrentSession() {
+        
+        guard let session = currentSession else { return }
+        guard delegate?.autocompleteManager(self, shouldUnregister: session.prefix) != false else { return }
+        currentSession = nil
+        layoutIfNeeded()
+        delegate?.autocompleteManager(self, shouldBecomeVisible: false)
+    }
+    
+    /// Calls the required methods to relayout the `AutocompleteTableView` in it's superview
+    private func layoutIfNeeded() {
+        
+        tableView.reloadData()
+        
+        // Resize the table to be fit properly in an `InputStackView`
+        tableView.invalidateIntrinsicContentSize()
+        
+        // Layout the table's superview
+        tableView.superview?.layoutIfNeeded()
+    }
+    
+    // MARK: - UITextViewDelegate
+    
+    public func textViewDidChange(_ textView: UITextView) {
+        reloadData()
+    }
+    
+    public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
+        
+        // Ensure that the text to be inserted is not using previous attributes
+        preserveTypingAttributes()
+        
+        if let session = currentSession {
+            let textToReplace = (textView.text as NSString).substring(with: range)
+            let deleteSpaceCount = textToReplace.filter { $0 == .space }.count
+            let insertSpaceCount = text.filter { $0 == .space }.count
+            let spaceCountDiff = insertSpaceCount - deleteSpaceCount
+            session.spaceCounter = spaceCountDiff
+        }
+        
+        let totalRange = NSRange(location: 0, length: textView.attributedText.length)
+        let selectedRange = textView.selectedRange
+        
+        // range.length > 0: Backspace/removing text
+        // range.lowerBound < textView.selectedRange.lowerBound: Ignore trying to delete
+        //      the substring if the user is already doing so
+        // range == selectedRange: User selected a chunk to delete
+        if range.length > 0, range.location < selectedRange.location {
+            
+            // Backspace/removing text
+            let attributes = textView.attributedText.attributes(at: range.location, longestEffectiveRange: nil, in: range)
+            let isAutocompleted = attributes[.autocompleted] as? Bool ?? false
+            
+            if isAutocompleted {
+                textView.attributedText.enumerateAttribute(.autocompleted, in: totalRange, options: .reverse) { _, subrange, stop in
+                    
+                    let intersection = NSIntersectionRange(range, subrange)
+                    guard intersection.length > 0 else { return }
+                    defer { stop.pointee = true }
+
+                    let nothing = NSAttributedString(string: "", attributes: typingTextAttributes)
+
+                    let textToReplace = textView.attributedText.attributedSubstring(from: subrange).string
+                    guard deleteCompletionByParts, let delimiterRange = textToReplace.rangeOfCharacter(from: .whitespacesAndNewlines, options: .backwards, range: Range(subrange, in: textToReplace)) else {
+                        // Replace entire autocomplete
+                        textView.attributedText = textView.attributedText.replacingCharacters(in: subrange, with: nothing)
+                        textView.selectedRange = NSRange(location: subrange.location, length: 0)
+                        return
+                    }
+                    // Delete up to delimiter
+                    let delimiterLocation = delimiterRange.lowerBound.encodedOffset
+                    let length = subrange.length - delimiterLocation
+                    let rangeFromDelimiter = NSRange(location: delimiterLocation + subrange.location, length: length)
+                    textView.attributedText = textView.attributedText.replacingCharacters(in: rangeFromDelimiter, with: nothing)
+                    textView.selectedRange = NSRange(location: subrange.location + delimiterLocation, length: 0)
+                }
+                unregisterCurrentSession()
+                return false
+            }
+        } else if range.length >= 0, range.location < totalRange.length {
+            
+            // Inserting text in the middle of an autocompleted string
+            let attributes = textView.attributedText.attributes(at: range.location, longestEffectiveRange: nil, in: range)
+            let isAutocompleted = attributes[.autocompleted] as? Bool ?? false
+            if isAutocompleted {
+                textView.attributedText.enumerateAttribute(.autocompleted, in: totalRange, options: .reverse) { _, subrange, stop in
+                    
+                    let compareRange = range.length == 0 ? NSRange(location: range.location, length: 1) : range
+                    let intersection = NSIntersectionRange(compareRange, subrange)
+                    guard intersection.length > 0 else { return }
+                    
+                    let mutable = NSMutableAttributedString(attributedString: textView.attributedText)
+                    mutable.setAttributes(typingTextAttributes, range: subrange)
+                    let replacementText = NSAttributedString(string: text, attributes: typingTextAttributes)
+                    textView.attributedText = mutable.replacingCharacters(in: range, with: replacementText)
+                    textView.selectedRange = NSRange(location: range.location + text.count, length: 0)
+                    stop.pointee = true
+                }
+                unregisterCurrentSession()
+                return false
+            }
+        }
+        return true
+    }
+    
+    // MARK: - UITableViewDataSource
+    
+    open func numberOfSections(in tableView: UITableView) -> Int {
+        return 1
+    }
+    
+    open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return currentAutocompleteOptions.count
+    }
+    
+    open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        
+        guard let session = currentSession else { fatalError("Attempted to render a cell for a nil `AutocompleteSession`") }
+        session.completion = currentAutocompleteOptions[indexPath.row]
+        guard let cell = dataSource?.autocompleteManager(self, tableView: tableView, cellForRowAt: indexPath, for: session) else {
+            fatalError("Failed to return a cell from `dataSource: AutocompleteManagerDataSource`")
+        }
+        return cell
+    }
+    
+    // MARK: - UITableViewDelegate
+    
+    open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        
+        guard let session = currentSession else { return }
+        session.completion = currentAutocompleteOptions[indexPath.row]
+        autocomplete(with: session)
+    }
+    
+}

+ 48 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Models/AutocompleteCompletion.swift

@@ -0,0 +1,48 @@
+//
+//  AutocompleteCompletion.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import Foundation
+
+public struct AutocompleteCompletion {
+    
+    // The String to insert/replace upon autocompletion
+    public let text: String
+    
+    // The context of the completion that you may need later when completed
+    public let context: [String: Any]?
+    
+    public init(text: String, context: [String: Any]? = nil) {
+        self.text = text
+        self.context = context
+    }
+    
+    @available(*, deprecated, message: "`displayText` should no longer be used, use `context: [String: Any]` instead")
+    public init(_ text: String, displayText: String) {
+        self.text = text
+        self.context = nil
+    }
+}

+ 52 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Models/AutocompleteSession.swift

@@ -0,0 +1,52 @@
+//
+//  AutocompleteSession.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import Foundation
+
+/// A class containing data on the `AutocompleteManager`'s session
+public class AutocompleteSession {
+    
+    public let prefix: String
+    public let range: NSRange
+    public var filter: String
+    public var completion: AutocompleteCompletion?
+    internal var spaceCounter: Int = 0
+    
+    public init?(prefix: String?, range: NSRange?, filter: String?) {
+        guard let pfx = prefix, let rng = range, let flt = filter else { return nil }
+        self.prefix = pfx
+        self.range = rng
+        self.filter = flt
+    }
+}
+
+extension AutocompleteSession: Equatable {
+
+    public static func == (lhs: AutocompleteSession, rhs: AutocompleteSession) -> Bool {
+        return lhs.prefix == rhs.prefix && lhs.range == rhs.range
+    }
+}

+ 67 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Protocols/AutocompleteManagerDataSource.swift

@@ -0,0 +1,67 @@
+//
+//  AutocompleteManagerDataSource.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/1/17.
+//
+
+import UIKit
+
+/// AutocompleteManagerDataSource is a protocol that passes data to the AutocompleteManager
+public protocol AutocompleteManagerDataSource: AnyObject {
+    
+    /// The autocomplete options for the registered prefix.
+    ///
+    /// - Parameters:
+    ///   - manager: The AutocompleteManager
+    ///   - prefix: The registered prefix
+    /// - Returns: An array of `AutocompleteCompletion` options for the given prefix
+    func autocompleteManager(_ manager: AutocompleteManager, autocompleteSourceFor prefix: String) -> [AutocompleteCompletion]
+    
+    /// The cell to populate the `AutocompleteTableView` with
+    ///
+    /// - Parameters:
+    ///   - manager: The `AttachmentManager` that sources the UITableViewDataSource
+    ///   - tableView: The `AttachmentManager`'s `AutocompleteTableView`
+    ///   - indexPath: The `IndexPath` of the cell
+    ///   - session: The current `Session` of the `AutocompleteManager`
+    /// - Returns: A UITableViewCell to populate the `AutocompleteTableView`
+    func autocompleteManager(_ manager: AutocompleteManager, tableView: UITableView, cellForRowAt indexPath: IndexPath, for session: AutocompleteSession) -> UITableViewCell
+}
+
+public extension AutocompleteManagerDataSource {
+    
+    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("AutocompleteCell is not registered")
+        }
+        
+        cell.textLabel?.attributedText = manager.attributedText(matching: session, fontSize: 13)
+        cell.backgroundColor = .white
+        cell.separatorLine.isHidden = tableView.numberOfRows(inSection: indexPath.section) - 1 == indexPath.row
+        return cell
+        
+    }
+    
+}

+ 82 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Protocols/AutocompleteManagerDelegate.swift

@@ -0,0 +1,82 @@
+//
+//  AutocompleteManagerDelegate.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import UIKit
+
+/// AutocompleteManagerDelegate is a protocol that more precisely define AutocompleteManager logic
+public protocol AutocompleteManagerDelegate: AnyObject {
+    
+    /// Can be used to determine if the AutocompleteManager should be inserted into an InputStackView
+    ///
+    /// - Parameters:
+    ///   - manager: The AutocompleteManager
+    ///   - shouldBecomeVisible: If the AutocompleteManager should be presented or dismissed
+    func autocompleteManager(_ manager: AutocompleteManager, shouldBecomeVisible: Bool)
+    
+    /// Determines if a prefix character should be registered to initialize the auto-complete selection table
+    ///
+    /// - Parameters:
+    ///   - manager: The AutocompleteManager
+    ///   - prefix: The prefix `Character` could be registered
+    ///   - range: The `NSRange` of the prefix in the UITextView managed by the AutocompleteManager
+    /// - Returns: If the prefix should be registered. Default is TRUE
+    func autocompleteManager(_ manager: AutocompleteManager, shouldRegister prefix: String, at range: NSRange) -> Bool
+    
+    /// Determines if a prefix character should be unregistered to de-initialize the auto-complete selection table
+    ///
+    /// - Parameters:
+    ///   - manager: The AutocompleteManager
+    ///   - prefix: The prefix character could be unregistered
+    ///   - range: The range of the prefix in the UITextView managed by the AutocompleteManager
+    /// - Returns: If the prefix should be unregistered. Default is TRUE
+    func autocompleteManager(_ manager: AutocompleteManager, shouldUnregister prefix: String) -> Bool
+    
+    /// Determines if a prefix character can should be autocompleted
+    ///
+    /// - Parameters:
+    ///   - manager: The AutocompleteManager
+    ///   - prefix: The prefix character that is currently registered
+    ///   - text: The text to autocomplete with
+    /// - Returns: If the prefix can be autocompleted. Default is TRUE
+    func autocompleteManager(_ manager: AutocompleteManager, shouldComplete prefix: String, with text: String) -> Bool
+}
+
+public extension AutocompleteManagerDelegate {
+    
+    func autocompleteManager(_ manager: AutocompleteManager, shouldRegister prefix: String, at range: NSRange) -> Bool {
+        return true
+    }
+    
+    func autocompleteManager(_ manager: AutocompleteManager, shouldUnregister prefix: String) -> Bool {
+        return true
+    }
+    
+    func autocompleteManager(_ manager: AutocompleteManager, shouldComplete prefix: String, with text: String) -> Bool {
+        return true
+    }
+}
+

+ 97 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Views/AutocompleteCell.swift

@@ -0,0 +1,97 @@
+//
+//  AutocompleteCell.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import UIKit
+
+open class AutocompleteCell: UITableViewCell {
+    
+    // MARK: - Properties
+    
+    open class var reuseIdentifier: String {
+        return "AutocompleteCell"
+    }
+    
+    /// A boarder line anchored to the top of the view
+    public let separatorLine = SeparatorLine()
+    
+    open var imageViewEdgeInsets: UIEdgeInsets = .zero { didSet { setNeedsLayout() } }
+    
+    // MARK: - Initialization
+    
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
+        setup()
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        setup()
+    }
+    
+    open override func prepareForReuse() {
+        super.prepareForReuse()
+        textLabel?.text = nil
+        detailTextLabel?.text = nil
+        imageView?.image = nil
+        imageViewEdgeInsets = .zero
+        separatorLine.backgroundColor = .lightGray
+        separatorLine.isHidden = false
+    }
+    
+    // MARK: - Setup
+    
+    private func setup() {
+        
+        setupSubviews()
+        setupConstraints()
+    }
+    
+    open func setupSubviews() {
+        
+        addSubview(separatorLine)
+    }
+    
+    open func setupConstraints() {
+        
+        separatorLine.addConstraints(left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, heightConstant: 0.5)
+    }
+    
+    open override func layoutSubviews() {
+        super.layoutSubviews()
+        guard let imageViewFrame = imageView?.frame else { return }
+        let imageViewOrigin = CGPoint(x: imageViewFrame.origin.x + imageViewEdgeInsets.left, y: imageViewFrame.origin.y + imageViewEdgeInsets.top)
+        let imageViewSize = CGSize(width: imageViewFrame.size.width - imageViewEdgeInsets.left - imageViewEdgeInsets.right, height: imageViewFrame.size.height - imageViewEdgeInsets.top - imageViewEdgeInsets.bottom)
+        imageView?.frame = CGRect(origin: imageViewOrigin, size: imageViewSize)
+    }
+    
+    // MARK: - API [Public]
+    
+    @available(*, deprecated, message: "This function has been moved to the `AutocompleteManager`")
+    open func attributedText(matching session: AutocompleteSession) -> NSMutableAttributedString {
+        fatalError("Please use `func attributedText(matching:, fontSize:)` implemented in the `AutocompleteManager`")
+    }
+}

+ 41 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Plugins/AutocompleteManager/Views/AutocompleteTableView.swift

@@ -0,0 +1,41 @@
+//
+//  AutocompleteTableView.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import UIKit
+
+open class AutocompleteTableView: UITableView {
+    
+    /// The max visible rows visible in the autocomplete table before the user has to scroll throught them
+    open var maxVisibleRows = 3 { didSet { invalidateIntrinsicContentSize() } }
+    
+    open override var intrinsicContentSize: CGSize {
+        
+        let rows = numberOfRows(inSection: 0) < maxVisibleRows ? numberOfRows(inSection: 0) : maxVisibleRows
+        return CGSize(width: super.intrinsicContentSize.width, height: (CGFloat(rows) * rowHeight))
+    }
+    
+}

+ 74 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Protocols/InputBarAccessoryViewDelegate.swift

@@ -0,0 +1,74 @@
+//
+//  InputBarAccessoryViewDelegate.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/18/17.
+//
+
+import Foundation
+import UIKit
+
+/// InputBarAccessoryViewDelegate is a protocol that can recieve notifications from the InputBarAccessoryView
+public protocol InputBarAccessoryViewDelegate: AnyObject {
+    
+    /// Called when the default send button has been selected
+    ///
+    /// - Parameters:
+    ///   - inputBar: The InputBarAccessoryView
+    ///   - text: The current text in the InputBarAccessoryView's InputTextView
+    func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String)
+    
+    /// Called when the instrinsicContentSize of the InputBarAccessoryView has changed. Can be used for adjusting content insets
+    /// on other views to make sure the InputBarAccessoryView does not cover up any other view
+    ///
+    /// - Parameters:
+    ///   - inputBar: The InputBarAccessoryView
+    ///   - size: The new instrinsicContentSize
+    func inputBar(_ inputBar: InputBarAccessoryView, didChangeIntrinsicContentTo size: CGSize)
+    
+    /// Called when the InputBarAccessoryView's InputTextView's text has changed. Useful for adding your own logic without the
+    /// need of assigning a delegate or notification
+    ///
+    /// - Parameters:
+    ///   - inputBar: The InputBarAccessoryView
+    ///   - text: The current text in the InputBarAccessoryView's InputTextView
+    func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String)
+    
+    /// Called when a swipe gesture was recognized on the InputBarAccessoryView's InputTextView
+    ///
+    /// - Parameters:
+    ///   - inputBar: The InputBarAccessoryView
+    ///   - gesture: The gesture that was recognized
+    func inputBar(_ inputBar: InputBarAccessoryView, didSwipeTextViewWith gesture: UISwipeGestureRecognizer)
+}
+
+public extension InputBarAccessoryViewDelegate {
+    
+    func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) {}
+    
+    func inputBar(_ inputBar: InputBarAccessoryView, didChangeIntrinsicContentTo size: CGSize) {}
+    
+    func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) {}
+    
+    func inputBar(_ inputBar: InputBarAccessoryView, didSwipeTextViewWith gesture: UISwipeGestureRecognizer) {}
+}

+ 50 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Protocols/InputItem.swift

@@ -0,0 +1,50 @@
+//
+//  InputItem.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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 © 2017-2019 Nathan Tannar. All rights reserved.
+//
+
+import UIKit
+
+/// InputItem is a protocol that links elements to the InputBarAccessoryView to make them reactive
+public protocol InputItem: AnyObject {
+    
+    /// A weak reference to the InputBarAccessoryView. Set when inserted into an InputStackView
+    var inputBarAccessoryView: InputBarAccessoryView? { get set }
+    
+    /// A reference to the InputStackView that the InputItem is contained in. Set when inserted into an InputStackView
+    var parentStackViewPosition: InputStackView.Position? { get set }
+    
+    /// A hook that is called when the InputTextView's text is changed
+    func textViewDidChangeAction(with textView: InputTextView)
+    
+    /// A hook that is called when the InputBarAccessoryView's InputTextView receieves a swipe gesture
+    func keyboardSwipeGestureAction(with gesture: UISwipeGestureRecognizer)
+    
+    /// A hook that is called when the InputTextView is resigned as the first responder
+    func keyboardEditingEndsAction()
+    
+    /// A hook that is called when the InputTextView is made the first responder
+    func keyboardEditingBeginsAction()
+}

+ 43 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Protocols/InputPlugin.swift

@@ -0,0 +1,43 @@
+//
+//  InputPlugin.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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 © 2017-2019 Nathan Tannar. All rights reserved.
+//
+
+import UIKit
+
+/// `InputPlugin` is a protocol that makes integrating plugins to the `InputBarAccessoryView` easy.
+public protocol InputPlugin: AnyObject {
+    
+    /// Should reload the state if the `InputPlugin`
+    func reloadData()
+    
+    /// Should remove any content that the `InputPlugin` is managing
+    func invalidate()
+    
+    /// Should handle the input of data types that an `InputPlugin` manages
+    ///
+    /// - Parameter object: The object to input
+    func handleInput(of object: AnyObject) -> Bool
+}

+ 185 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/RxInputBarAccessoryView/RxInputBarAccessoryView.swift

@@ -0,0 +1,185 @@
+//
+//  RxInputBarAccessoryView.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 9/13/18.
+//
+
+import UIKit
+#if canImport(RxSwift) && canImport(RxCocoa)
+import RxSwift
+import RxCocoa
+
+final class RxInputBarAccessoryViewDelegate:
+    DelegateProxy<InputBarAccessoryView, InputBarAccessoryViewDelegate>,
+    DelegateProxyType,
+InputBarAccessoryViewDelegate {
+
+    let sendText = PublishSubject<String>()
+    let currentText = PublishSubject<String>()
+    let intrinsicContentSize = PublishSubject<CGSize>()
+    let swipeGesture = PublishSubject<UISwipeGestureRecognizer>()
+
+    func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) {
+        sendText.onNext(text)
+    }
+
+    func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) {
+        currentText.onNext(text)
+    }
+
+    func inputBar(_ inputBar: InputBarAccessoryView, didChangeIntrinsicContentTo size: CGSize) {
+        intrinsicContentSize.onNext(size)
+    }
+
+    func inputBar(_ inputBar: InputBarAccessoryView, didSwipeTextViewWith gesture: UISwipeGestureRecognizer) {
+        swipeGesture.onNext(gesture)
+    }
+
+    static func registerKnownImplementations() {
+        register {
+            RxInputBarAccessoryViewDelegate(
+                parentObject: $0,
+                delegateProxy: RxInputBarAccessoryViewDelegate.self
+            )
+        }
+    }
+
+    static func currentDelegate(for object: InputBarAccessoryView) -> InputBarAccessoryViewDelegate? {
+        return object.delegate
+    }
+
+    static func setCurrentDelegate(_ delegate: InputBarAccessoryViewDelegate?, to object: InputBarAccessoryView) {
+        object.delegate = delegate
+    }
+}
+
+extension InputBarAccessoryView {
+    var rx_delegate: RxInputBarAccessoryViewDelegate {
+        return RxInputBarAccessoryViewDelegate.proxy(for: self)
+    }
+}
+
+extension Reactive where Base: InputBarAccessoryView {
+    public var isTranslucent: Binder<Bool> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.isTranslucent = newValue
+        }
+    }
+
+    public var shouldAutoUpdateMaxTextViewHeight: Binder<Bool> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.shouldAutoUpdateMaxTextViewHeight = newValue
+        }
+    }
+
+    public var maxTextViewHeight: Binder<CGFloat> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.maxTextViewHeight = newValue
+        }
+    }
+
+    public var shouldManageSendButtonEnabledState: Binder<Bool> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.shouldManageSendButtonEnabledState = newValue
+        }
+    }
+
+    public var leftStackViewItems: Binder<[InputItem]> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.setStackViewItems(newValue, forStack: .left, animated: false)
+        }
+    }
+
+    public var rightStackViewItems: Binder<[InputItem]> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.setStackViewItems(newValue, forStack: .right, animated: false)
+        }
+    }
+
+    public var topStackViewItems: Binder<[InputItem]> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.setStackViewItems(newValue, forStack: .top, animated: false)
+        }
+    }
+
+    public var bottomStackViewItems: Binder<[InputItem]> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.setStackViewItems(newValue, forStack: .bottom, animated: false)
+        }
+    }
+
+    public var leftStackViewWidthConstant: Binder<CGFloat> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.setLeftStackViewWidthConstant(to: newValue, animated: false)
+        }
+    }
+
+    public var rightStackViewWidthConstant: Binder<CGFloat> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.setRightStackViewWidthConstant(to: newValue, animated: false)
+        }
+    }
+
+    public var shouldForceMaxTextViewHeight: Binder<Bool> {
+        return Binder(base) { inputBar, newValue in
+            inputBar.setShouldForceMaxTextViewHeight(to: newValue, animated: false)
+        }
+    }
+}
+
+extension Reactive where Base: InputBarButtonItem {
+    public var size: Binder<CGSize?> {
+        return Binder(base) { item, newValue in
+            item.setSize(newValue, animated: false)
+        }
+    }
+
+    public var spacing: Binder<InputBarButtonItem.Spacing> {
+        return Binder(base) { item, newValue in
+            item.spacing = newValue
+        }
+    }
+}
+
+extension Reactive where Base: InputBarSendButton {
+    public var isAnimating: Binder<Bool> {
+        return Binder(base) { item, newValue in
+            if newValue {
+                item.startAnimating()
+            } else {
+                item.stopAnimating()
+            }
+        }
+    }
+}
+
+extension Reactive where Base: InputBarViewController {
+    public var isInputBarHidden: Binder<Bool> {
+        return Binder(base) { viewController, newValue in
+            viewController.isInputBarHidden = newValue
+        }
+    }
+}
+
+#endif

+ 91 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Supporting Files/InputBarAccessoryView+Availability.swift

@@ -0,0 +1,91 @@
+//
+//  InputBarAccessoryView+Availability.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 2/12/17.
+//
+
+import UIKit
+
+@available(*, deprecated, message: "InputManager has been renamed to InputPlugin")
+public typealias InputManager = InputPlugin
+
+extension InputPlugin {
+    
+    @available(*, deprecated, message: "`handleInput(object:)` should return a `Bool` if handle was successful or now")
+    func handleInput(of object: AnyObject) {
+        _ = self.handleInput(of: object)
+    }
+}
+
+extension AutocompleteCompletion {
+    
+    // An optional string to display instead of `text`, for example emojis
+    @available(*, deprecated, message: "`displayText` should no longer be used, use `context: [String: Any]` instead")
+    public var displayText: String? {
+        return text
+    }
+}
+
+extension AutocompleteManager {
+    /// If the autocomplete matches should be made by casting the strings to lowercase.
+    /// Default value is `FALSE`
+    /// DEPRICATED; will always return `FALSE`
+    @available(*, deprecated, message: "`isCaseSensitive` was replaced in favour of a more customizable `filterBlock: (String) -> (Bool)`")
+    public var isCaseSensitive: Bool {
+        get { return false }
+        set {
+            if isCaseSensitive {
+                filterBlock = { session, completion in
+                    completion.text.contains(session.filter)
+                }
+            } else {
+                filterBlock = { session, completion in completion.text.lowercased().contains(session.filter.lowercased())
+                }
+            }
+        }
+    }
+}
+
+extension InputBarAccessoryView {
+
+    /**
+     The anchor constants used by the InputStackView
+
+     ````
+     V:|...-(padding.top)-(textViewPadding.top)-[InputTextView]-(textViewPadding.bottom)-[InputStackView.bottom]-...|
+
+     H:|...-[InputStackView.left]-(textViewPadding.left)-[InputTextView]-(textViewPadding.right)-[InputStackView.right]-...|
+     ````
+
+     */
+    @available(*, deprecated, message: "The `InputTextView` now resides in the `middleContentView` and thus this property has been renamed to `middleContentViewPadding`")
+    public var textViewPadding: UIEdgeInsets {
+        get {
+            return middleContentViewPadding
+        }
+        set {
+            middleContentViewPadding = newValue
+        }
+    }
+}

+ 88 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/ViewControllers/InputBarViewController.swift

@@ -0,0 +1,88 @@
+//
+//  InputBarViewController.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 9/13/18.
+//
+
+import UIKit
+
+/// An simple `UIViewController` subclass that is ready to work
+/// with an `inputAccessoryView`
+open class InputBarViewController: UIViewController, InputBarAccessoryViewDelegate {
+
+    /// A powerful InputAccessoryView ideal for messaging applications
+    public let inputBar = InputBarAccessoryView()
+
+    /// A boolean value that when changed will update the `inputAccessoryView`
+    /// of the `InputBarViewController`. When set to `TRUE`, the
+    /// `inputAccessoryView` is set to `nil` and the `inputBar` slides off
+    /// the screen.
+    ///
+    /// The default value is FALSE
+    open var isInputBarHidden: Bool = false {
+        didSet {
+            isInputBarHiddenDidChange()
+        }
+    }
+
+    open override var inputAccessoryView: UIView? {
+        return isInputBarHidden ? nil : inputBar
+    }
+
+    open override var canBecomeFirstResponder: Bool {
+        return !isInputBarHidden
+    }
+
+    open override func viewDidLoad() {
+        super.viewDidLoad()
+        inputBar.delegate = self
+    }
+
+    /// Invoked when `isInputBarHidden` changes to become or
+    /// resign first responder
+    open func isInputBarHiddenDidChange() {
+        if isInputBarHidden, isFirstResponder {
+            resignFirstResponder()
+        } else if !isFirstResponder {
+            becomeFirstResponder()
+        }
+    }
+
+    @discardableResult
+    open override func resignFirstResponder() -> Bool {
+        inputBar.inputTextView.resignFirstResponder()
+        return super.resignFirstResponder()
+    }
+
+    // MARK: - InputBarAccessoryViewDelegate
+
+    open func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) { }
+
+    open func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) { }
+
+    open func inputBar(_ inputBar: InputBarAccessoryView, didChangeIntrinsicContentTo size: CGSize) { }
+
+    open func inputBar(_ inputBar: InputBarAccessoryView, didSwipeTextViewWith gesture: UISwipeGestureRecognizer) { }
+}
+

+ 76 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Views/InputStackView.swift

@@ -0,0 +1,76 @@
+//
+//  InputStackView.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/6/17.
+//
+
+import UIKit
+
+/**
+ A UIStackView that's intended for holding `InputItem`s
+ 
+ ## Important Notes ##
+ 1. Default alignment is .fill
+ 2. Default distribution is .fill
+ 3. The distribution property needs to be based on its arranged subviews intrinsicContentSize so it is not recommended to change it
+ */
+open class InputStackView: UIStackView {
+    
+    /// The stack view position in the InputBarAccessoryView
+    ///
+    /// - left: Left Stack View
+    /// - right: Bottom Stack View
+    /// - bottom: Left Stack View
+    /// - top: Top Stack View
+    public enum Position {
+        case left, right, bottom, top
+    }
+    
+    // MARK: Initialization
+    
+    convenience init(axis: NSLayoutConstraint.Axis, spacing: CGFloat) {
+        self.init(frame: .zero)
+        self.axis = axis
+        self.spacing = spacing
+    }
+    
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        setup()
+    }
+    
+    required public init(coder: NSCoder) {
+        super.init(coder: coder)
+    }
+    
+    // MARK: - Setup
+    
+    /// Sets up the default properties
+    open func setup() {
+        translatesAutoresizingMaskIntoConstraints = false
+        distribution = .fill
+        alignment = .bottom
+    }
+    
+}

+ 62 - 50
Pods/MessageInputBar/Sources/Views/InputTextView.swift → Pods/InputBarAccessoryView/InputBarAccessoryView/Views/InputTextView.swift

@@ -1,37 +1,41 @@
-/*
- MIT License
-
- Copyright (c) 2017-2018 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.
- */
+//
+//  InputTextView.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 8/18/17.
+//
 
 
+import Foundation
 import UIKit
 import UIKit
 
 
 /**
 /**
- A `UITextView` that has a `UILabel` embedded for placeholder text
+ A UITextView that has a UILabel embedded for placeholder text
  
  
  ## Important Notes ##
  ## Important Notes ##
  1. Changing the font, textAlignment or textContainerInset automatically performs the same modifications to the placeholderLabel
  1. Changing the font, textAlignment or textContainerInset automatically performs the same modifications to the placeholderLabel
- 2. Intended to be used in an `MessageInputBar`
- 3. Default placeholder text is "New Message"
- 4. Will pass a pasted image it's `MessageInputBar`'s `InputManager`s
+ 2. Intended to be used in an `InputBarAccessoryView`
+ 3. Default placeholder text is "Aa"
+ 4. Will pass a pasted image it's `InputBarAccessoryView`'s `InputPlugin`s
  */
  */
 open class InputTextView: UITextView {
 open class InputTextView: UITextView {
     
     
@@ -49,7 +53,7 @@ open class InputTextView: UITextView {
         }
         }
     }
     }
     
     
-    /// The images that are currently stored as `NSTextAttachment`'s
+    /// The images that are currently stored as NSTextAttachment's
     open var images: [UIImage] {
     open var images: [UIImage] {
         return parseForAttachedImages()
         return parseForAttachedImages()
     }
     }
@@ -59,20 +63,20 @@ open class InputTextView: UITextView {
     }
     }
     
     
     open var isImagePasteEnabled: Bool = true
     open var isImagePasteEnabled: Bool = true
-    
-    /// A UILabel that holds the `InputTextView`'s placeholder text
+
+    /// A UILabel that holds the InputTextView's placeholder text
     public let placeholderLabel: UILabel = {
     public let placeholderLabel: UILabel = {
         let label = UILabel()
         let label = UILabel()
         label.numberOfLines = 0
         label.numberOfLines = 0
         label.textColor = .lightGray
         label.textColor = .lightGray
-        label.text = "New Message"
+        label.text = "Aa"
         label.backgroundColor = .clear
         label.backgroundColor = .clear
         label.translatesAutoresizingMaskIntoConstraints = false
         label.translatesAutoresizingMaskIntoConstraints = false
         return label
         return label
     }()
     }()
     
     
-    /// The placeholder text that appears when there is no text. The default value is "New Message"
-    open var placeholder: String? = "New Message" {
+    /// The placeholder text that appears when there is no text
+    open var placeholder: String? = "Aa" {
         didSet {
         didSet {
             placeholderLabel.text = placeholder
             placeholderLabel.text = placeholder
         }
         }
@@ -85,28 +89,28 @@ open class InputTextView: UITextView {
         }
         }
     }
     }
     
     
-    /// The `UIEdgeInsets` the placeholderLabel has within the `InputTextView`
-    open var placeholderLabelInsets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 4, bottom: 8, right: 4)  {
+    /// The UIEdgeInsets the placeholderLabel has within the InputTextView
+    open var placeholderLabelInsets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 4, bottom: 8, right: 4) {
         didSet {
         didSet {
             updateConstraintsForPlaceholderLabel()
             updateConstraintsForPlaceholderLabel()
         }
         }
     }
     }
     
     
-    /// The font of the `InputTextView`. When set the placeholderLabel's font is also updated
+    /// The font of the InputTextView. When set the placeholderLabel's font is also updated
     open override var font: UIFont! {
     open override var font: UIFont! {
         didSet {
         didSet {
             placeholderLabel.font = font
             placeholderLabel.font = font
         }
         }
     }
     }
     
     
-    /// The `textAlignment` of the `InputTextView`. When set the placeholderLabel's `textAlignment` is also updated
+    /// The textAlignment of the InputTextView. When set the placeholderLabel's textAlignment is also updated
     open override var textAlignment: NSTextAlignment {
     open override var textAlignment: NSTextAlignment {
         didSet {
         didSet {
             placeholderLabel.textAlignment = textAlignment
             placeholderLabel.textAlignment = textAlignment
         }
         }
     }
     }
     
     
-    /// The textContainerInset of the `InputTextView`. When set the placeholderLabelInsets is also updated
+    /// The textContainerInset of the InputTextView. When set the placeholderLabelInsets is also updated
     open override var textContainerInset: UIEdgeInsets {
     open override var textContainerInset: UIEdgeInsets {
         didSet {
         didSet {
             placeholderLabelInsets = textContainerInset
             placeholderLabelInsets = textContainerInset
@@ -125,12 +129,12 @@ open class InputTextView: UITextView {
         }
         }
     }
     }
     
     
-    /// A weak reference to the `MessageInputBar` that the `InputTextView` is contained within
-    open weak var messageInputBar: MessageInputBar?
+    /// A weak reference to the InputBarAccessoryView that the InputTextView is contained within
+    open weak var inputBarAccessoryView: InputBarAccessoryView?
     
     
     /// The constraints of the placeholderLabel
     /// The constraints of the placeholderLabel
     private var placeholderLabelConstraintSet: NSLayoutConstraintSet?
     private var placeholderLabelConstraintSet: NSLayoutConstraintSet?
-    
+ 
     // MARK: - Initializers
     // MARK: - Initializers
     
     
     public convenience init() {
     public convenience init() {
@@ -166,7 +170,7 @@ open class InputTextView: UITextView {
         setupPlaceholderLabel()
         setupPlaceholderLabel()
         setupObservers()
         setupObservers()
     }
     }
-
+    
     /// Adds the placeholderLabel to the view and sets up its initial constraints
     /// Adds the placeholderLabel to the view and sets up its initial constraints
     private func setupPlaceholderLabel() {
     private func setupPlaceholderLabel() {
 
 
@@ -184,7 +188,8 @@ open class InputTextView: UITextView {
         placeholderLabelConstraintSet?.activate()
         placeholderLabelConstraintSet?.activate()
     }
     }
     
     
-    /// Adds the required notification observers
+    /// Adds a notification for .UITextViewTextDidChange to detect when the placeholderLabel
+    /// should be hidden or shown
     private func setupObservers() {
     private func setupObservers() {
         
         
         NotificationCenter.default.addObserver(self,
         NotificationCenter.default.addObserver(self,
@@ -194,7 +199,7 @@ open class InputTextView: UITextView {
                                                selector: #selector(InputTextView.textViewTextDidChange),
                                                selector: #selector(InputTextView.textViewTextDidChange),
                                                name: UITextView.textDidChangeNotification, object: nil)
                                                name: UITextView.textDidChangeNotification, object: nil)
     }
     }
-    
+
     /// Updates the placeholderLabels constraint constants to match the placeholderLabelInsets
     /// Updates the placeholderLabels constraint constants to match the placeholderLabelInsets
     private func updateConstraintsForPlaceholderLabel() {
     private func updateConstraintsForPlaceholderLabel() {
 
 
@@ -204,8 +209,7 @@ open class InputTextView: UITextView {
         placeholderLabelConstraintSet?.right?.constant = -placeholderLabelInsets.right
         placeholderLabelConstraintSet?.right?.constant = -placeholderLabelInsets.right
     }
     }
     
     
-    
-    // MARK: - Notification
+    // MARK: - Notifications
     
     
     private func postTextViewDidChangeNotification() {
     private func postTextViewDidChangeNotification() {
         NotificationCenter.default.post(name: UITextView.textDidChangeNotification, object: self)
         NotificationCenter.default.post(name: UITextView.textDidChangeNotification, object: self)
@@ -213,7 +217,14 @@ open class InputTextView: UITextView {
     
     
     @objc
     @objc
     private func textViewTextDidChange() {
     private func textViewTextDidChange() {
-        placeholderLabel.isHidden = !text.isEmpty
+        let isPlaceholderHidden = !text.isEmpty
+        placeholderLabel.isHidden = isPlaceholderHidden
+        // Adjust constraints to prevent unambiguous content size
+        if isPlaceholderHidden {
+            placeholderLabelConstraintSet?.deactivate()
+        } else {
+            placeholderLabelConstraintSet?.activate()
+        }
     }
     }
     
     
     // MARK: - Image Paste Support
     // MARK: - Image Paste Support
@@ -234,7 +245,7 @@ open class InputTextView: UITextView {
         if isImagePasteEnabled {
         if isImagePasteEnabled {
             pasteImageInTextContainer(with: image)
             pasteImageInTextContainer(with: image)
         } else {
         } else {
-            for plugin in messageInputBar?.plugins ?? [] {
+            for plugin in inputBarAccessoryView?.inputPlugins ?? [] {
                 if plugin.handleInput(of: image) {
                 if plugin.handleInput(of: image) {
                     return
                     return
                 }
                 }
@@ -288,7 +299,7 @@ open class InputTextView: UITextView {
         guard let cgImage = image.cgImage else { return NSTextAttachment() }
         guard let cgImage = image.cgImage else { return NSTextAttachment() }
         let scale = image.size.width / (frame.width - 2 * (textContainerInset.left + textContainerInset.right))
         let scale = image.size.width / (frame.width - 2 * (textContainerInset.left + textContainerInset.right))
         let textAttachment = NSTextAttachment()
         let textAttachment = NSTextAttachment()
-        textAttachment.image = UIImage(cgImage: cgImage, scale: scale, orientation: .up)
+        textAttachment.image = UIImage(cgImage: cgImage, scale: scale, orientation: image.imageOrientation)
         return textAttachment
         return textAttachment
     }
     }
     
     
@@ -347,7 +358,7 @@ open class InputTextView: UITextView {
             attachments.forEach { (attachment) in
             attachments.forEach { (attachment) in
                 let (range, image) = attachment
                 let (range, image) = attachment
                 if curLocation < range.location {
                 if curLocation < range.location {
-                    let textRange = NSMakeRange(curLocation, range.location)
+                    let textRange = NSMakeRange(curLocation, range.location - curLocation)
                     let text = attributedText.attributedSubstring(from: textRange).string.trimmingCharacters(in: .whitespacesAndNewlines)
                     let text = attributedText.attributedSubstring(from: textRange).string.trimmingCharacters(in: .whitespacesAndNewlines)
                     if !text.isEmpty {
                     if !text.isEmpty {
                         components.append(text)
                         components.append(text)
@@ -387,3 +398,4 @@ open class InputTextView: UITextView {
     }
     }
     
     
 }
 }
+

+ 72 - 0
Pods/InputBarAccessoryView/InputBarAccessoryView/Views/SeparatorLine.swift

@@ -0,0 +1,72 @@
+//
+//  SeparatorLine.swift
+//  InputBarAccessoryView
+//
+//  Copyright © 2017-2019 Nathan Tannar.
+//
+//  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.
+//
+//  Created by Nathan Tannar on 10/4/17.
+//
+
+import UIKit
+
+/**
+ A UIView thats intrinsicContentSize is overrided so an exact height can be specified
+ 
+ ## Important Notes ##
+ 1. Default height is 1.0
+ 2. Default backgroundColor is UIColor.lightGray
+ 3. Intended to be used in an `InputStackView`
+ */
+open class SeparatorLine: UIView {
+    
+    // MARK: - Properties
+    
+    /// The height of the line
+    open var height: CGFloat = 1.0 {
+        didSet {
+            constraints.filter { $0.identifier == "height" }.forEach { $0.constant = height } // Assumes constraint was given an identifier
+            invalidateIntrinsicContentSize()
+        }
+    }
+    
+    open override var intrinsicContentSize: CGSize {
+        return CGSize(width: super.intrinsicContentSize.width, height: height)
+    }
+    
+    // MARK: - Initialization
+    
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        setup()
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        setup()
+    }
+    
+    /// Sets up the default properties
+    open func setup() {
+        backgroundColor = .lightGray
+        translatesAutoresizingMaskIntoConstraints = false
+        setContentHuggingPriority(.defaultHigh, for: .vertical)
+    }
+}

+ 3 - 2
Pods/MessageInputBar/LICENSE.md → Pods/InputBarAccessoryView/LICENSE

@@ -1,6 +1,7 @@
-MIT License
 
 
-Copyright (c) 2017-2018 MessageKit
+The MIT License (MIT)
+
+Copyright (c) Nathan Tannar <nathantannar4@gmail.com> 2017-2019
 
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 of this software and associated documentation files (the "Software"), to deal

+ 129 - 0
Pods/InputBarAccessoryView/README.md

@@ -0,0 +1,129 @@
+[![Image](./Screenshots/Preview.gif)]()
+
+# InputBarAccessoryView
+
+### Features
+
+- [x] Autocomplete text with @mention, #hashtag or any other prefix 
+- [x] A self-sizing `UITextView` with an optional fixed height (can be replaced with any other view)
+- [x] Image paste support   
+- [x] Autocomplete attributed text highlighting
+- [x] Reactive components that respond to given events
+- [x] Top/Bottom/Left/Right `InputStackView`s that act as toolbars to place buttons
+- [x] `RxSwift`/`RxCocoa` Support
+- [x] Drop in attachment view for file/photo management
+- [x] Plugin support for your own `InputPlugin`s
+- [x] Compatible with all iPhones and iPads
+- [x] RTL Support
+
+### Installation via CocoaPods
+
+```ruby
+pod 'InputBarAccessoryView'
+```
+
+### Installation via Carthage
+
+```ruby
+github "nathantannar4/InputBarAccessoryView"
+```
+
+### Requirements
+
+iOS 9.0+
+Swift 4.2+
+XCode 9.0+
+
+### Documentation
+
+[Getting Started](./GETTING_STARTED.md)
+
+> See the Example project to see how you can make the iMessage, Slack, Facebook and GitHawk input bars!
+
+### Example Usage
+
+<img src="https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/master/Screenshots/ScreenshotA.png" width="242" height="432"> <img src="https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/master/Screenshots/ScreenshotB.png" width="242" height="432"> <img src="https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/master/Screenshots/ScreenshotC.png" width="242" height="432"> <img src="https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/master/Screenshots/ScreenshotD.png" width="242" height="432"> <img src="https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/master/Screenshots/ScreenshotE.png" width="242" height="432"> <img src="https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/master/Screenshots/ScreenshotF.png" width="242" height="432">
+
+### Featured In
+
+Add your app to the list of apps using this library and make a pull request.
+
+- [MessageKit](https://github.com/MessageKit/MessageKit) *(renamed to MessageInputBar)*
+<p>
+  <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/mklogo.png" title="MessageKit Logo" height="50">
+</p>
+
+- [MessageViewController](https://github.com/GitHawkApp/MessageViewController) *(Autocomplete Highlighting Algorithm)*
+<p>
+  <img src="https://avatars3.githubusercontent.com/u/32285710?s=200&v=4" title="GitHawk Logo" height="50">
+</p>
+
+### See Also
+
+iMessage style [TypingIndicator](https://github.com/nathantannar4/TypingIndicator) for chat apps
+
+## Latest Releases
+
+- 4.2.1
+    - Fixed autocompletes of completions that contain spaces
+    - Depricated `isCaseSensitive` in favor of a function asignment to pre-process autocompletions
+- 4.2.0
+    - Added new API for overriding the main middle view, normally the `InputTextView` so it can be replaced with views such as a "Join" button", `setMiddleContentView(_ view: UIView?, animated: Bool)`
+- 4.1.2
+        - Add RxSwift/RxCocoa support through extensions and delegate proxies in `RxInputBarAccessoryView`, requires `RxSwift` and `RxCocoa`
+        - Add `InputBarViewController` which contains an `InputBarAccessoryView` as the `inputAccessoryView` by default with a convenient `isInputBarHidden: Bool` property to show/hide it 
+- 4.1.1
+        - Add `frameInsets: HorizontalEdgePadding` property to `InputBarAccessoryView` to inset the view to be compatible with `UISplitViewController` or other custom containers where the view should not be the full width
+- 4.1.0
+        - Fix issue where setting long strings in `viewDidLoad` broke inital layout, Issue #41
+        - Add `deleteCompletionByParts: Bool` flag to `AutocompleteManager` to allow for partial deletions of autocompletes rather than just the entire substring. 
+        - Add `InputBarSendButton` to use as the `sendButton` in `InputBarAccessoryView`. This subclass of `InputBarButtonItem` has a `UIActivityIndicatorView` to show a spinner when making HTTP requests
+
+### [CHANGELOG](./CHANGELOG.md)
+
+**Find a bug? Open an issue!**
+
+## Layout
+
+The layout of the `InputBarAccessoryView` is made of of 4  `InputStackView`'s and an `InputTextView`. The padding of the subviews can be easily adjusted by changing the `padding` and `textViewPadding` properties. The constraints will automatically be updated.
+
+<img src="https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/master/Screenshots/Layout.png">
+
+It is important to note that each of the `InputStackView `'s to the left and right of the `InputTextView` are anchored by a width constraint. This way the `InputTextView` will always fill the space inbetween in addition to providing methods that can easily be called to hide all buttons to the right or left of the `InputTextView` by setting the width constraint constant to 0. The bottom and top stack views are not height constraint and rely on their `intrinsicContentSize`
+
+```swift
+func setLeftStackViewWidthConstant(to newValue: CGFloat, animated: Bool)
+
+func setRightStackViewWidthConstant(to newValue: CGFloat, animated: Bool)
+```
+
+### Reactive Hooks
+
+Each `InputBarButtonItem` has properties that can hold actions that will be executed during various hooks such as the button being touched, the `UITextView `text changing and more! Thanks to these easy hooks with a few lines of code the items can be easily resized and animated similar to that of the Facebook messenger app.
+
+```swift
+// MARK: - Hooks
+    
+public typealias InputBarButtonItemAction = ((InputBarButtonItem) -> Void)    
+    
+private var onTouchUpInsideAction: InputBarButtonItemAction?
+private var onKeyboardEditingBeginsAction: InputBarButtonItemAction?
+private var onKeyboardEditingEndsAction: InputBarButtonItemAction?
+private var onKeyboardSwipeGestureAction: ((InputBarButtonItem, UISwipeGestureRecognizer) -> Void)?
+private var onTextViewDidChangeAction: ((InputBarButtonItem, InputTextView) -> Void)?
+private var onSelectedAction: InputBarButtonItemAction?
+private var onDeselectedAction: InputBarButtonItemAction?
+private var onEnabledAction: InputBarButtonItemAction?
+private var onDisabledAction: InputBarButtonItemAction?
+```
+
+## Author
+<p>
+	<img src="https://github.com/nathantannar4/NTComponents/raw/master/NTComponents/Assets/Nathan.png" width="100" height="100">
+</p>
+
+**Nathan Tannar** - [https://nathantannar.me](https://nathantannar.me)
+
+## License
+
+Distributed under the MIT license. See ``LICENSE`` for more information.

+ 20 - 20
Pods/Manifest.lock

@@ -1,22 +1,22 @@
 PODS:
 PODS:
   - ALCameraViewController (3.0.3)
   - ALCameraViewController (3.0.3)
   - DBDebugToolkit (0.5.0)
   - DBDebugToolkit (0.5.0)
+  - InputBarAccessoryView (4.2.2)
   - JGProgressHUD (2.0.3)
   - JGProgressHUD (2.0.3)
-  - MessageInputBar/Core (0.4.1)
-  - MessageKit (2.0.0):
-    - MessageInputBar/Core
-  - QuickTableViewController (1.1.0)
-  - ReachabilitySwift (4.3.0)
-  - SwiftFormat/CLI (0.37.2)
-  - SwiftLint (0.29.3)
-  - SwiftyBeaver (1.6.1)
-  - UICircularProgressRing (6.1.0)
+  - MessageKit (3.0.0):
+    - InputBarAccessoryView (~> 4.2.2)
+  - QuickTableViewController (1.1.1)
+  - ReachabilitySwift (4.3.1)
+  - SwiftFormat/CLI (0.40.9)
+  - SwiftLint (0.32.0)
+  - SwiftyBeaver (1.7.0)
+  - UICircularProgressRing (6.2.0)
 
 
 DEPENDENCIES:
 DEPENDENCIES:
   - ALCameraViewController (from `https://github.com/dignifiedquire/ALCameraViewController`)
   - ALCameraViewController (from `https://github.com/dignifiedquire/ALCameraViewController`)
   - DBDebugToolkit
   - DBDebugToolkit
   - JGProgressHUD
   - JGProgressHUD
-  - MessageKit (= 2.0.0)
+  - MessageKit
   - QuickTableViewController
   - QuickTableViewController
   - ReachabilitySwift
   - ReachabilitySwift
   - SwiftFormat/CLI
   - SwiftFormat/CLI
@@ -27,8 +27,8 @@ DEPENDENCIES:
 SPEC REPOS:
 SPEC REPOS:
   https://github.com/cocoapods/specs.git:
   https://github.com/cocoapods/specs.git:
     - DBDebugToolkit
     - DBDebugToolkit
+    - InputBarAccessoryView
     - JGProgressHUD
     - JGProgressHUD
-    - MessageInputBar
     - MessageKit
     - MessageKit
     - QuickTableViewController
     - QuickTableViewController
     - ReachabilitySwift
     - ReachabilitySwift
@@ -49,16 +49,16 @@ CHECKOUT OPTIONS:
 SPEC CHECKSUMS:
 SPEC CHECKSUMS:
   ALCameraViewController: dd13cf0a5b44a4d542c73bbcbebd02bc09e929c7
   ALCameraViewController: dd13cf0a5b44a4d542c73bbcbebd02bc09e929c7
   DBDebugToolkit: c04bb6f618051d3de447a4b4323f37826116cfed
   DBDebugToolkit: c04bb6f618051d3de447a4b4323f37826116cfed
+  InputBarAccessoryView: 2b937602598e2fab3149f37f51dd7ad795653812
   JGProgressHUD: 12b20a8f4ffe05258f8635c1ab92816e451f904d
   JGProgressHUD: 12b20a8f4ffe05258f8635c1ab92816e451f904d
-  MessageInputBar: e81c7535347f1f7b923de7080409a535a004b6e4
-  MessageKit: 29c1c87e5a396d2ca7a3f712e5171dc9aba42a1e
-  QuickTableViewController: 129e71fc37a69991aff02bc946723d51a13d6aa5
-  ReachabilitySwift: 408477d1b6ed9779dba301953171e017c31241f3
-  SwiftFormat: abbfb26dde4c6d683923cde153c88e10da9d7881
-  SwiftLint: bfa7ca7b4d170cfaf0d236ca3ffd969e88a2f002
-  SwiftyBeaver: ccfcdf85a04d429f1633f668650b0ce8020bda3a
-  UICircularProgressRing: b3eba859a515e4d9d38503eec913f09c28450785
+  MessageKit: e892a9ca49ebe6d82684d3a93ad84021fa23e2c6
+  QuickTableViewController: 30f689d314a7c1fb726c6dab6fa5ad53c6bc9e94
+  ReachabilitySwift: 4032e2f59586e11e3b0ebe15b167abdd587a388b
+  SwiftFormat: 6b67b6e7fe73d664f0cbb4f13721f130462c86a5
+  SwiftLint: 009a898ef2a1c851f45e1b59349bf6ff2ddc990d
+  SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
+  UICircularProgressRing: 0ff679b05a17f15ad6301a7886686837b8c301a9
 
 
-PODFILE CHECKSUM: 8eb78a7985e96bf680a3423fa0fe74be1c95457e
+PODFILE CHECKSUM: 46d42246ca1255cc4af799d1aa792d60033b9fb8
 
 
 COCOAPODS: 1.6.1
 COCOAPODS: 1.6.1

+ 0 - 91
Pods/MessageInputBar/README.md

@@ -1,91 +0,0 @@
-<p>
-  <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/mklogo.png" title="MessageKit logo">
-</p>
-
-[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
-<a href="https://swift.org">
- <img src="https://img.shields.io/badge/Swift-4-orange.svg"
-      alt="Swift" />
-</a>
-<a href="https://cocoapods.org/">
-  <img src="https://cocoapod-badges.herokuapp.com/v/MessageInputBar/badge.png"
-      alt="CocoaPods">
-</a>
-<a href="https://developer.apple.com/xcode">
-  <img src="https://img.shields.io/badge/Xcode-9-blue.svg"
-      alt="Xcode">
-</a>
-<a href="https://opensource.org/licenses/MIT">
-  <img src="https://img.shields.io/badge/License-MIT-red.svg"
-      alt="MIT">
-</a>
-<a href="https://github.com/MessageKit/MessageInputBar/issues">
-   <img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat"
-        alt="Contributions Welcome">
-</a>
-
-## Installation
-
-### [CocoaPods](https://cocoapods.org/) **Recommended**
-````ruby
-pod 'MessageInputBar'
-````
-
-### [Carthage](https://github.com/Carthage/Carthage)
-
-To integrate MessageKit using Carthage, add the following to your `Cartfile`:
-
-````
-github "MessageKit/MessageInputBar"
-````
-
-## Requirements
-
-- **iOS 9** or later
-- **Swift 4** or later
-
-> Version 0.4.0 onwards is only Swift 4.2 compatible
-
-
-## Contributing
-
-Great! Look over these things first.
-- Please read our [Code of Conduct](https://github.com/MessageKit/MessageInputBar/blob/master/Code_of_Conduct.md)
-- Check the [Contributing Guide Lines](https://github.com/MessageKit/MessageInputBar/blob/master/CONTRIBUTING.md).
-- Come join us on [Slack](https://join.slack.com/t/messagekit/shared_invite/MjI4NzIzNzMyMzU0LTE1MDMwODIzMDUtYzllYzIyNTU4MA) and 🗣 don't be a stranger. 
-- Check out the [current issues](https://github.com/MessageKit/MessageInputBar/issues) and see if you can tackle any of those. 
-- Download the project and check out the current code base. Suggest any improvements by opening a new issue. 
-- Check out the [What's Next](#whats-next) section :point_down: to see where we are headed.
-- Check [StackOverflow](https://stackoverflow.com/questions/tagged/messagekit)
-- Install [SwiftLint](https://github.com/realm/SwiftLint) too keep yourself in :neckbeard: style. 
-- Be kind and helpful.  
-
-
-## What's Next?
-
-Check out the [Releases](https://github.com/MessageKit/MessageInputBar/releases) to see what we are working on next.
-
-## Contact
-
-Have a question or an issue about MessageKit? Create an [issue](https://github.com/MessageKit/MessageInputBar/issues/new)!
-
-Interested in contributing to MessageKit? Click here to join our [Slack](https://join.slack.com/t/messagekit/shared_invite/MjI4NzIzNzMyMzU0LTE1MDMwODIzMDUtYzllYzIyNTU4MA).
-
-### Apps using this library
-
-Add your app to the list of apps using this library and make a pull request.
-
-*Please provide attribution, it is greatly appreciated.*
-
-## MessageKit Core Team
-
-- [@SD10](https://github.com/sd10), Steven Deutsch
-- [@nathantannar4](https://github.com/nathantannar4), Nathan Tannar (MessageInputBar Maintainer)
-- [@zhongwuzw](https://github.com/zhongwuzw), Wu Zhong
-
-## Thanks
-
-Many thanks to [**the contributors**](https://github.com/MessageKit/MessageInputBar/graphs/contributors) of this project.
-
-## License
-MessageInputBar is released under the [MIT License](https://github.com/MessageKit/MessageInputBar/blob/master/LICENSE.md).

+ 0 - 73
Pods/MessageInputBar/Sources/Extensions/NSMutableAttributedString+Extensions.swift

@@ -1,73 +0,0 @@
-/*
- MIT License
- 
- Copyright (c) 2017-2018 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
-
-extension NSMutableAttributedString {
- 
-    @discardableResult
-    internal func bold(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor = .black) -> NSMutableAttributedString {
-        let attrs: [NSAttributedString.Key:AnyObject] = [
-            NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: fontSize),
-            NSAttributedString.Key.foregroundColor : textColor
-        ]
-        let boldString = NSMutableAttributedString(string: text, attributes: attrs)
-        self.append(boldString)
-        return self
-    }
-    
-    @discardableResult
-    internal func normal(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor = .black) -> NSMutableAttributedString {
-        let attrs:[NSAttributedString.Key:AnyObject] = [
-            NSAttributedString.Key.font : UIFont.systemFont(ofSize: fontSize),
-            NSAttributedString.Key.foregroundColor : textColor
-        ]
-        let normal =  NSMutableAttributedString(string: text, attributes: attrs)
-        self.append(normal)
-        return self
-    }
-
-}
-
-extension NSAttributedString {
-
-    internal func replacingCharacters(in range: NSRange, with attributedString: NSAttributedString) -> NSMutableAttributedString {
-        let ns = NSMutableAttributedString(attributedString: self)
-        ns.replaceCharacters(in: range, with: attributedString)
-        return ns
-    }
-    
-    internal static func += (lhs: inout NSAttributedString, rhs: NSAttributedString) {
-        let ns = NSMutableAttributedString(attributedString: lhs)
-        ns.append(rhs)
-        lhs = ns
-    }
-    
-    internal static func + (lhs: NSAttributedString, rhs: NSAttributedString) -> NSAttributedString {
-        let ns = NSMutableAttributedString(attributedString: lhs)
-        ns.append(rhs)
-        return NSAttributedString(attributedString: ns)
-    }
-    
-}

+ 0 - 116
Pods/MessageInputBar/Sources/Extensions/UIView+Extensions.swift

@@ -1,116 +0,0 @@
-/*
- MIT License
- 
- Copyright (c) 2017-2018 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
-
-extension UIView {
-    
-    internal func fillSuperview() {
-        guard let superview = self.superview else {
-            return
-        }
-        translatesAutoresizingMaskIntoConstraints = false
-
-	    let constraints: [NSLayoutConstraint] = [
-    	    leftAnchor.constraint(equalTo: superview.leftAnchor),
-    	    rightAnchor.constraint(equalTo: superview.rightAnchor),
-    	    topAnchor.constraint(equalTo: superview.topAnchor),
-    	    bottomAnchor.constraint(equalTo: superview.bottomAnchor)
-    	    ]
-	    NSLayoutConstraint.activate(constraints)
-    }
-
-    internal func centerInSuperview() {
-        guard let superview = self.superview else {
-            return
-        }
-        translatesAutoresizingMaskIntoConstraints = false
-        let constraints: [NSLayoutConstraint] = [
-            centerXAnchor.constraint(equalTo: superview.centerXAnchor),
-            centerYAnchor.constraint(equalTo: superview.centerYAnchor)
-        ]
-        NSLayoutConstraint.activate(constraints)
-    }
-    
-    internal func constraint(equalTo size: CGSize) {
-        guard superview != nil else { return }
-        translatesAutoresizingMaskIntoConstraints = false
-        let constraints: [NSLayoutConstraint] = [
-            widthAnchor.constraint(equalToConstant: size.width),
-            heightAnchor.constraint(equalToConstant: size.height)
-        ]
-        NSLayoutConstraint.activate(constraints)
-        
-    }
-
-    @discardableResult
-    internal func addConstraints(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
-        
-        if self.superview == nil {
-            return []
-        }
-        translatesAutoresizingMaskIntoConstraints = false
-        
-        var constraints = [NSLayoutConstraint]()
-        
-        if let top = top {
-            let constraint = topAnchor.constraint(equalTo: top, constant: topConstant)
-            constraint.identifier = "top"
-            constraints.append(constraint)
-        }
-        
-        if let left = left {
-            let constraint = leftAnchor.constraint(equalTo: left, constant: leftConstant)
-            constraint.identifier = "left"
-            constraints.append(constraint)
-        }
-        
-        if let bottom = bottom {
-            let constraint = bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant)
-            constraint.identifier = "bottom"
-            constraints.append(constraint)
-        }
-        
-        if let right = right {
-            let constraint = rightAnchor.constraint(equalTo: right, constant: -rightConstant)
-            constraint.identifier = "right"
-            constraints.append(constraint)
-        }
-        
-        if widthConstant > 0 {
-            let constraint = widthAnchor.constraint(equalToConstant: widthConstant)
-            constraint.identifier = "width"
-            constraints.append(constraint)
-        }
-        
-        if heightConstant > 0 {
-            let constraint = heightAnchor.constraint(equalToConstant: heightConstant)
-            constraint.identifier = "height"
-            constraints.append(constraint)
-        }
-        
-        NSLayoutConstraint.activate(constraints)
-        return constraints
-    }
-}

+ 0 - 81
Pods/MessageInputBar/Sources/Models/NSConstraintLayoutSet.swift

@@ -1,81 +0,0 @@
-/*
- MIT License
- 
- Copyright (c) 2017-2018 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
-
-internal class NSLayoutConstraintSet {
-    
-    internal var top: NSLayoutConstraint?
-    internal var bottom: NSLayoutConstraint?
-    internal var left: NSLayoutConstraint?
-    internal var right: NSLayoutConstraint?
-    internal var centerX: NSLayoutConstraint?
-    internal var centerY: NSLayoutConstraint?
-    internal var width: NSLayoutConstraint?
-    internal var height: NSLayoutConstraint?
-    
-    internal init(top: NSLayoutConstraint? = nil, bottom: NSLayoutConstraint? = nil,
-                left: NSLayoutConstraint? = nil, right: NSLayoutConstraint? = nil,
-                centerX: NSLayoutConstraint? = nil, centerY: NSLayoutConstraint? = nil,
-                width: NSLayoutConstraint? = nil, height: NSLayoutConstraint? = nil) {
-        self.top = top
-        self.bottom = bottom
-        self.left = left
-        self.right = right
-        self.centerX = centerX
-        self.centerY = centerY
-        self.width = width
-        self.height = height
-    }
-
-    /// All of the currently configured constraints
-    private var availableConstraints: [NSLayoutConstraint] {
-        let constraints = [top, bottom, left, right, centerX, centerY, width, height]
-        var available: [NSLayoutConstraint] = []
-        for constraint in constraints {
-            if let value = constraint {
-                available.append(value)
-            }
-        }
-        return available
-    }
-    
-    /// Activates all of the non-nil constraints
-    ///
-    /// - Returns: Self
-    @discardableResult
-    internal func activate() -> Self {
-        NSLayoutConstraint.activate(availableConstraints)
-        return self
-    }
-    
-    /// Deactivates all of the non-nil constraints
-    ///
-    /// - Returns: Self
-    @discardableResult
-    internal func deactivate() -> Self {
-        NSLayoutConstraint.deactivate(availableConstraints)
-        return self
-    }
-}

+ 0 - 62
Pods/MessageInputBar/Sources/Protocols/MessageInputBarDelegate.swift

@@ -1,62 +0,0 @@
-/*
- MIT License
- 
- Copyright (c) 2017-2018 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
-
-/// A protocol that can receive different event notifications from the MessageInputBar.
-public protocol MessageInputBarDelegate: AnyObject {
-    
-    /// Called when the default send button has been selected.
-    ///
-    /// - Parameters:
-    ///   - inputBar: The `MessageInputBar`.
-    ///   - text: The current text in the `InputTextView` of the `MessageInputBar`.
-    func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String)
-    
-    /// Called when the instrinsicContentSize of the MessageInputBar has changed.
-    /// Can be used for adjusting content insets on other views to make sure
-    /// the MessageInputBar does not cover up any other view.
-    ///
-    /// - Parameters:
-    ///   - inputBar: The `MessageInputBar`.
-    ///   - size: The new instrinsic content size.
-    func messageInputBar(_ inputBar: MessageInputBar, didChangeIntrinsicContentTo size: CGSize)
-    
-    /// Called when the `MessageInputBar`'s `InputTextView`'s text has changed.
-    /// Useful for adding your own logic without the need of assigning a delegate or notification.
-    ///
-    /// - Parameters:
-    ///   - inputBar: The MessageInputBar
-    ///   - text: The current text in the MessageInputBar's InputTextView
-    func messageInputBar(_ inputBar: MessageInputBar, textViewTextDidChangeTo text: String)
-}
-
-public extension MessageInputBarDelegate {
-    
-    func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {}
-    
-    func messageInputBar(_ inputBar: MessageInputBar, didChangeIntrinsicContentTo size: CGSize) {}
-    
-    func messageInputBar(_ inputBar: MessageInputBar, textViewTextDidChangeTo text: String) {}
-}

+ 0 - 72
Pods/MessageInputBar/Sources/Views/InputStackView.swift

@@ -1,72 +0,0 @@
-/*
- MIT License
- 
- Copyright (c) 2017-2018 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
-
-/**
- A UIStackView that's intended for holding `InputItem`s
- 
- ## Important Notes ##
- 1. Default alignment is .fill
- 2. Default distribution is .fill
- 3. The distribution property needs to be based on its arranged subviews intrinsicContentSize so it is not recommended to change it
- */
-open class InputStackView: UIStackView {
-    
-    /// The stack view position in the MessageInputBar
-    ///
-    /// - left: Left Stack View
-    /// - right: Bottom Stack View
-    /// - bottom: Left Stack View
-    /// - top: Top Stack View
-    public enum Position {
-        case left, right, bottom, top
-    }
-    
-    // MARK: Initialization
-    
-    public convenience init(axis: NSLayoutConstraint.Axis, spacing: CGFloat) {
-        self.init(frame: .zero)
-        self.axis = axis
-        self.spacing = spacing
-    }
-    
-    public override init(frame: CGRect) {
-        super.init(frame: frame)
-        setup()
-    }
-    
-    required public init(coder: NSCoder) {
-        super.init(coder: coder)
-    }
-    
-    // MARK: - Setup
-    
-    /// Sets up the default properties
-    open func setup() {
-        translatesAutoresizingMaskIntoConstraints = false
-        distribution = .fill
-        alignment = .bottom
-    }
-}

BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/disclouser.png


BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/disclouser@2x.png


BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/disclouser@3x.png


BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/pause.png


BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/pause@2x.png


BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/pause@3x.png


BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/play.png


BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/play@2x.png


BIN
Pods/MessageKit/Assets/MessageKitAssets.bundle/Images/play@3x.png


+ 1 - 1
Pods/MessageKit/LICENSE.md

@@ -1,6 +1,6 @@
 MIT License
 MIT License
 
 
-Copyright (c) 2017-2018 MessageKit
+Copyright (c) 2017-2019 MessageKit
 
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 of this software and associated documentation files (the "Software"), to deal

+ 67 - 30
Pods/MessageKit/README.md

@@ -1,6 +1,9 @@
 <p>
 <p>
   <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/mklogo.png" title="MessageKit logo">
   <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/mklogo.png" title="MessageKit logo">
 </p>
 </p>
+<p>
+  <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/TypingIndicator.png" title="MessageKit header">
+</p>
 
 
 [![CircleCI](https://circleci.com/gh/MessageKit/MessageKit.svg?style=svg)](https://circleci.com/gh/MessageKit/MessageKit)
 [![CircleCI](https://circleci.com/gh/MessageKit/MessageKit.svg?style=svg)](https://circleci.com/gh/MessageKit/MessageKit)
 [![codecov](https://codecov.io/gh/MessageKit/MessageKit/branch/master/graph/badge.svg)](https://codecov.io/gh/MessageKit/MessageKit)
 [![codecov](https://codecov.io/gh/MessageKit/MessageKit/branch/master/graph/badge.svg)](https://codecov.io/gh/MessageKit/MessageKit)
@@ -26,18 +29,6 @@
         alt="Contributions Welcome">
         alt="Contributions Welcome">
 </a>
 </a>
 
 
-## Table of Contents
-
-* [**Goal**](#goals)📈
-* [**Contributing**](#contributing)
-* [**Requirements**](#requirements)
-* [**Code of Conduct**](https://github.com/MessageKit/MessageKit/blob/master/CODE_OF_CONDUCT.md)
-* [**What's Next**](#whats-next)
-* [**Contact**](#contact)
-* [**Apps using this library**](#apps-using-this-library)
-* [**License**](#license)
-
-
 ## Goals
 ## Goals
 
 
 - Provide a :rotating_light:safe:rotating_light: environment for others to learn and grow through Open Source.
 - Provide a :rotating_light:safe:rotating_light: environment for others to learn and grow through Open Source.
@@ -49,30 +40,14 @@
 ## Vision
 ## Vision
 See [VISION.md](https://github.com/MessageKit/MessageKit/blob/master/VISION.md) for Goals, Scope, & Technical Considerations.
 See [VISION.md](https://github.com/MessageKit/MessageKit/blob/master/VISION.md) for Goals, Scope, & Technical Considerations.
 
 
-
 ## Installation
 ## Installation
 ### [CocoaPods](https://cocoapods.org/) **Recommended**
 ### [CocoaPods](https://cocoapods.org/) **Recommended**
 ````ruby
 ````ruby
+# Swift 4.2
 pod 'MessageKit'
 pod 'MessageKit'
 ````
 ````
 
 
-If your project is still using Swift 3, you can add the following code to your Podfile:
-
-````ruby
-target 'TARGET_NAME' do
-    pod 'MessageKit'
-    ...
-    post_install do |installer|
-        installer.pods_project.targets.each do |target|
-            if target.name == 'MessageKit'
-                target.build_configurations.each do |config|
-                    config.build_settings['SWIFT_VERSION'] = '4.0'
-                end
-            end
-        end
-    end
-end
-````
+> If you are already using Swift 5, use the `3.0.0-swift5` branch until the offical release is made
 
 
 ### [Carthage](https://github.com/Carthage/Carthage)
 ### [Carthage](https://github.com/Carthage/Carthage)
 
 
@@ -82,9 +57,69 @@ To integrate MessageKit using Carthage, add the following to your `Cartfile`:
 github "MessageKit/MessageKit"
 github "MessageKit/MessageKit"
 ````
 ````
 
 
+### [Manual]([https://github.com/MessageKit/MessageKit/blob/master/Documentation/MANUAL_INSTALLATION.md)
+
 ## Requirements
 ## Requirements
 
 
 - **iOS9** or later
 - **iOS9** or later
+- **Swift 4.2** or later
+
+
+## Getting Started
+
+### Cell Structure
+<p>
+  <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/CellStructure.png" title="CellStructure">
+</p>
+
+Each default cell is a subclass of [`MessageContentCell`](https://github.com/MessageKit/MessageKit/blob/master/Sources/Views/Cells/MessageContentCell.swift) which has 7 parts. From top down we have a: `cellTopLabel`, `messageTopLabel`, `messageContainerView`, `messageBottomLabel`, `cellBottomLabel` with the `avatarView` and `accessoryView` on either side respectively. Above we see the basic [`TextMessageCell`](https://github.com/MessageKit/MessageKit/blob/master/Sources/Views/Cells/TextMessageCell.swift) which uses a `MessageLabel` as its main content. 
+
+This structure will allow you to create a layout that suits your needs as you can customize the size, appearance and padding of each. If you need something more advanced you can implement a custom cell, which we show how to do in the [Example](https://github.com/MessageKit/MessageKit/tree/master/Example) project.
+
+### MessageInputBar Structure
+<p>
+  <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/InputBarAccessoryViewLayout.png" title="InputBarAccessoryViewLayout">
+</p>
+
+The `MessageInputBar`, derrived from [InputBarAccessoryView](https://github.com/nathantannar4/InputBarAccessoryView) is a flexible and robust way of creating any kind of input layout you wish. It is self-sizing which means as the user types it will grow to fill available space. It is centered around the `middleContentView` which by default holds the `InputTextView`. This is surrounded by `InputStackView`'s that will also grow in high based on the needs of their subviews `intrinsicContentSize`. See the [Example](https://github.com/MessageKit/MessageKit/tree/master/Example) project for examples on how to taylor the layout for your own needs.
+
+### Guides
+
+Please have a look at the [Quick Start guide](https://github.com/MessageKit/MessageKit/blob/master/Documentation/QuickStart.md) and the [FAQs](https://github.com/MessageKit/MessageKit/blob/master/Documentation/FAQs.md).
+
+We recommend you start by looking at the [Example](https://github.com/MessageKit/MessageKit/tree/master/Example) project or write a question with the "messagekit" tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/messagekit). You can also look at previous issues here on GitHib with the **"Question"** tag.
+
+For more on how to use the MessageInputBar, see the dependency it is based on [InputBarAccessoryView](https://github.com/nathantannar4/InputBarAccessoryView). You can also see this [short guide]([https://github.com/MessageKit/MessageKit/blob/master/Documentation/MessageInputBar.md) 
+
+## Default Cells
+
+<p>
+  <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/ExampleA.png" title="Example A" height=400>
+  <img src="https://raw.githubusercontent.com/MessageKit/MessageKit/master/Assets/ExampleB.png" title="Example B" height=400>
+</p>
+
+The type of cell rendered for a given message is based on the `MessageKind`
+
+```swift
+public enum MessageKind {
+    case text(String) // TextMessageCell
+    case attributedText(NSAttributedString) // TextMessageCell
+    case photo(MediaItem) // MediaMessageCell
+    case video(MediaItem) // MediaMessageCell
+    case location(LocationItem) // LocationMessageCell
+    case emoji(String) // TextMessageCell
+    case audio(AudioItem) // AudioMessageCell
+    case contact(ContactItem) // ContactMessageCell
+
+    /// A custom message.
+    /// - Note: Using this case requires that you implement the following methods and handle this case:
+    ///   - MessagesDataSource: customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
+    ///   - MessagesLayoutDelegate: customCellSizeCalculator(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator
+    case custom(Any?)
+}
+```
+
+If you choose to use the `.custom` kind you are responsible for all of the cells layout. Any `UICollectionViewCell` can be returned for custom cells which means any of the styling you provide from the `MessageDisplayDelegate` will not effect your custom cell. Even if you subclass your cell from `MessageContentCell`.
 
 
 
 
 ## Contributing
 ## Contributing
@@ -115,6 +150,8 @@ Interested in contributing to MessageKit? Click here to join our [Slack](https:/
 
 
 Add your app to the list of apps using this library and make a pull request.
 Add your app to the list of apps using this library and make a pull request.
 
 
+- [Formacar](https://itunes.apple.com/ru/app/id1180117334)
+- [HopUp](https://itunes.apple.com/us/app/hopup-airsoft-community/id1128903141?mt=8)
 - [MediQuo](https://www.mediquo.com)
 - [MediQuo](https://www.mediquo.com)
 - [RappresentaMe](https://itunes.apple.com/it/app/rappresentame/id1330914443)
 - [RappresentaMe](https://itunes.apple.com/it/app/rappresentame/id1330914443)
 - [WiseEyes](https://itunes.apple.com/us/app/wiseeyes/id1391408511?mt=8)
 - [WiseEyes](https://itunes.apple.com/us/app/wiseeyes/id1391408511?mt=8)

+ 5 - 5
Pods/MessageKit/Sources/Controllers/MessagesViewController+Keyboard.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -23,9 +23,9 @@
  */
  */
 
 
 import Foundation
 import Foundation
-import MessageInputBar
+import InputBarAccessoryView
 
 
-extension MessagesViewController {
+internal extension MessagesViewController {
 
 
     // MARK: - Register / Unregister Observers
     // MARK: - Register / Unregister Observers
 
 
@@ -56,7 +56,7 @@ extension MessagesViewController {
         guard !isMessagesControllerBeingDismissed else { return }
         guard !isMessagesControllerBeingDismissed else { return }
 
 
         guard let keyboardStartFrameInScreenCoords = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect else { return }
         guard let keyboardStartFrameInScreenCoords = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect else { return }
-        guard !keyboardStartFrameInScreenCoords.isEmpty else {
+        guard !keyboardStartFrameInScreenCoords.isEmpty || UIDevice.current.userInterfaceIdiom != .pad else {
             // WORKAROUND for what seems to be a bug in iPad's keyboard handling in iOS 11: we receive an extra spurious frame change
             // WORKAROUND for what seems to be a bug in iPad's keyboard handling in iOS 11: we receive an extra spurious frame change
             // notification when undocking the keyboard, with a zero starting frame and an incorrect end frame. The workaround is to
             // notification when undocking the keyboard, with a zero starting frame and an incorrect end frame. The workaround is to
             // ignore this notification.
             // ignore this notification.
@@ -112,7 +112,7 @@ extension MessagesViewController {
         // see https://developer.apple.com/videos/play/wwdc2017/242/ for more details
         // see https://developer.apple.com/videos/play/wwdc2017/242/ for more details
         let intersection = messagesCollectionView.frame.intersection(keyboardFrame)
         let intersection = messagesCollectionView.frame.intersection(keyboardFrame)
         
         
-        if intersection.isNull || intersection.maxY < messagesCollectionView.frame.maxY {
+        if intersection.isNull || (messagesCollectionView.frame.maxY - intersection.maxY) > 0.001 {
             // The keyboard is hidden, is a hardware one, or is undocked and does not cover the bottom of the collection view.
             // The keyboard is hidden, is a hardware one, or is undocked and does not cover the bottom of the collection view.
             // Note: intersection.maxY may be less than messagesCollectionView.frame.maxY when dealing with undocked keyboards.
             // Note: intersection.maxY may be less than messagesCollectionView.frame.maxY when dealing with undocked keyboards.
             return max(0, additionalBottomInset - automaticallyAddedBottomInset)
             return max(0, additionalBottomInset - automaticallyAddedBottomInset)

+ 2 - 3
Pods/MessageKit/Sources/Controllers/MessagesViewController+Menu.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -23,9 +23,8 @@
  */
  */
 
 
 import Foundation
 import Foundation
-import MessageInputBar
 
 
-extension MessagesViewController {
+internal extension MessagesViewController {
 
 
     // MARK: - Register / Unregister Observers
     // MARK: - Register / Unregister Observers
 
 

+ 114 - 9
Pods/MessageKit/Sources/Controllers/MessagesViewController.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -23,7 +23,7 @@
  */
  */
 
 
 import UIKit
 import UIKit
-import MessageInputBar
+import InputBarAccessoryView
 
 
 /// A subclass of `UIViewController` with a `MessagesCollectionView` object
 /// A subclass of `UIViewController` with a `MessagesCollectionView` object
 /// that is used to display conversation interfaces.
 /// that is used to display conversation interfaces.
@@ -33,8 +33,8 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
     /// The `MessagesCollectionView` managed by the messages view controller object.
     /// The `MessagesCollectionView` managed by the messages view controller object.
     open var messagesCollectionView = MessagesCollectionView()
     open var messagesCollectionView = MessagesCollectionView()
 
 
-    /// The `MessageInputBar` used as the `inputAccessoryView` in the view controller.
-    open var messageInputBar = MessageInputBar()
+    /// The `InputBarAccessoryView` used as the `inputAccessoryView` in the view controller.
+    open var messageInputBar = InputBarAccessoryView()
 
 
     /// A Boolean value that determines whether the `MessagesCollectionView` scrolls to the
     /// A Boolean value that determines whether the `MessagesCollectionView` scrolls to the
     /// bottom whenever the `InputTextView` begins editing.
     /// bottom whenever the `InputTextView` begins editing.
@@ -71,12 +71,16 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
         }
         }
     }
     }
 
 
+    public var isTypingIndicatorHidden: Bool {
+        return messagesCollectionView.isTypingIndicatorHidden
+    }
+
+    public var selectedIndexPathForMenu: IndexPath?
+
     private var isFirstLayout: Bool = true
     private var isFirstLayout: Bool = true
     
     
     internal var isMessagesControllerBeingDismissed: Bool = false
     internal var isMessagesControllerBeingDismissed: Bool = false
 
 
-    internal var selectedIndexPathForMenu: IndexPath?
-
     internal var messageCollectionViewBottomInset: CGFloat = 0 {
     internal var messageCollectionViewBottomInset: CGFloat = 0 {
         didSet {
         didSet {
             messagesCollectionView.contentInset.bottom = messageCollectionViewBottomInset
             messagesCollectionView.contentInset.bottom = messageCollectionViewBottomInset
@@ -121,6 +125,13 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
         adjustScrollViewTopInset()
         adjustScrollViewTopInset()
     }
     }
 
 
+    open override func viewSafeAreaInsetsDidChange() {
+        if #available(iOS 11.0, *) {
+            super.viewSafeAreaInsetsDidChange()
+        }
+        messageCollectionViewBottomInset = requiredInitialScrollViewBottomInset()
+    }
+
     // MARK: - Initializers
     // MARK: - Initializers
 
 
     deinit {
     deinit {
@@ -165,24 +176,87 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
         }
         }
     }
     }
 
 
+    // MARK: - Typing Indicator API
+
+    /// Sets the typing indicator sate by inserting/deleting the `TypingBubbleCell`
+    ///
+    /// - Parameters:
+    ///   - isHidden: A Boolean value that is to be the new state of the typing indicator
+    ///   - animated: A Boolean value determining if the insertion is to be animated
+    ///   - updates: A block of code that will be executed during `performBatchUpdates`
+    ///              when `animated` is `TRUE` or before the `completion` block executes
+    ///              when `animated` is `FALSE`
+    ///   - completion: A completion block to execute after the insertion/deletion
+    open func setTypingIndicatorViewHidden(_ isHidden: Bool, animated: Bool, whilePerforming updates: (() -> Void)? = nil, completion: ((Bool) -> Void)? = nil) {
+
+        guard isTypingIndicatorHidden != isHidden else {
+            completion?(false)
+            return
+        }
+
+        let section = messagesCollectionView.numberOfSections
+        messagesCollectionView.setTypingIndicatorViewHidden(isHidden)
+
+        if animated {
+            messagesCollectionView.performBatchUpdates({ [weak self] in
+                self?.performUpdatesForTypingIndicatorVisability(at: section)
+                updates?()
+                }, completion: completion)
+        } else {
+            performUpdatesForTypingIndicatorVisability(at: section)
+            updates?()
+            completion?(true)
+        }
+    }
+
+    /// Performs a delete or insert on the `MessagesCollectionView` on the provided section
+    ///
+    /// - Parameter section: The index to modify
+    private func performUpdatesForTypingIndicatorVisability(at section: Int) {
+        if isTypingIndicatorHidden {
+            messagesCollectionView.deleteSections([section - 1])
+        } else {
+            messagesCollectionView.insertSections([section])
+        }
+    }
+
+    /// A method that by default checks if the section is the last in the
+    /// `messagesCollectionView` and that `isTypingIndicatorViewHidden`
+    /// is FALSE
+    ///
+    /// - Parameter section
+    /// - Returns: A Boolean indicating if the TypingIndicator should be presented at the given section
+    public func isSectionReservedForTypingIndicator(_ section: Int) -> Bool {
+        return !messagesCollectionView.isTypingIndicatorHidden && section == self.numberOfSections(in: messagesCollectionView) - 1
+    }
+
     // MARK: - UICollectionViewDataSource
     // MARK: - UICollectionViewDataSource
 
 
     open func numberOfSections(in collectionView: UICollectionView) -> Int {
     open func numberOfSections(in collectionView: UICollectionView) -> Int {
         guard let collectionView = collectionView as? MessagesCollectionView else {
         guard let collectionView = collectionView as? MessagesCollectionView else {
             fatalError(MessageKitError.notMessagesCollectionView)
             fatalError(MessageKitError.notMessagesCollectionView)
         }
         }
-        return collectionView.messagesDataSource?.numberOfSections(in: collectionView) ?? 0
+        let sections = collectionView.messagesDataSource?.numberOfSections(in: collectionView) ?? 0
+        return collectionView.isTypingIndicatorHidden ? sections : sections + 1
     }
     }
 
 
     open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
     open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
         guard let collectionView = collectionView as? MessagesCollectionView else {
         guard let collectionView = collectionView as? MessagesCollectionView else {
             fatalError(MessageKitError.notMessagesCollectionView)
             fatalError(MessageKitError.notMessagesCollectionView)
         }
         }
+        if isSectionReservedForTypingIndicator(section) {
+            return 1
+        }
         return collectionView.messagesDataSource?.numberOfItems(inSection: section, in: collectionView) ?? 0
         return collectionView.messagesDataSource?.numberOfItems(inSection: section, in: collectionView) ?? 0
     }
     }
 
 
-    /// Note:
-    ///   If you override this method, remember to call MessagesDataSource's customCell(for:at:in:) for MessageKind.custom messages, if necessary
+    /// Notes:
+    /// - If you override this method, remember to call MessagesDataSource's customCell(for:at:in:)
+    /// for MessageKind.custom messages, if necessary.
+    ///
+    /// - If you are using the typing indicator you will need to ensure that the section is not
+    /// reserved for it with `isSectionReservedForTypingIndicator` defined in
+    /// `MessagesCollectionViewFlowLayout`
     open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
     open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 
 
         guard let messagesCollectionView = collectionView as? MessagesCollectionView else {
         guard let messagesCollectionView = collectionView as? MessagesCollectionView else {
@@ -193,6 +267,10 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
             fatalError(MessageKitError.nilMessagesDataSource)
             fatalError(MessageKitError.nilMessagesDataSource)
         }
         }
 
 
+        if isSectionReservedForTypingIndicator(indexPath.section) {
+            return messagesDataSource.typingIndicator(at: indexPath, in: messagesCollectionView)
+        }
+
         let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
         let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
 
 
         switch message.kind {
         switch message.kind {
@@ -208,6 +286,14 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
             let cell = messagesCollectionView.dequeueReusableCell(LocationMessageCell.self, for: indexPath)
             let cell = messagesCollectionView.dequeueReusableCell(LocationMessageCell.self, for: indexPath)
             cell.configure(with: message, at: indexPath, and: messagesCollectionView)
             cell.configure(with: message, at: indexPath, and: messagesCollectionView)
             return cell
             return cell
+        case .audio:
+            let cell = messagesCollectionView.dequeueReusableCell(AudioMessageCell.self, for: indexPath)
+            cell.configure(with: message, at: indexPath, and: messagesCollectionView)
+            return cell
+        case .contact:
+            let cell = messagesCollectionView.dequeueReusableCell(ContactMessageCell.self, for: indexPath)
+            cell.configure(with: message, at: indexPath, and: messagesCollectionView)
+            return cell
         case .custom:
         case .custom:
             return messagesDataSource.customCell(for: message, at: indexPath, in: messagesCollectionView)
             return messagesDataSource.customCell(for: message, at: indexPath, in: messagesCollectionView)
         }
         }
@@ -248,9 +334,17 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
         guard let layoutDelegate = messagesCollectionView.messagesLayoutDelegate else {
         guard let layoutDelegate = messagesCollectionView.messagesLayoutDelegate else {
             fatalError(MessageKitError.nilMessagesLayoutDelegate)
             fatalError(MessageKitError.nilMessagesLayoutDelegate)
         }
         }
+        if isSectionReservedForTypingIndicator(section) {
+            return .zero
+        }
         return layoutDelegate.headerViewSize(for: section, in: messagesCollectionView)
         return layoutDelegate.headerViewSize(for: section, in: messagesCollectionView)
     }
     }
 
 
+    open func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+        guard let cell = cell as? TypingIndicatorCell else { return }
+        cell.typingBubble.startAnimating()
+    }
+
     open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
     open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
         guard let messagesCollectionView = collectionView as? MessagesCollectionView else {
         guard let messagesCollectionView = collectionView as? MessagesCollectionView else {
             fatalError(MessageKitError.notMessagesCollectionView)
             fatalError(MessageKitError.notMessagesCollectionView)
@@ -258,11 +352,19 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
         guard let layoutDelegate = messagesCollectionView.messagesLayoutDelegate else {
         guard let layoutDelegate = messagesCollectionView.messagesLayoutDelegate else {
             fatalError(MessageKitError.nilMessagesLayoutDelegate)
             fatalError(MessageKitError.nilMessagesLayoutDelegate)
         }
         }
+        if isSectionReservedForTypingIndicator(section) {
+            return .zero
+        }
         return layoutDelegate.footerViewSize(for: section, in: messagesCollectionView)
         return layoutDelegate.footerViewSize(for: section, in: messagesCollectionView)
     }
     }
 
 
     open func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
     open func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
         guard let messagesDataSource = messagesCollectionView.messagesDataSource else { return false }
         guard let messagesDataSource = messagesCollectionView.messagesDataSource else { return false }
+
+        if isSectionReservedForTypingIndicator(indexPath.section) {
+            return false
+        }
+
         let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
         let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
 
 
         switch message.kind {
         switch message.kind {
@@ -275,6 +377,9 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
     }
     }
 
 
     open func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
     open func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
+        if isSectionReservedForTypingIndicator(indexPath.section) {
+            return false
+        }
         return (action == NSSelectorFromString("copy:"))
         return (action == NSSelectorFromString("copy:"))
     }
     }
 
 

+ 3 - 3
Pods/MessageKit/Sources/Extensions/Bundle+Extensions.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,9 @@
 
 
 import Foundation
 import Foundation
 
 
-extension Bundle {
+internal extension Bundle {
 
 
-    internal static func messageKitAssetBundle() -> Bundle {
+    static func messageKitAssetBundle() -> Bundle { // swiftlint:disable:this explicit_acl
         let podBundle = Bundle(for: MessagesViewController.self)
         let podBundle = Bundle(for: MessagesViewController.self)
         
         
         guard let resourceBundleUrl = podBundle.url(forResource: "MessageKitAssets", withExtension: "bundle") else {
         guard let resourceBundleUrl = podBundle.url(forResource: "MessageKitAssets", withExtension: "bundle") else {

+ 3 - 3
Pods/MessageKit/Sources/Extensions/CGRect+Extensions.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,9 @@
 
 
 import Foundation
 import Foundation
 
 
-extension CGRect {
+internal extension CGRect {
     
     
-    internal init(_ x: CGFloat, _ y: CGFloat, _ w: CGFloat, _ h: CGFloat) {
+    init(_ x: CGFloat, _ y: CGFloat, _ w: CGFloat, _ h: CGFloat) { // swiftlint:disable:this explicit_acl
         self.init(x: x, y: y, width: w, height: h)
         self.init(x: x, y: y, width: w, height: h)
     }
     }
 
 

+ 3 - 3
Pods/MessageKit/Sources/Extensions/NSAttributedString+Extensions.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,9 @@
 
 
 import Foundation
 import Foundation
 
 
-extension NSAttributedString {
+internal extension NSAttributedString {
 
 
-    internal func width(considering height: CGFloat) -> CGFloat {
+    func width(considering height: CGFloat) -> CGFloat { // swiftlint:disable:this explicit_acl
 
 
         let constraintBox = CGSize(width: .greatestFiniteMagnitude, height: height)
         let constraintBox = CGSize(width: .greatestFiniteMagnitude, height: height)
         let rect = self.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
         let rect = self.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)

+ 9 - 7
Pods/MessageKit/Sources/Extensions/UIColor+Extensions.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -24,16 +24,18 @@
 
 
 import Foundation
 import Foundation
 
 
-extension UIColor {
+// swiftlint:disable explicit_acl
 
 
-    internal static let incomingGray = UIColor(red: 230/255, green: 230/255, blue: 235/255, alpha: 1.0)
+internal extension UIColor {
 
 
-    internal static let outgoingGreen = UIColor(red: 69/255, green: 214/255, blue: 93/255, alpha: 1.0)
+    static let incomingGray = UIColor(red: 230/255, green: 230/255, blue: 235/255, alpha: 1.0)
 
 
-    internal static let inputBarGray = UIColor(red: 247/255, green: 247/255, blue: 247/255, alpha: 1.0)
+    static let outgoingGreen = UIColor(red: 69/255, green: 214/255, blue: 93/255, alpha: 1.0)
 
 
-    internal static let playButtonLightGray = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1.0)
+    static let inputBarGray = UIColor(red: 247/255, green: 247/255, blue: 247/255, alpha: 1.0)
 
 
-    internal static let sendButtonBlue = UIColor(red: 15/255, green: 135/255, blue: 255/255, alpha: 1.0)
+    static let playButtonLightGray = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1.0)
+
+    static let sendButtonBlue = UIColor(red: 15/255, green: 135/255, blue: 255/255, alpha: 1.0)
 
 
 }
 }

+ 6 - 4
Pods/MessageKit/Sources/Extensions/UIEdgeInsets+Extensions.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -24,13 +24,15 @@
 
 
 import Foundation
 import Foundation
 
 
-extension UIEdgeInsets {
+// swiftlint:disable explicit_acl
 
 
-    internal var vertical: CGFloat {
+internal extension UIEdgeInsets {
+
+    var vertical: CGFloat {
         return top + bottom
         return top + bottom
     }
     }
 
 
-    internal var horizontal: CGFloat {
+    var horizontal: CGFloat {
         return left + right
         return left + right
     }
     }
 
 

+ 14 - 15
Pods/MessageInputBar/Sources/Protocols/InputItem.swift → Pods/MessageKit/Sources/Extensions/UIImage+Extension.swift

@@ -22,23 +22,22 @@
  SOFTWARE.
  SOFTWARE.
  */
  */
 
 
+public enum ImageType: String {
+    case play
+    case pause
+    case disclouser
+}
+
 import UIKit
 import UIKit
 
 
-/// InputItem is a protocol that links elements to the MessageInputBar to make them reactive
-public protocol InputItem: AnyObject {
-    
-    /// A reference to the MessageInputBar. Set automatically when inserted into an InputStackView
-    var messageInputBar: MessageInputBar? { get set }
-    
-    /// A reference to the InputStackView that the InputItem is contained in. Set when inserted into an InputStackView
-    var parentStackViewPosition: InputStackView.Position? { get set }
-    
-    /// A hook that is called when the InputTextView's text is changed
-    func textViewDidChangeAction(with textView: InputTextView)
+/// This extension provide a way to access image resources with in framework
+public extension UIImage {
     
     
-    /// A hook that is called when the InputTextView is resigned as the first responder
-    func keyboardEditingEndsAction()
+    public class func messageKitImageWith(type: ImageType) -> UIImage? {
+        let assetBundle = Bundle.messageKitAssetBundle()
+        let imagePath = assetBundle.path(forResource: type.rawValue, ofType: "png", inDirectory: "Images")
+        let image = UIImage(contentsOfFile: imagePath ?? "")
+        return image
+    }
     
     
-    /// A hook that is called when the InputTextView is made the first responder
-    func keyboardEditingBeginsAction()
 }
 }

+ 20 - 6
Pods/MessageKit/Sources/Extensions/UIView+Extensions.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,11 @@
 
 
 import UIKit
 import UIKit
 
 
-extension UIView {
+// swiftlint:disable explicit_acl
+
+internal extension UIView {
     
     
-    internal func fillSuperview() {
+    func fillSuperview() {
         guard let superview = self.superview else {
         guard let superview = self.superview else {
             return
             return
         }
         }
@@ -41,7 +43,7 @@ extension UIView {
 	    NSLayoutConstraint.activate(constraints)
 	    NSLayoutConstraint.activate(constraints)
     }
     }
 
 
-    internal func centerInSuperview() {
+    func centerInSuperview() {
         guard let superview = self.superview else {
         guard let superview = self.superview else {
             return
             return
         }
         }
@@ -53,7 +55,7 @@ extension UIView {
         NSLayoutConstraint.activate(constraints)
         NSLayoutConstraint.activate(constraints)
     }
     }
     
     
-    internal func constraint(equalTo size: CGSize) {
+    func constraint(equalTo size: CGSize) {
         guard superview != nil else { return }
         guard superview != nil else { return }
         translatesAutoresizingMaskIntoConstraints = false
         translatesAutoresizingMaskIntoConstraints = false
         let constraints: [NSLayoutConstraint] = [
         let constraints: [NSLayoutConstraint] = [
@@ -65,7 +67,7 @@ extension UIView {
     }
     }
 
 
     @discardableResult
     @discardableResult
-    internal func addConstraints(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
+    internal func addConstraints(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, centerY: NSLayoutYAxisAnchor? = nil, centerX: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, centerYConstant: CGFloat = 0, centerXConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
         
         
         if self.superview == nil {
         if self.superview == nil {
             return []
             return []
@@ -97,6 +99,18 @@ extension UIView {
             constraint.identifier = "right"
             constraint.identifier = "right"
             constraints.append(constraint)
             constraints.append(constraint)
         }
         }
+
+        if let centerY = centerY {
+            let constraint = centerYAnchor.constraint(equalTo: centerY, constant: centerYConstant)
+            constraint.identifier = "centerY"
+            constraints.append(constraint)
+        }
+
+        if let centerX = centerX {
+            let constraint = centerXAnchor.constraint(equalTo: centerX, constant: centerXConstant)
+            constraint.identifier = "centerX"
+            constraints.append(constraint)
+        }
         
         
         if widthConstant > 0 {
         if widthConstant > 0 {
             let constraint = widthAnchor.constraint(equalToConstant: widthConstant)
             let constraint = widthAnchor.constraint(equalToConstant: widthConstant)

+ 43 - 0
Pods/MessageKit/Sources/Layout/AudioMessageSizeCalculator.swift

@@ -0,0 +1,43 @@
+/*
+ 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
+
+open class AudioMessageSizeCalculator: MessageSizeCalculator {
+
+    open override func messageContainerSize(for message: MessageType) -> CGSize {
+        switch message.kind {
+        case .audio(let item):
+            let maxWidth = messageContainerMaxWidth(for: message)
+            if maxWidth < item.size.width {
+                // Maintain the ratio if width is too great
+                let height = maxWidth * item.size.height / item.size.width
+                return CGSize(width: maxWidth, height: height)
+            }
+            return item.size
+        default:
+            fatalError("messageContainerSize received unhandled MessageDataType: \(message.kind)")
+        }
+    }
+}

+ 1 - 1
Pods/MessageKit/Sources/Layout/CellSizeCalculator.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 73 - 0
Pods/MessageKit/Sources/Layout/ContactMessageSizeCalculator.swift

@@ -0,0 +1,73 @@
+/*
+ MIT License
+ 
+ Copyright (c) 2017-2018 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
+
+open class ContactMessageSizeCalculator: MessageSizeCalculator {
+    
+    public var incomingMessageNameLabelInsets = UIEdgeInsets(top: 7, left: 46, bottom: 7, right: 30)
+    public var outgoingMessageNameLabelInsets = UIEdgeInsets(top: 7, left: 41, bottom: 7, right: 35)
+    public var contactLabelFont = UIFont.preferredFont(forTextStyle: .body)
+    
+    internal func contactLabelInsets(for message: MessageType) -> UIEdgeInsets {
+        let dataSource = messagesLayout.messagesDataSource
+        let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
+        return isFromCurrentSender ? outgoingMessageNameLabelInsets : incomingMessageNameLabelInsets
+    }
+    
+    open override func messageContainerMaxWidth(for message: MessageType) -> CGFloat {
+        let maxWidth = super.messageContainerMaxWidth(for: message)
+        let textInsets = contactLabelInsets(for: message)
+        return maxWidth - textInsets.horizontal
+    }
+    
+    open override func messageContainerSize(for message: MessageType) -> CGSize {
+        let maxWidth = messageContainerMaxWidth(for: message)
+        
+        var messageContainerSize: CGSize
+        let attributedText: NSAttributedString
+        
+        switch message.kind {
+        case .contact(let item):
+            attributedText = NSAttributedString(string: item.displayName, attributes: [.font: contactLabelFont])
+        default:
+            fatalError("messageContainerSize received unhandled MessageDataType: \(message.kind)")
+        }
+        
+        messageContainerSize = labelSize(for: attributedText, considering: maxWidth)
+        
+        let messageInsets = contactLabelInsets(for: message)
+        messageContainerSize.width += messageInsets.horizontal
+        messageContainerSize.height += messageInsets.vertical
+        
+        return messageContainerSize
+    }
+    
+    open override func configure(attributes: UICollectionViewLayoutAttributes) {
+        super.configure(attributes: attributes)
+        guard let attributes = attributes as? MessagesCollectionViewLayoutAttributes else { return }
+        attributes.messageLabelFont = contactLabelFont
+    }
+
+}

+ 1 - 1
Pods/MessageKit/Sources/Layout/LocationMessageSizeCalculator.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Pods/MessageKit/Sources/Layout/MediaMessageSizeCalculator.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 42 - 7
Pods/MessageKit/Sources/Layout/MessageSizeCalculator.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -38,11 +38,16 @@ open class MessageSizeCalculator: CellSizeCalculator {
     public var incomingAvatarPosition = AvatarPosition(vertical: .cellBottom)
     public var incomingAvatarPosition = AvatarPosition(vertical: .cellBottom)
     public var outgoingAvatarPosition = AvatarPosition(vertical: .cellBottom)
     public var outgoingAvatarPosition = AvatarPosition(vertical: .cellBottom)
 
 
+    public var avatarLeadingTrailingPadding: CGFloat = 0
+
     public var incomingMessagePadding = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 30)
     public var incomingMessagePadding = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 30)
     public var outgoingMessagePadding = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 4)
     public var outgoingMessagePadding = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 4)
 
 
     public var incomingCellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
     public var incomingCellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
     public var outgoingCellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
     public var outgoingCellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
+    
+    public var incomingCellBottomLabelAlignment = LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(left: 42))
+    public var outgoingCellBottomLabelAlignment = LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(right: 42))
 
 
     public var incomingMessageTopLabelAlignment = LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(left: 42))
     public var incomingMessageTopLabelAlignment = LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(left: 42))
     public var outgoingMessageTopLabelAlignment = LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(right: 42))
     public var outgoingMessageTopLabelAlignment = LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(right: 42))
@@ -55,6 +60,9 @@ open class MessageSizeCalculator: CellSizeCalculator {
 
 
     public var incomingAccessoryViewPadding = HorizontalEdgeInsets.zero
     public var incomingAccessoryViewPadding = HorizontalEdgeInsets.zero
     public var outgoingAccessoryViewPadding = HorizontalEdgeInsets.zero
     public var outgoingAccessoryViewPadding = HorizontalEdgeInsets.zero
+    
+    public var incomingAccessoryViewPosition: AccessoryPosition = .messageCenter
+    public var outgoingAccessoryViewPosition: AccessoryPosition = .messageCenter
 
 
     open override func configure(attributes: UICollectionViewLayoutAttributes) {
     open override func configure(attributes: UICollectionViewLayoutAttributes) {
         guard let attributes = attributes as? MessagesCollectionViewLayoutAttributes else { return }
         guard let attributes = attributes as? MessagesCollectionViewLayoutAttributes else { return }
@@ -65,10 +73,13 @@ open class MessageSizeCalculator: CellSizeCalculator {
 
 
         attributes.avatarSize = avatarSize(for: message)
         attributes.avatarSize = avatarSize(for: message)
         attributes.avatarPosition = avatarPosition(for: message)
         attributes.avatarPosition = avatarPosition(for: message)
+        attributes.avatarLeadingTrailingPadding = avatarLeadingTrailingPadding
 
 
         attributes.messageContainerPadding = messageContainerPadding(for: message)
         attributes.messageContainerPadding = messageContainerPadding(for: message)
         attributes.messageContainerSize = messageContainerSize(for: message)
         attributes.messageContainerSize = messageContainerSize(for: message)
         attributes.cellTopLabelSize = cellTopLabelSize(for: message, at: indexPath)
         attributes.cellTopLabelSize = cellTopLabelSize(for: message, at: indexPath)
+        attributes.cellBottomLabelSize = cellBottomLabelSize(for: message, at: indexPath)
+        attributes.cellBottomLabelAlignment = cellBottomLabelAlignment(for: message)
         attributes.messageTopLabelSize = messageTopLabelSize(for: message, at: indexPath)
         attributes.messageTopLabelSize = messageTopLabelSize(for: message, at: indexPath)
         attributes.messageTopLabelAlignment = messageTopLabelAlignment(for: message)
         attributes.messageTopLabelAlignment = messageTopLabelAlignment(for: message)
 
 
@@ -77,6 +88,7 @@ open class MessageSizeCalculator: CellSizeCalculator {
 
 
         attributes.accessoryViewSize = accessoryViewSize(for: message)
         attributes.accessoryViewSize = accessoryViewSize(for: message)
         attributes.accessoryViewPadding = accessoryViewPadding(for: message)
         attributes.accessoryViewPadding = accessoryViewPadding(for: message)
+        attributes.accessoryViewPosition = accessoryViewPosition(for: message)
     }
     }
 
 
     open override func sizeForItem(at indexPath: IndexPath) -> CGSize {
     open override func sizeForItem(at indexPath: IndexPath) -> CGSize {
@@ -89,6 +101,7 @@ open class MessageSizeCalculator: CellSizeCalculator {
     open func cellContentHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
     open func cellContentHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat {
 
 
         let messageContainerHeight = messageContainerSize(for: message).height
         let messageContainerHeight = messageContainerSize(for: message).height
+        let cellBottomLabelHeight = cellBottomLabelSize(for: message, at: indexPath).height
         let messageBottomLabelHeight = messageBottomLabelSize(for: message, at: indexPath).height
         let messageBottomLabelHeight = messageBottomLabelSize(for: message, at: indexPath).height
         let cellTopLabelHeight = cellTopLabelSize(for: message, at: indexPath).height
         let cellTopLabelHeight = cellTopLabelSize(for: message, at: indexPath).height
         let messageTopLabelHeight = messageTopLabelSize(for: message, at: indexPath).height
         let messageTopLabelHeight = messageTopLabelSize(for: message, at: indexPath).height
@@ -100,12 +113,13 @@ open class MessageSizeCalculator: CellSizeCalculator {
         switch avatarVerticalPosition {
         switch avatarVerticalPosition {
         case .messageCenter:
         case .messageCenter:
             let totalLabelHeight: CGFloat = cellTopLabelHeight + messageTopLabelHeight
             let totalLabelHeight: CGFloat = cellTopLabelHeight + messageTopLabelHeight
-                + messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight
+                + messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + cellBottomLabelHeight
             let cellHeight = max(avatarHeight, totalLabelHeight)
             let cellHeight = max(avatarHeight, totalLabelHeight)
             return max(cellHeight, accessoryViewHeight)
             return max(cellHeight, accessoryViewHeight)
         case .messageBottom:
         case .messageBottom:
             var cellHeight: CGFloat = 0
             var cellHeight: CGFloat = 0
             cellHeight += messageBottomLabelHeight
             cellHeight += messageBottomLabelHeight
+            cellHeight += cellBottomLabelHeight
             let labelsHeight = messageContainerHeight + messageVerticalPadding + cellTopLabelHeight + messageTopLabelHeight
             let labelsHeight = messageContainerHeight + messageVerticalPadding + cellTopLabelHeight + messageTopLabelHeight
             cellHeight += max(labelsHeight, avatarHeight)
             cellHeight += max(labelsHeight, avatarHeight)
             return max(cellHeight, accessoryViewHeight)
             return max(cellHeight, accessoryViewHeight)
@@ -113,18 +127,18 @@ open class MessageSizeCalculator: CellSizeCalculator {
             var cellHeight: CGFloat = 0
             var cellHeight: CGFloat = 0
             cellHeight += cellTopLabelHeight
             cellHeight += cellTopLabelHeight
             cellHeight += messageTopLabelHeight
             cellHeight += messageTopLabelHeight
-            let labelsHeight = messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight
+            let labelsHeight = messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + cellBottomLabelHeight
             cellHeight += max(labelsHeight, avatarHeight)
             cellHeight += max(labelsHeight, avatarHeight)
             return max(cellHeight, accessoryViewHeight)
             return max(cellHeight, accessoryViewHeight)
         case .messageLabelTop:
         case .messageLabelTop:
             var cellHeight: CGFloat = 0
             var cellHeight: CGFloat = 0
             cellHeight += cellTopLabelHeight
             cellHeight += cellTopLabelHeight
-            let messageLabelsHeight = messageContainerHeight + messageBottomLabelHeight + messageVerticalPadding + messageTopLabelHeight
+            let messageLabelsHeight = messageContainerHeight + messageBottomLabelHeight + messageVerticalPadding + messageTopLabelHeight + cellBottomLabelHeight
             cellHeight += max(messageLabelsHeight, avatarHeight)
             cellHeight += max(messageLabelsHeight, avatarHeight)
             return max(cellHeight, accessoryViewHeight)
             return max(cellHeight, accessoryViewHeight)
         case .cellTop, .cellBottom:
         case .cellTop, .cellBottom:
             let totalLabelHeight: CGFloat = cellTopLabelHeight + messageTopLabelHeight
             let totalLabelHeight: CGFloat = cellTopLabelHeight + messageTopLabelHeight
-                + messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight
+                + messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + cellBottomLabelHeight
             let cellHeight = max(avatarHeight, totalLabelHeight)
             let cellHeight = max(avatarHeight, totalLabelHeight)
             return max(cellHeight, accessoryViewHeight)
             return max(cellHeight, accessoryViewHeight)
         }
         }
@@ -181,8 +195,23 @@ open class MessageSizeCalculator: CellSizeCalculator {
         let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
         let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
         return isFromCurrentSender ? outgoingMessageTopLabelAlignment : incomingMessageTopLabelAlignment
         return isFromCurrentSender ? outgoingMessageTopLabelAlignment : incomingMessageTopLabelAlignment
     }
     }
+    
+    // MARK: - Bottom cell Label
+    
+    open func cellBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
+        let layoutDelegate = messagesLayout.messagesLayoutDelegate
+        let collectionView = messagesLayout.messagesCollectionView
+        let height = layoutDelegate.cellBottomLabelHeight(for: message, at: indexPath, in: collectionView)
+        return CGSize(width: messagesLayout.itemWidth, height: height)
+    }
+    
+    open func cellBottomLabelAlignment(for message: MessageType) -> LabelAlignment {
+        let dataSource = messagesLayout.messagesDataSource
+        let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
+        return isFromCurrentSender ? outgoingCellBottomLabelAlignment : incomingCellBottomLabelAlignment
+    }
 
 
-    // MARK: - Bottom Label
+    // MARK: - Bottom Message Label
 
 
     open func messageBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
     open func messageBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
         let layoutDelegate = messagesLayout.messagesLayoutDelegate
         let layoutDelegate = messagesLayout.messagesLayoutDelegate
@@ -210,6 +239,12 @@ open class MessageSizeCalculator: CellSizeCalculator {
         let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
         let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
         return isFromCurrentSender ? outgoingAccessoryViewPadding : incomingAccessoryViewPadding
         return isFromCurrentSender ? outgoingAccessoryViewPadding : incomingAccessoryViewPadding
     }
     }
+    
+    public func accessoryViewPosition(for message: MessageType) -> AccessoryPosition {
+        let dataSource = messagesLayout.messagesDataSource
+        let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
+        return isFromCurrentSender ? outgoingAccessoryViewPosition : incomingAccessoryViewPosition
+    }
 
 
     // MARK: - MessageContainer
     // MARK: - MessageContainer
 
 
@@ -229,7 +264,7 @@ open class MessageSizeCalculator: CellSizeCalculator {
         let messagePadding = messageContainerPadding(for: message)
         let messagePadding = messageContainerPadding(for: message)
         let accessoryWidth = accessoryViewSize(for: message).width
         let accessoryWidth = accessoryViewSize(for: message).width
         let accessoryPadding = accessoryViewPadding(for: message)
         let accessoryPadding = accessoryViewPadding(for: message)
-        return messagesLayout.itemWidth - avatarWidth - messagePadding.horizontal - accessoryWidth - accessoryPadding.horizontal
+        return messagesLayout.itemWidth - avatarWidth - messagePadding.horizontal - accessoryWidth - accessoryPadding.horizontal - avatarLeadingTrailingPadding
     }
     }
 
 
     // MARK: - Helpers
     // MARK: - Helpers

+ 75 - 9
Pods/MessageKit/Sources/Layout/MessagesCollectionViewFlowLayout.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -62,18 +62,18 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
         return collectionView.frame.width - sectionInset.left - sectionInset.right
         return collectionView.frame.width - sectionInset.left - sectionInset.right
     }
     }
 
 
+    public private(set) var isTypingIndicatorViewHidden: Bool = true
+
     // MARK: - Initializers
     // MARK: - Initializers
 
 
     public override init() {
     public override init() {
         super.init()
         super.init()
-        
         setupView()
         setupView()
         setupObserver()
         setupObserver()
     }
     }
 
 
     required public init?(coder aDecoder: NSCoder) {
     required public init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
         super.init(coder: aDecoder)
-        
         setupView()
         setupView()
         setupObserver()
         setupObserver()
     }
     }
@@ -92,6 +92,26 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
         NotificationCenter.default.addObserver(self, selector: #selector(MessagesCollectionViewFlowLayout.handleOrientationChange(_:)), name: UIDevice.orientationDidChangeNotification, object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(MessagesCollectionViewFlowLayout.handleOrientationChange(_:)), name: UIDevice.orientationDidChangeNotification, object: nil)
     }
     }
 
 
+    // MARK: - Typing Indicator API
+
+    /// Notifies the layout that the typing indicator will change state
+    ///
+    /// - Parameters:
+    ///   - isHidden: A Boolean value that is to be the new state of the typing indicator
+    internal func setTypingIndicatorViewHidden(_ isHidden: Bool) {
+        isTypingIndicatorViewHidden = isHidden
+    }
+
+    /// A method that by default checks if the section is the last in the
+    /// `messagesCollectionView` and that `isTypingIndicatorViewHidden`
+    /// is FALSE
+    ///
+    /// - Parameter section
+    /// - Returns: A Boolean indicating if the TypingIndicator should be presented at the given section
+    open func isSectionReservedForTypingIndicator(_ section: Int) -> Bool {
+        return !isTypingIndicatorViewHidden && section == messagesCollectionView.numberOfSections - 1
+    }
+
     // MARK: - Attributes
     // MARK: - Attributes
 
 
     open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
     open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
@@ -146,10 +166,19 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
     lazy open var photoMessageSizeCalculator = MediaMessageSizeCalculator(layout: self)
     lazy open var photoMessageSizeCalculator = MediaMessageSizeCalculator(layout: self)
     lazy open var videoMessageSizeCalculator = MediaMessageSizeCalculator(layout: self)
     lazy open var videoMessageSizeCalculator = MediaMessageSizeCalculator(layout: self)
     lazy open var locationMessageSizeCalculator = LocationMessageSizeCalculator(layout: self)
     lazy open var locationMessageSizeCalculator = LocationMessageSizeCalculator(layout: self)
+    lazy open var audioMessageSizeCalculator = AudioMessageSizeCalculator(layout: self)
+    lazy open var contactMessageSizeCalculator = ContactMessageSizeCalculator(layout: self)
+    lazy open var typingIndicatorSizeCalculator = TypingCellSizeCalculator(layout: self)
 
 
-    /// - Note:
-    ///   If you override this method, remember to call MessageLayoutDelegate's customCellSizeCalculator(for:at:in:) method for MessageKind.custom messages, if necessary
+    /// Note:
+    /// - If you override this method, remember to call MessageLayoutDelegate's
+    /// customCellSizeCalculator(for:at:in:) method for MessageKind.custom messages, if necessary
+    /// - If you are using the typing indicator be sure to return the `typingIndicatorSizeCalculator`
+    /// when the section is reserved for it, indicated by `isSectionReservedForTypingIndicator`
     open func cellSizeCalculatorForItem(at indexPath: IndexPath) -> CellSizeCalculator {
     open func cellSizeCalculatorForItem(at indexPath: IndexPath) -> CellSizeCalculator {
+        if isSectionReservedForTypingIndicator(indexPath.section) {
+            return typingIndicatorSizeCalculator
+        }
         let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
         let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
         switch message.kind {
         switch message.kind {
         case .text:
         case .text:
@@ -164,6 +193,10 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
             return videoMessageSizeCalculator
             return videoMessageSizeCalculator
         case .location:
         case .location:
             return locationMessageSizeCalculator
             return locationMessageSizeCalculator
+        case .audio:
+            return audioMessageSizeCalculator
+        case .contact:
+            return contactMessageSizeCalculator
         case .custom:
         case .custom:
             return messagesLayoutDelegate.customCellSizeCalculator(for: message, at: indexPath, in: messagesCollectionView)
             return messagesLayoutDelegate.customCellSizeCalculator(for: message, at: indexPath, in: messagesCollectionView)
         }
         }
@@ -193,6 +226,11 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
     public func setMessageOutgoingAvatarPosition(_ newPosition: AvatarPosition) {
     public func setMessageOutgoingAvatarPosition(_ newPosition: AvatarPosition) {
         messageSizeCalculators().forEach { $0.outgoingAvatarPosition = newPosition }
         messageSizeCalculators().forEach { $0.outgoingAvatarPosition = newPosition }
     }
     }
+
+    /// Set `avatarLeadingTrailingPadding` of all `MessageSizeCalculator`s
+    public func setAvatarLeadingTrailingPadding(_ newPadding: CGFloat) {
+        messageSizeCalculators().forEach { $0.avatarLeadingTrailingPadding = newPadding }
+    }
     
     
     /// Set `incomingMessagePadding` of all `MessageSizeCalculator`s
     /// Set `incomingMessagePadding` of all `MessageSizeCalculator`s
     public func setMessageIncomingMessagePadding(_ newPadding: UIEdgeInsets) {
     public func setMessageIncomingMessagePadding(_ newPadding: UIEdgeInsets) {
@@ -214,6 +252,16 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
         messageSizeCalculators().forEach { $0.outgoingCellTopLabelAlignment = newAlignment }
         messageSizeCalculators().forEach { $0.outgoingCellTopLabelAlignment = newAlignment }
     }
     }
     
     
+    /// Set `incomingCellBottomLabelAlignment` of all `MessageSizeCalculator`s
+    public func setMessageIncomingCellBottomLabelAlignment(_ newAlignment: LabelAlignment) {
+        messageSizeCalculators().forEach { $0.incomingCellBottomLabelAlignment = newAlignment }
+    }
+    
+    /// Set `outgoingCellBottomLabelAlignment` of all `MessageSizeCalculator`s
+    public func setMessageOutgoingCellBottomLabelAlignment(_ newAlignment: LabelAlignment) {
+        messageSizeCalculators().forEach { $0.outgoingCellBottomLabelAlignment = newAlignment }
+    }
+    
     /// Set `incomingMessageTopLabelAlignment` of all `MessageSizeCalculator`s
     /// Set `incomingMessageTopLabelAlignment` of all `MessageSizeCalculator`s
     public func setMessageIncomingMessageTopLabelAlignment(_ newAlignment: LabelAlignment) {
     public func setMessageIncomingMessageTopLabelAlignment(_ newAlignment: LabelAlignment) {
         messageSizeCalculators().forEach { $0.incomingMessageTopLabelAlignment = newAlignment }
         messageSizeCalculators().forEach { $0.incomingMessageTopLabelAlignment = newAlignment }
@@ -239,24 +287,42 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
         messageSizeCalculators().forEach { $0.incomingAccessoryViewSize = newSize }
         messageSizeCalculators().forEach { $0.incomingAccessoryViewSize = newSize }
     }
     }
 
 
-    /// Set `outgoingAvatarSize` of all `MessageSizeCalculator`s
+    /// Set `outgoingAccessoryViewSize` of all `MessageSizeCalculator`s
     public func setMessageOutgoingAccessoryViewSize(_ newSize: CGSize) {
     public func setMessageOutgoingAccessoryViewSize(_ newSize: CGSize) {
         messageSizeCalculators().forEach { $0.outgoingAccessoryViewSize = newSize }
         messageSizeCalculators().forEach { $0.outgoingAccessoryViewSize = newSize }
     }
     }
 
 
-    /// Set `incomingAccessoryViewSize` of all `MessageSizeCalculator`s
+    /// Set `incomingAccessoryViewPadding` of all `MessageSizeCalculator`s
     public func setMessageIncomingAccessoryViewPadding(_ newPadding: HorizontalEdgeInsets) {
     public func setMessageIncomingAccessoryViewPadding(_ newPadding: HorizontalEdgeInsets) {
         messageSizeCalculators().forEach { $0.incomingAccessoryViewPadding = newPadding }
         messageSizeCalculators().forEach { $0.incomingAccessoryViewPadding = newPadding }
     }
     }
 
 
-    /// Set `outgoingAvatarSize` of all `MessageSizeCalculator`s
+    /// Set `outgoingAccessoryViewPadding` of all `MessageSizeCalculator`s
     public func setMessageOutgoingAccessoryViewPadding(_ newPadding: HorizontalEdgeInsets) {
     public func setMessageOutgoingAccessoryViewPadding(_ newPadding: HorizontalEdgeInsets) {
         messageSizeCalculators().forEach { $0.outgoingAccessoryViewPadding = newPadding }
         messageSizeCalculators().forEach { $0.outgoingAccessoryViewPadding = newPadding }
     }
     }
+    
+    /// Set `incomingAccessoryViewPosition` of all `MessageSizeCalculator`s
+    public func setMessageIncomingAccessoryViewPosition(_ newPosition: AccessoryPosition) {
+        messageSizeCalculators().forEach { $0.incomingAccessoryViewPosition = newPosition }
+    }
+    
+    /// Set `outgoingAccessoryViewPosition` of all `MessageSizeCalculator`s
+    public func setMessageOutgoingAccessoryViewPosition(_ newPosition: AccessoryPosition) {
+        messageSizeCalculators().forEach { $0.outgoingAccessoryViewPosition = newPosition }
+    }
 
 
     /// Get all `MessageSizeCalculator`s
     /// Get all `MessageSizeCalculator`s
     open func messageSizeCalculators() -> [MessageSizeCalculator] {
     open func messageSizeCalculators() -> [MessageSizeCalculator] {
-        return [textMessageSizeCalculator, attributedTextMessageSizeCalculator, emojiMessageSizeCalculator, photoMessageSizeCalculator, videoMessageSizeCalculator, locationMessageSizeCalculator]
+        return [textMessageSizeCalculator,
+                attributedTextMessageSizeCalculator,
+                emojiMessageSizeCalculator,
+                photoMessageSizeCalculator,
+                videoMessageSizeCalculator,
+                locationMessageSizeCalculator,
+                audioMessageSizeCalculator,
+                contactMessageSizeCalculator
+        ]
     }
     }
     
     
 }
 }

+ 16 - 3
Pods/MessageKit/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -31,6 +31,7 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
 
 
     public var avatarSize: CGSize = .zero
     public var avatarSize: CGSize = .zero
     public var avatarPosition = AvatarPosition(vertical: .cellBottom)
     public var avatarPosition = AvatarPosition(vertical: .cellBottom)
+    public var avatarLeadingTrailingPadding: CGFloat = 0
 
 
     public var messageContainerSize: CGSize = .zero
     public var messageContainerSize: CGSize = .zero
     public var messageContainerPadding: UIEdgeInsets = .zero
     public var messageContainerPadding: UIEdgeInsets = .zero
@@ -40,6 +41,9 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
     public var cellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
     public var cellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
     public var cellTopLabelSize: CGSize = .zero
     public var cellTopLabelSize: CGSize = .zero
     
     
+    public var cellBottomLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
+    public var cellBottomLabelSize: CGSize = .zero
+    
     public var messageTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
     public var messageTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero)
     public var messageTopLabelSize: CGSize = .zero
     public var messageTopLabelSize: CGSize = .zero
 
 
@@ -48,7 +52,8 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
 
 
     public var accessoryViewSize: CGSize = .zero
     public var accessoryViewSize: CGSize = .zero
     public var accessoryViewPadding: HorizontalEdgeInsets = .zero
     public var accessoryViewPadding: HorizontalEdgeInsets = .zero
-
+    public var accessoryViewPosition: AccessoryPosition = .messageCenter
+    
     // MARK: - Methods
     // MARK: - Methods
 
 
     open override func copy(with zone: NSZone? = nil) -> Any {
     open override func copy(with zone: NSZone? = nil) -> Any {
@@ -56,18 +61,22 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
         let copy = super.copy(with: zone) as! MessagesCollectionViewLayoutAttributes
         let copy = super.copy(with: zone) as! MessagesCollectionViewLayoutAttributes
         copy.avatarSize = avatarSize
         copy.avatarSize = avatarSize
         copy.avatarPosition = avatarPosition
         copy.avatarPosition = avatarPosition
+        copy.avatarLeadingTrailingPadding = avatarLeadingTrailingPadding
         copy.messageContainerSize = messageContainerSize
         copy.messageContainerSize = messageContainerSize
         copy.messageContainerPadding = messageContainerPadding
         copy.messageContainerPadding = messageContainerPadding
         copy.messageLabelFont = messageLabelFont
         copy.messageLabelFont = messageLabelFont
         copy.messageLabelInsets = messageLabelInsets
         copy.messageLabelInsets = messageLabelInsets
         copy.cellTopLabelAlignment = cellTopLabelAlignment
         copy.cellTopLabelAlignment = cellTopLabelAlignment
         copy.cellTopLabelSize = cellTopLabelSize
         copy.cellTopLabelSize = cellTopLabelSize
+        copy.cellBottomLabelAlignment = cellBottomLabelAlignment
+        copy.cellBottomLabelSize = cellBottomLabelSize
         copy.messageTopLabelAlignment = messageTopLabelAlignment
         copy.messageTopLabelAlignment = messageTopLabelAlignment
         copy.messageTopLabelSize = messageTopLabelSize
         copy.messageTopLabelSize = messageTopLabelSize
         copy.messageBottomLabelAlignment = messageBottomLabelAlignment
         copy.messageBottomLabelAlignment = messageBottomLabelAlignment
         copy.messageBottomLabelSize = messageBottomLabelSize
         copy.messageBottomLabelSize = messageBottomLabelSize
         copy.accessoryViewSize = accessoryViewSize
         copy.accessoryViewSize = accessoryViewSize
         copy.accessoryViewPadding = accessoryViewPadding
         copy.accessoryViewPadding = accessoryViewPadding
+        copy.accessoryViewPosition = accessoryViewPosition
         return copy
         return copy
         // swiftlint:enable force_cast
         // swiftlint:enable force_cast
     }
     }
@@ -76,19 +85,23 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu
         // MARK: - LEAVE this as is
         // MARK: - LEAVE this as is
         if let attributes = object as? MessagesCollectionViewLayoutAttributes {
         if let attributes = object as? MessagesCollectionViewLayoutAttributes {
             return super.isEqual(object) && attributes.avatarSize == avatarSize
             return super.isEqual(object) && attributes.avatarSize == avatarSize
-                && attributes.avatarPosition == attributes.avatarPosition
+                && attributes.avatarPosition == avatarPosition
+                && attributes.avatarLeadingTrailingPadding == avatarLeadingTrailingPadding
                 && attributes.messageContainerSize == messageContainerSize
                 && attributes.messageContainerSize == messageContainerSize
                 && attributes.messageContainerPadding == messageContainerPadding
                 && attributes.messageContainerPadding == messageContainerPadding
                 && attributes.messageLabelFont == messageLabelFont
                 && attributes.messageLabelFont == messageLabelFont
                 && attributes.messageLabelInsets == messageLabelInsets
                 && attributes.messageLabelInsets == messageLabelInsets
                 && attributes.cellTopLabelAlignment == cellTopLabelAlignment
                 && attributes.cellTopLabelAlignment == cellTopLabelAlignment
                 && attributes.cellTopLabelSize == cellTopLabelSize
                 && attributes.cellTopLabelSize == cellTopLabelSize
+                && attributes.cellBottomLabelAlignment == cellBottomLabelAlignment
+                && attributes.cellBottomLabelSize == cellBottomLabelSize
                 && attributes.messageTopLabelAlignment == messageTopLabelAlignment
                 && attributes.messageTopLabelAlignment == messageTopLabelAlignment
                 && attributes.messageTopLabelSize == messageTopLabelSize
                 && attributes.messageTopLabelSize == messageTopLabelSize
                 && attributes.messageBottomLabelAlignment == messageBottomLabelAlignment
                 && attributes.messageBottomLabelAlignment == messageBottomLabelAlignment
                 && attributes.messageBottomLabelSize == messageBottomLabelSize
                 && attributes.messageBottomLabelSize == messageBottomLabelSize
                 && attributes.accessoryViewSize == accessoryViewSize
                 && attributes.accessoryViewSize == accessoryViewSize
                 && attributes.accessoryViewPadding == accessoryViewPadding
                 && attributes.accessoryViewPadding == accessoryViewPadding
+                && attributes.accessoryViewPosition == accessoryViewPosition
         } else {
         } else {
             return false
             return false
         }
         }

+ 1 - 1
Pods/MessageKit/Sources/Layout/TextMessageSizeCalculator.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 43 - 0
Pods/MessageKit/Sources/Layout/TypingIndicatorCellSizeCalculator.swift

@@ -0,0 +1,43 @@
+/*
+ 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 UIKit
+
+open class TypingCellSizeCalculator: CellSizeCalculator {
+
+    open var height: CGFloat = 62
+
+    public init(layout: MessagesCollectionViewFlowLayout? = nil) {
+        super.init()
+        self.layout = layout
+    }
+
+    open override func sizeForItem(at indexPath: IndexPath) -> CGSize {
+        guard let layout = layout else { return .zero }
+        let collectionViewWidth = layout.collectionView?.bounds.width ?? 0
+        let contentInset = layout.collectionView?.contentInset ?? .zero
+        let inset = layout.sectionInset.horizontal + contentInset.horizontal
+        return CGSize(width: collectionViewWidth - inset, height: height)
+    }
+}

+ 48 - 0
Pods/MessageKit/Sources/Models/AccessoryPosition.swift

@@ -0,0 +1,48 @@
+/*
+ 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
+
+/// Used to determine the `Horizontal` and `Vertical` position of
+// an `AccessoryView` in a `MessageCollectionViewCell`.
+public enum AccessoryPosition {
+    
+    /// Aligns the `AccessoryView`'s top edge to the cell's top edge.
+    case cellTop
+    
+    /// Aligns the `AccessoryView`'s top edge to the `messageTopLabel`'s top edge.
+    case messageLabelTop
+    
+    /// Aligns the `AccessoryView`'s top edge to the `MessageContainerView`'s top edge.
+    case messageTop
+    
+    /// Aligns the `AccessoryView` center to the `MessageContainerView` center.
+    case messageCenter
+    
+    /// Aligns the `AccessoryView`'s bottom edge to the `MessageContainerView`s bottom edge.
+    case messageBottom
+    
+    /// Aligns the `AccessoryView`'s bottom edge to the cell's bottom edge.
+    case cellBottom
+}

+ 1 - 1
Pods/MessageKit/Sources/Models/Avatar.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 4 - 4
Pods/MessageKit/Sources/Models/AvatarPosition.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -26,7 +26,7 @@ import Foundation
 
 
 /// Used to determine the `Horizontal` and `Vertical` position of
 /// Used to determine the `Horizontal` and `Vertical` position of
 // an `AvatarView` in a `MessageCollectionViewCell`.
 // an `AvatarView` in a `MessageCollectionViewCell`.
-public struct AvatarPosition {
+public struct AvatarPosition: Equatable {
     
     
     /// An enum representing the horizontal alignment of an `AvatarView`.
     /// An enum representing the horizontal alignment of an `AvatarView`.
     public enum Horizontal {
     public enum Horizontal {
@@ -89,9 +89,9 @@ public struct AvatarPosition {
 
 
 // MARK: - Equatable Conformance
 // MARK: - Equatable Conformance
 
 
-extension AvatarPosition: Equatable {
+public extension AvatarPosition {
 
 
-    public static func == (lhs: AvatarPosition, rhs: AvatarPosition) -> Bool {
+    static func == (lhs: AvatarPosition, rhs: AvatarPosition) -> Bool {
         return lhs.vertical == rhs.vertical && lhs.horizontal == rhs.horizontal
         return lhs.vertical == rhs.vertical && lhs.horizontal == rhs.horizontal
     }
     }
 
 

+ 33 - 7
Pods/MessageKit/Sources/Models/DetectorType.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -24,19 +24,19 @@
 
 
 import Foundation
 import Foundation
 
 
-public enum DetectorType {
+public enum DetectorType: Hashable {
 
 
     case address
     case address
     case date
     case date
     case phoneNumber
     case phoneNumber
     case url
     case url
     case transitInformation
     case transitInformation
+    case custom(NSRegularExpression)
 
 
-    // MARK: - Not supported yet
-
-    //case mention
-    //case hashtag
-    //case custom
+    // swiftlint:disable force_try
+    public static var hashtag = DetectorType.custom(try! NSRegularExpression(pattern: "#[a-zA-Z0-9]{4,}", options: []))
+    public static var mention = DetectorType.custom(try! NSRegularExpression(pattern: "@[a-zA-Z0-9]{4,}", options: []))
+    // swiftlint:enable force_try
 
 
     internal var textCheckingType: NSTextCheckingResult.CheckingType {
     internal var textCheckingType: NSTextCheckingResult.CheckingType {
         switch self {
         switch self {
@@ -45,6 +45,32 @@ public enum DetectorType {
         case .phoneNumber: return .phoneNumber
         case .phoneNumber: return .phoneNumber
         case .url: return .link
         case .url: return .link
         case .transitInformation: return .transitInformation
         case .transitInformation: return .transitInformation
+        case .custom: return .regularExpression
+        }
+    }
+
+    /// Simply check if the detector type is a .custom
+    public var isCustom: Bool {
+        switch self {
+        case .custom: return true
+        default: return false
+        }
+    }
+
+    ///The hashValue of the `DetectorType` so we can conform to `Hashable` and be sorted.
+    public func hash(into: inout Hasher) {
+        into.combine(toInt())
+    }
+
+    /// Return an 'Int' value for each `DetectorType` type so `DetectorType` can conform to `Hashable`
+    private func toInt() -> Int {
+        switch self {
+        case .address: return 0
+        case .date: return 1
+        case .phoneNumber: return 2
+        case .url: return 3
+        case .transitInformation: return 4
+        case .custom(let regex): return regex.hashValue
         }
         }
     }
     }
 
 

+ 5 - 5
Pods/MessageKit/Sources/Models/HorizontalEdgeInsets.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,7 @@
 import Foundation
 import Foundation
 
 
 /// A varient of `UIEdgeInsets` that only has horizontal inset properties
 /// A varient of `UIEdgeInsets` that only has horizontal inset properties
-public struct HorizontalEdgeInsets {
+public struct HorizontalEdgeInsets: Equatable {
 
 
     public var left: CGFloat
     public var left: CGFloat
     public var right: CGFloat
     public var right: CGFloat
@@ -40,14 +40,14 @@ public struct HorizontalEdgeInsets {
     }
     }
 }
 }
 
 
-extension HorizontalEdgeInsets: Equatable {
+public extension HorizontalEdgeInsets {
 
 
-    public static func == (lhs: HorizontalEdgeInsets, rhs: HorizontalEdgeInsets) -> Bool {
+    static func == (lhs: HorizontalEdgeInsets, rhs: HorizontalEdgeInsets) -> Bool {
         return lhs.left == rhs.left && lhs.right == rhs.right
         return lhs.left == rhs.left && lhs.right == rhs.right
     }
     }
 }
 }
 
 
-extension HorizontalEdgeInsets {
+internal extension HorizontalEdgeInsets {
 
 
     internal var horizontal: CGFloat {
     internal var horizontal: CGFloat {
         return left + right
         return left + right

+ 4 - 4
Pods/MessageKit/Sources/Models/LabelAlignment.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,7 @@
 
 
 import UIKit
 import UIKit
 
 
-public struct LabelAlignment {
+public struct LabelAlignment: Equatable {
 
 
     public var textAlignment: NSTextAlignment
     public var textAlignment: NSTextAlignment
     public var textInsets: UIEdgeInsets
     public var textInsets: UIEdgeInsets
@@ -38,9 +38,9 @@ public struct LabelAlignment {
 
 
 // MARK: - Equatable Conformance
 // MARK: - Equatable Conformance
 
 
-extension LabelAlignment: Equatable {
+public extension LabelAlignment {
 
 
-    public static func == (lhs: LabelAlignment, rhs: LabelAlignment) -> Bool {
+    static func == (lhs: LabelAlignment, rhs: LabelAlignment) -> Bool {
         return lhs.textAlignment == rhs.textAlignment && lhs.textInsets == rhs.textInsets
         return lhs.textAlignment == rhs.textAlignment && lhs.textInsets == rhs.textInsets
     }
     }
 
 

+ 1 - 1
Pods/MessageKit/Sources/Models/LocationMessageSnapshotOptions.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 7 - 3
Pods/MessageKit/Sources/Models/MessageKind.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -51,6 +51,12 @@ public enum MessageKind {
     /// An emoji message.
     /// An emoji message.
     case emoji(String)
     case emoji(String)
 
 
+    /// An audio message.
+    case audio(AudioItem)
+    
+    /// A contact message.
+    case contact(ContactItem)
+
     /// A custom message.
     /// A custom message.
     /// - Note: Using this case requires that you implement the following methods and handle this case:
     /// - Note: Using this case requires that you implement the following methods and handle this case:
     ///   - MessagesDataSource: customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
     ///   - MessagesDataSource: customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
@@ -59,8 +65,6 @@ public enum MessageKind {
 
 
     // MARK: - Not supported yet
     // MARK: - Not supported yet
 
 
-//    case audio(Data)
-//
 //    case system(String)
 //    case system(String)
 //    
 //    
 //    case placeholder
 //    case placeholder

+ 1 - 1
Pods/MessageKit/Sources/Models/MessageKitDateFormatter.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Pods/MessageKit/Sources/Models/MessageStyle.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Pods/MessageKit/Sources/Models/NSConstraintLayoutSet.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 14 - 14
Pods/MessageKit/Sources/Models/Sender.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -25,33 +25,33 @@
 import Foundation
 import Foundation
 
 
 /// An object that groups the metadata of a messages sender.
 /// An object that groups the metadata of a messages sender.
-public struct Sender {
+@available(*, deprecated: 3.0.0, message: "`Sender` has been replaced with the `SenderType` protocol in 3.0.0")
+public struct Sender: SenderType {
 
 
     /// MARK: - Properties
     /// MARK: - Properties
 
 
     /// The unique String identifier for the sender.
     /// The unique String identifier for the sender.
     ///
     ///
     /// Note: This value must be unique across all senders.
     /// Note: This value must be unique across all senders.
-    public let id: String
+    public let senderId: String
+
+    @available(*, deprecated: 3.0.0, message: "`id` has been renamed `senderId` as defined in the `SenderType` protocol")
+    public var id: String {
+        return senderId
+    }
 
 
     /// The display name of a sender.
     /// The display name of a sender.
     public let displayName: String
     public let displayName: String
 
 
     // MARK: - Intializers
     // MARK: - Intializers
 
 
-    public init(id: String, displayName: String) {
-        self.id = id
+    public init(senderId: String, displayName: String) {
+        self.senderId = senderId
         self.displayName = displayName
         self.displayName = displayName
     }
     }
-}
 
 
-// MARK: - Equatable Conformance
-
-extension Sender: Equatable {
-
-    /// Two senders are considered equal if they have the same id.
-    public static func == (left: Sender, right: Sender) -> Bool {
-        return left.id == right.id
+    @available(*, deprecated: 3.0.0, message: "`id` has been renamed `senderId` as defined in the `SenderType` protocol")
+    public init(id: String, displayName: String) {
+        self.init(senderId: id, displayName: displayName)
     }
     }
-
 }
 }

+ 39 - 0
Pods/MessageKit/Sources/Protocols/AudioItem.swift

@@ -0,0 +1,39 @@
+/*
+ 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 class AVFoundation.AVAudioPlayer
+
+/// A protocol used to represent the data for an audio message.
+public protocol AudioItem {
+
+    /// The url where the audio file is located.
+    var url: URL { get }
+
+    /// The audio file duration in seconds.
+    var duration: Float { get }
+
+    /// The size of the audio item.
+    var size: CGSize { get }
+
+}

+ 12 - 11
Pods/MessageInputBar/Sources/Protocols/InputPlugin.swift → Pods/MessageKit/Sources/Protocols/ContactItem.swift

@@ -22,19 +22,20 @@
  SOFTWARE.
  SOFTWARE.
  */
  */
 
 
-import UIKit
+import Foundation
 
 
-/// `InputPlugin` is a protocol that makes integrating plugins to the `MessageInputBar` easy.
-public protocol InputPlugin: AnyObject {
+/// A protocol used to represent the data for a contact message.
+public protocol ContactItem {
     
     
-    /// Should reload the state if the `InputPlugin`
-    func reloadData()
+    /// contact displayed name
+    var displayName: String { get }
     
     
-    /// Should remove any content that the `InputPlugin` is managing
-    func invalidate()
+    /// initials from contact first and last name
+    var initials: String { get }
     
     
-    /// Should handle the input of data types that an `InputPlugin` manages
-    ///
-    /// - Parameter object: The object to input
-    func handleInput(of object: AnyObject) -> Bool
+    /// contact phone numbers
+    var phoneNumbers: [String] { get }
+    
+    /// contact emails
+    var emails: [String] { get }
 }
 }

+ 1 - 1
Pods/MessageKit/Sources/Protocols/LocationItem.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Pods/MessageKit/Sources/Protocols/MediaItem.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal

+ 75 - 1
Pods/MessageKit/Sources/Protocols/MessageCellDelegate.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -27,6 +27,17 @@ import Foundation
 /// A protocol used by `MessageContentCell` subclasses to detect taps in the cell's subviews.
 /// A protocol used by `MessageContentCell` subclasses to detect taps in the cell's subviews.
 public protocol MessageCellDelegate: MessageLabelDelegate {
 public protocol MessageCellDelegate: MessageLabelDelegate {
 
 
+    /// Triggered when a tap occurs in the background of the cell.
+    ///
+    /// - Parameters:
+    ///   - cell: The cell where the tap occurred.
+    ///
+    /// - Note:
+    /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s
+    /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource`
+    /// method `messageForItem(at:indexPath:messagesCollectionView)`.
+    func didTapBackground(in cell: MessageCollectionViewCell)
+
     /// Triggered when a tap occurs in the `MessageContainerView`.
     /// Triggered when a tap occurs in the `MessageContainerView`.
     ///
     ///
     /// - Parameters:
     /// - Parameters:
@@ -58,6 +69,16 @@ public protocol MessageCellDelegate: MessageLabelDelegate {
     /// method `messageForItem(at:indexPath:messagesCollectionView)`.
     /// method `messageForItem(at:indexPath:messagesCollectionView)`.
     func didTapCellTopLabel(in cell: MessageCollectionViewCell)
     func didTapCellTopLabel(in cell: MessageCollectionViewCell)
     
     
+    /// Triggered when a tap occurs in the cellBottomLabel.
+    ///
+    /// - Parameters:
+    ///   - cell: The cell tap the touch occurred.
+    ///
+    /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s
+    /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource`
+    /// method `messageForItem(at:indexPath:messagesCollectionView)`.
+    func didTapCellBottomLabel(in cell: MessageCollectionViewCell)
+    
     /// Triggered when a tap occurs in the messageTopLabel.
     /// Triggered when a tap occurs in the messageTopLabel.
     ///
     ///
     /// - Parameters:
     /// - Parameters:
@@ -88,19 +109,72 @@ public protocol MessageCellDelegate: MessageLabelDelegate {
     /// method `messageForItem(at:indexPath:messagesCollectionView)`.
     /// method `messageForItem(at:indexPath:messagesCollectionView)`.
     func didTapAccessoryView(in cell: MessageCollectionViewCell)
     func didTapAccessoryView(in cell: MessageCollectionViewCell)
 
 
+    /// Triggered when a tap occurs on the play button from audio cell.
+    ///
+    /// - Parameters:
+    ///   - cell: The audio cell where the touch occurred.
+    ///
+    /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s
+    /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource`
+    /// method `messageForItem(at:indexPath:messagesCollectionView)`.
+    func didTapPlayButton(in cell: AudioMessageCell)
+
+    /// Triggered when audio player start playing audio.
+    ///
+    /// - Parameters:
+    ///   - cell: The cell where the audio sound is playing.
+    ///
+    /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s
+    /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource`
+    /// method `messageForItem(at:indexPath:messagesCollectionView)`.
+    func didStartAudio(in cell: AudioMessageCell)
+
+    /// Triggered when audio player pause audio.
+    ///
+    /// - Parameters:
+    ///   - cell: The cell where the audio sound is paused.
+    ///
+    /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s
+    /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource`
+    /// method `messageForItem(at:indexPath:messagesCollectionView)`.
+    func didPauseAudio(in cell: AudioMessageCell)
+
+    /// Triggered when audio player stoped audio.
+    ///
+    /// - Parameters:
+    ///   - cell: The cell where the audio sound is stoped.
+    ///
+    /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s
+    /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource`
+    /// method `messageForItem(at:indexPath:messagesCollectionView)`.
+    func didStopAudio(in cell: AudioMessageCell)
+
 }
 }
 
 
 public extension MessageCellDelegate {
 public extension MessageCellDelegate {
 
 
+    func didTapBackground(in cell: MessageCollectionViewCell) {}
+
     func didTapMessage(in cell: MessageCollectionViewCell) {}
     func didTapMessage(in cell: MessageCollectionViewCell) {}
 
 
     func didTapAvatar(in cell: MessageCollectionViewCell) {}
     func didTapAvatar(in cell: MessageCollectionViewCell) {}
 
 
     func didTapCellTopLabel(in cell: MessageCollectionViewCell) {}
     func didTapCellTopLabel(in cell: MessageCollectionViewCell) {}
+    
+    func didTapCellBottomLabel(in cell: MessageCollectionViewCell) {}
 
 
     func didTapMessageTopLabel(in cell: MessageCollectionViewCell) {}
     func didTapMessageTopLabel(in cell: MessageCollectionViewCell) {}
 
 
+    func didTapPlayButton(in cell: AudioMessageCell) {}
+
+    func didStartAudio(in cell: AudioMessageCell) {}
+
+    func didPauseAudio(in cell: AudioMessageCell) {}
+
+    func didStopAudio(in cell: AudioMessageCell) {}
+
     func didTapMessageBottomLabel(in cell: MessageCollectionViewCell) {}
     func didTapMessageBottomLabel(in cell: MessageCollectionViewCell) {}
     
     
     func didTapAccessoryView(in cell: MessageCollectionViewCell) {}
     func didTapAccessoryView(in cell: MessageCollectionViewCell) {}
+
 }
 }

+ 26 - 1
Pods/MessageKit/Sources/Protocols/MessageLabelDelegate.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -56,6 +56,25 @@ public protocol MessageLabelDelegate: AnyObject {
     /// - Parameters:
     /// - Parameters:
     ///   - transitInformation: The selected transit information.
     ///   - transitInformation: The selected transit information.
     func didSelectTransitInformation(_ transitInformation: [String: String])
     func didSelectTransitInformation(_ transitInformation: [String: String])
+    
+    /// Triggered when a tap occurs on a mention
+    ///
+    /// - Parameters:
+    ///   - mention: The selected mention
+    func didSelectMention(_ mention: String)
+    
+    /// Triggered when a tap occurs on a hashtag
+    ///
+    /// - Parameters:
+    ///   - mention: The selected hashtag
+    func didSelectHashtag(_ hashtag: String)
+
+    /// Triggered when a tap occurs on a custom regular expression
+    ///
+    /// - Parameters:
+    ///   - pattern: the pattern of the regular expression
+    ///   - match: part that match with the regular expression
+    func didSelectCustom(_ pattern: String, match: String?)
 
 
 }
 }
 
 
@@ -71,4 +90,10 @@ public extension MessageLabelDelegate {
     
     
     func didSelectTransitInformation(_ transitInformation: [String: String]) {}
     func didSelectTransitInformation(_ transitInformation: [String: String]) {}
 
 
+    func didSelectMention(_ mention: String) {}
+
+    func didSelectHashtag(_ hashtag: String) {}
+
+    func didSelectCustom(_ pattern: String, match: String?) {}
+
 }
 }

+ 2 - 2
Pods/MessageKit/Sources/Protocols/MessageType.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -29,7 +29,7 @@ import Foundation
 public protocol MessageType {
 public protocol MessageType {
 
 
     /// The sender of the message.
     /// The sender of the message.
-    var sender: Sender { get }
+    var sender: SenderType { get }
 
 
     /// The unique identifier for the message.
     /// The unique identifier for the message.
     var messageId: String { get }
     var messageId: String { get }

+ 34 - 8
Pods/MessageKit/Sources/Protocols/MessagesDataSource.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -28,17 +28,17 @@ import UIKit
 /// the data required by a `MessagesCollectionView`.
 /// the data required by a `MessagesCollectionView`.
 public protocol MessagesDataSource: AnyObject {
 public protocol MessagesDataSource: AnyObject {
 
 
-    /// The `Sender` of new messages in the `MessagesCollectionView`.
-    func currentSender() -> Sender
+    /// The `SenderType` of new messages in the `MessagesCollectionView`.
+    func currentSender() -> SenderType
 
 
-    /// A helper method to determine if a given message is from the current `Sender`.
+    /// A helper method to determine if a given message is from the current `SenderType`.
     ///
     ///
     /// - Parameters:
     /// - Parameters:
-    ///   - message: The message to check if it was sent by the current `Sender`.
+    ///   - message: The message to check if it was sent by the current `SenderType`.
     ///
     ///
     /// - Note:
     /// - Note:
     ///   The default implementation of this method checks for equality between
     ///   The default implementation of this method checks for equality between
-    ///   the message's `Sender` and the current `Sender`.
+    ///   the message's `SenderType` and the current `SenderType`.
     func isFromCurrentSender(message: MessageType) -> Bool
     func isFromCurrentSender(message: MessageType) -> Bool
 
 
     /// The message to be used for a `MessageCollectionViewCell` at the given `IndexPath`.
     /// The message to be used for a `MessageCollectionViewCell` at the given `IndexPath`.
@@ -73,6 +73,16 @@ public protocol MessagesDataSource: AnyObject {
     /// The default value returned by this method is `nil`.
     /// The default value returned by this method is `nil`.
     func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString?
     func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString?
     
     
+    /// The attributed text to be used for cell's bottom label.
+    ///
+    /// - 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.
+    ///
+    /// The default value returned by this method is `nil`.
+    func cellBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString?
+    
     /// The attributed text to be used for message bubble's top label.
     /// The attributed text to be used for message bubble's top label.
     ///
     ///
     /// - Parameters:
     /// - Parameters:
@@ -101,14 +111,22 @@ public protocol MessagesDataSource: AnyObject {
     ///   - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
     ///   - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
     ///
     ///
     /// - Note:
     /// - Note:
-    ///   This method will call fatalError() on default. You must override this method if you are using MessageType.custom messages.
+    ///   This method will call fatalError() on default. You must override this method if you are using MessageKind.custom messages.
     func customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
     func customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
+
+    /// Typing indicator cell used when the indicator is set to be shown
+    ///
+    /// - Parameters:
+    ///   - indexPath: The index path to dequeue the cell at
+    ///   - messagesCollectionView: The `MessagesCollectionView` the cell is to be rendered in
+    /// - Returns: A `UICollectionViewCell` that indicates a user is typing
+    func typingIndicator(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
 }
 }
 
 
 public extension MessagesDataSource {
 public extension MessagesDataSource {
 
 
     func isFromCurrentSender(message: MessageType) -> Bool {
     func isFromCurrentSender(message: MessageType) -> Bool {
-        return message.sender == currentSender()
+        return message.sender.senderId == currentSender().senderId
     }
     }
 
 
     func numberOfItems(inSection section: Int, in messagesCollectionView: MessagesCollectionView) -> Int {
     func numberOfItems(inSection section: Int, in messagesCollectionView: MessagesCollectionView) -> Int {
@@ -119,6 +137,10 @@ public extension MessagesDataSource {
         return nil
         return nil
     }
     }
     
     
+    func cellBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
+        return nil
+    }
+    
     func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
     func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
         return nil
         return nil
     }
     }
@@ -130,4 +152,8 @@ public extension MessagesDataSource {
     func customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell {
     func customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell {
         fatalError(MessageKitError.customDataUnresolvedCell)
         fatalError(MessageKitError.customDataUnresolvedCell)
     }
     }
+
+    func typingIndicator(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell {
+        return messagesCollectionView.dequeueReusableCell(TypingIndicatorCell.self, for: indexPath)
+    }
 }
 }

+ 71 - 3
Pods/MessageKit/Sources/Protocols/MessagesDisplayDelegate.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
  
  
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
  
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -50,7 +50,7 @@ public protocol MessagesDisplayDelegate: AnyObject {
     ///
     ///
     /// - Note:
     /// - Note:
     ///   The default value is `UIColor.clear` for emoji messages.
     ///   The default value is `UIColor.clear` for emoji messages.
-    ///   For all other `MessageKind` cases, the color depends on the `Sender`.
+    ///   For all other `MessageKind` cases, the color depends on the `SenderType`.
     ///
     ///
     ///   Current sender: Green
     ///   Current sender: Green
     ///
     ///
@@ -106,7 +106,7 @@ public protocol MessagesDisplayDelegate: AnyObject {
     ///   - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
     ///   - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
     ///
     ///
     /// - Note:
     /// - Note:
-    ///   The default value returned by this method is determined by the messages `Sender`.
+    ///   The default value returned by this method is determined by the messages `SenderType`.
     ///
     ///
     ///   Current sender: UIColor.white
     ///   Current sender: UIColor.white
     ///
     ///
@@ -173,6 +173,46 @@ public protocol MessagesDisplayDelegate: AnyObject {
     ///   - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
     ///   - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
     func configureMediaMessageImageView(_ imageView: UIImageView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView)
     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. progresssView 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 mintues 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
+
 }
 }
 
 
 public extension MessagesDisplayDelegate {
 public extension MessagesDisplayDelegate {
@@ -243,4 +283,32 @@ public extension MessagesDisplayDelegate {
 
 
     func configureMediaMessageImageView(_ imageView: UIImageView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) {
     func configureMediaMessageImageView(_ imageView: UIImageView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) {
     }
     }
+
+    // MARK: - Audio Message Defaults
+    
+    func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) {
+        
+    }
+
+    func audioTintColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
+        return UIColor.sendButtonBlue
+    }
+
+    func audioProgressTextFormat(_ duration: Float, for audioCell: AudioMessageCell, in messageCollectionView: MessagesCollectionView) -> String {
+        var retunValue = "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 {
+            retunValue = String(format: "0:%.02d", Int(duration.rounded(.up)))
+        } else if duration < 3600 {
+            retunValue = String(format: "%.02d:%.02d", Int(duration/60), Int(duration) % 60)
+        } else {
+            let hours = Int(duration/3600)
+            let remainingMinutsInSeconds = Int(duration) - hours*3600
+            retunValue = String(format: "%.02d:%.02d:%.02d", hours, Int(remainingMinutsInSeconds/60), Int(remainingMinutsInSeconds) % 60)
+        }
+        return retunValue
+    }
+
 }
 }

+ 43 - 1
Pods/MessageKit/Sources/Protocols/MessagesLayoutDelegate.swift

@@ -1,7 +1,7 @@
 /*
 /*
  MIT License
  MIT License
 
 
- Copyright (c) 2017-2018 MessageKit
+ Copyright (c) 2017-2019 MessageKit
 
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  of this software and associated documentation files (the "Software"), to deal
@@ -48,6 +48,25 @@ public protocol MessagesLayoutDelegate: AnyObject {
     ///   The default value returned by this method is a size of `GGSize.zero`.
     ///   The default value returned by this method is a size of `GGSize.zero`.
     func footerViewSize(for section: Int, in messagesCollectionView: MessagesCollectionView) -> CGSize
     func footerViewSize(for section: Int, in messagesCollectionView: MessagesCollectionView) -> CGSize
 
 
+    /// Specifies the size to use for a typing indicator view.
+    ///
+    /// - Parameters:
+    ///   - messagesCollectionView: The `MessagesCollectionView` in which this view will be displayed.
+    ///
+    /// - Note:
+    ///   The default value returned by this method is the width of the `messagesCollectionView` and
+    ///   a height of 52.
+    func typingIndicatorViewSize(in messagesCollectionView: MessagesCollectionView) -> CGSize
+
+    /// Specifies the top inset to use for a typing indicator view.
+    ///
+    /// - Parameters:
+    ///   - messagesCollectionView: The `MessagesCollectionView` in which this view will be displayed.
+    ///
+    /// - Note:
+    ///   The default value returned by this method is a top inset of 15.
+    func typingIndicatorViewTopInset(in messagesCollectionView: MessagesCollectionView) -> CGFloat
+
     /// Specifies the height for the `MessageContentCell`'s top label.
     /// Specifies the height for the `MessageContentCell`'s top label.
     ///
     ///
     /// - Parameters:
     /// - Parameters:
@@ -59,6 +78,17 @@ public protocol MessagesLayoutDelegate: AnyObject {
     ///   The default value returned by this method is zero.
     ///   The default value returned by this method is zero.
     func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat
     func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat
     
     
+    /// Specifies the height for the `MessageContentCell`'s bottom label.
+    ///
+    /// - Parameters:
+    ///   - message: The `MessageType` that will be displayed for 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 zero.
+    func cellBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat
+    
     /// Specifies the height for the message bubble's top label.
     /// Specifies the height for the message bubble's top label.
     ///
     ///
     /// - Parameters:
     /// - Parameters:
@@ -103,10 +133,22 @@ public extension MessagesLayoutDelegate {
         return .zero
         return .zero
     }
     }
 
 
+    func typingIndicatorViewSize(in messagesCollectionView: MessagesCollectionView) -> CGSize {
+        return CGSize(width: messagesCollectionView.bounds.width, height: 48)
+    }
+
+    func typingIndicatorViewTopInset(in messagesCollectionView: MessagesCollectionView) -> CGFloat {
+        return 15
+    }
+
     func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
     func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
         return 0
         return 0
     }
     }
     
     
+    func cellBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
+        return 0
+    }
+    
     func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
     func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
         return 0
         return 0
     }
     }

+ 38 - 0
Pods/MessageKit/Sources/Protocols/SenderType.swift

@@ -0,0 +1,38 @@
+/*
+ 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
+
+/// A standard protocol representing a sender.
+/// Use this protocol to adhere a object as the sender of a MessageType
+public protocol SenderType {
+
+    /// The unique String identifier for the sender.
+    ///
+    /// Note: This value must be unique across all senders.
+    var senderId: String { get }
+
+    /// The display name of a sender.
+    var displayName: String { get }
+}

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác