diff --git a/Demo/ViewController.swift b/Demo/ViewController.swift index ca00b19..916c19c 100644 --- a/Demo/ViewController.swift +++ b/Demo/ViewController.swift @@ -42,7 +42,13 @@ extension ViewController: MessagesViewDelegate { } func didTapRightButton() { - let text = messagesView.inputText + + let text = messagesView.inputText.trimmingCharacters(in: .whitespaces) + + guard !text.isEmpty else { + return + } + TestData.exampleMessageText.append(text) messagesView.refresh(scrollToLastMessage: true, animateLastMessage: true) } diff --git a/MessagesView.xcodeproj/project.pbxproj b/MessagesView.xcodeproj/project.pbxproj index 6f6bc55..61cdc04 100644 --- a/MessagesView.xcodeproj/project.pbxproj +++ b/MessagesView.xcodeproj/project.pbxproj @@ -290,7 +290,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0820; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "pgs-dkanak"; TargetAttributes = { 055DA17B1E9296600091279C = { @@ -471,7 +471,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -479,7 +481,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -524,7 +530,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -532,7 +540,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/MessagesView.xcodeproj/xcshareddata/xcschemes/MessagesView.xcscheme b/MessagesView.xcodeproj/xcshareddata/xcschemes/MessagesView.xcscheme index 6611f02..a4e051b 100644 --- a/MessagesView.xcodeproj/xcshareddata/xcschemes/MessagesView.xcscheme +++ b/MessagesView.xcodeproj/xcshareddata/xcschemes/MessagesView.xcscheme @@ -1,6 +1,6 @@ UIBezierPath { + private static func createTailPathIn(origin: CGPoint, size: CGSize) -> UIBezierPath { let width = size.width let height = size.height diff --git a/MessagesView/MessageEditorTextView.swift b/MessagesView/MessageEditorTextView.swift index a4a9b54..7cc24fe 100644 --- a/MessagesView/MessageEditorTextView.swift +++ b/MessagesView/MessageEditorTextView.swift @@ -8,29 +8,20 @@ import UIKit -class MessageEditorTextView: UITextView { +class MessageEditorTextView: UITextField { - /* - // Only override draw() if you perform custom drawing. - // An empty implementation adversely affects performance during animation. - override func draw(_ rect: CGRect) { - // Drawing code - } - */ - - override func awakeFromNib() { - super.awakeFromNib() - setupTextView() - } - - func setupTextView() { - self.backgroundColor = UIColor.yellow - } - func applySettings(settings: MessagesViewSettings) { - self.textColor = settings.textInputFieldTextColor - self.backgroundColor = settings.textInputFieldBackgroundColor - self.layer.cornerRadius = settings.textInputFieldCornerRadius + + textColor = settings.textInputFieldTextColor + backgroundColor = settings.textInputFieldBackgroundColor + tintColor = settings.textInputTintColor + layer.cornerRadius = settings.textInputFieldCornerRadius + placeholder = settings.textInputFieldTextPlaceholderText + + keyboardType = settings.keyboardType + keyboardAppearance = settings.keyboardAppearance + returnKeyType = settings.returnKeyType + enablesReturnKeyAutomatically = settings.enablesReturnKeyAutomatically } } diff --git a/MessagesView/MessagesInputToolbar.swift b/MessagesView/MessagesInputToolbar.swift index 1f5bfe3..c321413 100644 --- a/MessagesView/MessagesInputToolbar.swift +++ b/MessagesView/MessagesInputToolbar.swift @@ -8,19 +8,34 @@ import UIKit -class MessagesInputToolbar: UIToolbar { +class MessagesInputToolbar: UIView { let toolbarContentView = MessagesToolbarContentView.fromNib() - /* - // Only override draw() if you perform custom drawing. - // An empty implementation adversely affects performance during animation. - override func draw(_ rect: CGRect) { - // Drawing code - } - */ - var messageText : String { - return toolbarContentView.messageText + var leftButtonAction: ()->() { + get { + return toolbarContentView.leftButtonAction + } + set { + toolbarContentView.leftButtonAction = newValue + } + } + var rightButtonAction: ()->() { + get { + return toolbarContentView.rightButtonAction + } + set { + toolbarContentView.rightButtonAction = newValue + } + } + + var inputText : String { + get { + return toolbarContentView.inputText + } + set { + toolbarContentView.inputText = newValue + } } var settings = MessagesViewSettings() { diff --git a/MessagesView/MessagesToolbarContentView.swift b/MessagesView/MessagesToolbarContentView.swift index 78c1607..2f39493 100644 --- a/MessagesView/MessagesToolbarContentView.swift +++ b/MessagesView/MessagesToolbarContentView.swift @@ -34,6 +34,9 @@ class MessagesToolbarContentView: UIView { private var leftButtonEnabled: Bool = true private var rightButtonEnabled: Bool = true + var leftButtonAction: () -> () = {} + var rightButtonAction: () -> () = {} + private var leftTintColor: UIColor { return leftButtonEnabled ? settings.leftButtonTextColor : settings.leftButtonDisabledColor } @@ -57,7 +60,7 @@ class MessagesToolbarContentView: UIView { @IBAction func didPressLeftButton(_ sender: AnyObject) { if leftButtonEnabled { - settings.leftButtonAction() + leftButtonAction() } if settings.leftButtonHidesKeyboard { @@ -68,7 +71,7 @@ class MessagesToolbarContentView: UIView { @IBAction func didPressRightButton(_ sender: AnyObject) { if rightButtonEnabled { - settings.rightButtonAction() + rightButtonAction() } if settings.rightButtonHidesKeyboard { @@ -91,8 +94,13 @@ class MessagesToolbarContentView: UIView { apply(settings: settings) } } - var messageText : String { - return messageEditorTextView.text + var inputText : String { + get { + return messageEditorTextView.text ?? "" + } + set { + messageEditorTextView.text = newValue + } } func righButton(show: Bool, animated: Bool) { @@ -188,6 +196,11 @@ extension MessagesToolbarContentView : UITextFieldDelegate { } func textFieldShouldReturn(_ textField: UITextField) -> Bool { + + if settings.shouldDoRightActionWithReturnKey { + didPressRightButton(textField) + } + return true } diff --git a/MessagesView/MessagesToolbarContentView.xib b/MessagesView/MessagesToolbarContentView.xib index 9b061c1..9138c15 100644 --- a/MessagesView/MessagesToolbarContentView.xib +++ b/MessagesView/MessagesToolbarContentView.xib @@ -1,10 +1,11 @@ - - + + - + + @@ -14,7 +15,7 @@ - + - + - + diff --git a/MessagesView/MessagesView.swift b/MessagesView/MessagesView.swift index 9bc0822..28001a3 100644 --- a/MessagesView/MessagesView.swift +++ b/MessagesView/MessagesView.swift @@ -44,6 +44,7 @@ public class MessagesView: UIView { @IBOutlet weak var messageInputToolbarBottomConstraint: NSLayoutConstraint! private var toolBarBottomConstraintWithoutKeyboard: CGFloat = 0 + private var toolBarFrameWithoutKeyboard: CGRect = .zero //MARK:- Public properties @@ -56,13 +57,14 @@ public class MessagesView: UIView { @IBInspectable public var textInputFieldTextColor: UIColor = .black @IBInspectable public var textInputFieldBackgroundColor: UIColor = .clear + @IBInspectable public var textInputTintColor: UIColor = .pumpkin @IBInspectable public var textInputFieldTextPlaceholderText: String = "Write your message here" @IBInspectable public var textInputFieldCornerRadius: CGFloat = 0.0 - @IBInspectable public var textInputFieldFont: UIFont = UIFont.systemFont(ofSize: 10) + @IBInspectable public var textInputFieldFont: UIFont = .systemFont(ofSize: 10) @IBInspectable public var textInputFieldTopSeparatorLineHeight: CGFloat = 1.0 @IBInspectable public var textInputFieldTopSeparatorLineColor: UIColor = .pumpkin - @IBInspectable public var textInputFieldTopSeparatorLineAlpha: CGFloat = 0.3 + @IBInspectable public var textInputFieldTopSeparatorLineAlpha: CGFloat = 1.0 @IBInspectable public var inputToolbarBackgroundColor: UIColor = UIColor.white @@ -96,6 +98,8 @@ public class MessagesView: UIView { var bubbleImageLeft: BubbleImage = BubbleImage(cornerRadius: 8) var bubbleImageRight: BubbleImage = BubbleImage(cornerRadius: 8).flipped + private var isKeyboardShown = false + public func setBubbleImagesWith(left: BubbleImage, right: BubbleImage? = nil) { bubbleImageLeft = left @@ -103,13 +107,18 @@ public class MessagesView: UIView { } public var inputText: String { - return messagesInputToolbar.messageText + get { + return messagesInputToolbar.inputText + } + set { + messagesInputToolbar.inputText = newValue + } } var view: UIView! - public var settings = MessagesViewSettings.testChatSettings() { + public var settings = MessagesViewSettings() { didSet { - messagesInputToolbar.settings = settings + apply(settings: settings) } } @@ -190,60 +199,73 @@ public class MessagesView: UIView { pinSubviewToEdges(subview: view) registerCellNib() - settings.setLeftButtonAction { - self.delegate?.didTapLeftButton() + messagesInputToolbar.leftButtonAction = { [weak self] _ in + self?.delegate?.didTapLeftButton() } - settings.setRightButtonAction { - self.delegate?.didTapRightButton() + messagesInputToolbar.rightButtonAction = { [weak self] _ in + self?.delegate?.didTapRightButton() } messagesInputToolbar.settings = settings NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: .UIKeyboardWillChangeFrame, object: nil) } @objc private func keyboardWillShow(notification: Notification) { - guard settings.shouldAdjustToKeyboard, - let userInfo = notification.userInfo, - let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, - let animationDuration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue else { + guard !isKeyboardShown else { return } - toolBarBottomConstraintWithoutKeyboard = messageInputToolbarBottomConstraint.constant + isKeyboardShown = true - let toolbarFrameInWindow = convert(messagesInputToolbar.frame, to: nil) + toolBarBottomConstraintWithoutKeyboard = messageInputToolbarBottomConstraint.constant + toolBarFrameWithoutKeyboard = convert(messagesInputToolbar.frame, to: nil) - let keyboardOverlap = toolbarFrameInWindow.origin.y - keyboardFrame.origin.y + respondToKeyboardFrameChange(notification: notification) + } + + @objc private func keyboardWillChangeFrame(notification: Notification) { - guard keyboardOverlap > 0 else { + guard isKeyboardShown else { return } - let verticalAdjusttment = keyboardOverlap + toolbarFrameInWindow.size.height - - messageInputToolbarBottomConstraint.constant = toolBarBottomConstraintWithoutKeyboard + verticalAdjusttment - - UIView.animate(withDuration: animationDuration) { - let contentOffset = self.messagesCollectionView.contentOffset - - self.messagesCollectionView.contentOffset = CGPoint(x: contentOffset.x, y: contentOffset.y + verticalAdjusttment) - self.layoutIfNeeded() - } + respondToKeyboardFrameChange(notification: notification) } @objc private func keyboardWillHide(notification: Notification) { + isKeyboardShown = false + } + + private func respondToKeyboardFrameChange(notification: Notification) { + guard settings.shouldAdjustToKeyboard, - let animationDuration = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue else { + let userInfo = notification.userInfo, + let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, + let animationDuration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue else { + return + } + + let keyboardOverlap = toolBarFrameWithoutKeyboard.origin.y - keyboardFrame.origin.y + + let verticalAdjustment = keyboardOverlap > 0 ? keyboardOverlap + toolBarFrameWithoutKeyboard.size.height : 0 + + let newBottomConstraint = toolBarBottomConstraintWithoutKeyboard + verticalAdjustment + + guard newBottomConstraint != messageInputToolbarBottomConstraint.constant else { return } - messageInputToolbarBottomConstraint.constant = toolBarBottomConstraintWithoutKeyboard + messageInputToolbarBottomConstraint.constant = newBottomConstraint UIView.animate(withDuration: animationDuration) { + let contentOffset = self.messagesCollectionView.contentOffset + + self.messagesCollectionView.contentOffset = CGPoint(x: contentOffset.x, y: contentOffset.y + verticalAdjustment) self.layoutIfNeeded() } } @@ -313,42 +335,46 @@ public class MessagesView: UIView { } private func readSettingsFromInpectables(settings: inout MessagesViewSettings) { - settings.leftMessageCellTextColor = self.leftMessageCellTextColor - settings.leftMessageCellBackgroundColor = self.leftMessageCellBackgroundColor - settings.rightMessageCellTextColor = self.rightMessageCellTextColor - settings.rightMessageCellBackgroundColor = self.rightMessageCellBackgroundColor - - settings.collectionViewBackgroundColor = self.collectionViewBackgroundColor - - settings.textInputFieldTextColor = self.textInputFieldTextColor - settings.textInputFieldBackgroundColor = self.textInputFieldBackgroundColor - - settings.textInputFieldTopSeparatorLineHeight = self.textInputFieldTopSeparatorLineHeight - settings.textInputFieldTopSeparatorLineAlpha = self.textInputFieldTopSeparatorLineAlpha - settings.textInputFieldTopSeparatorLineColor = self.textInputFieldTopSeparatorLineColor - settings.textInputFieldTextPlaceholderText = self.textInputFieldTextPlaceholderText - - settings.buttonSlideAnimationDuration = self.buttonSlideAnimationDuration - settings.inputToolbarBackgroundColor = self.inputToolbarBackgroundColor - settings.textInputFieldCornerRadius = self.textInputFieldCornerRadius - - settings.leftButtonText = self.leftButtonText - settings.leftButtonShow = self.leftButtonShow - settings.leftButtonShowAnimated = self.leftButtonShowAnimated - settings.leftButtonTextColor = self.leftButtonTextColor - settings.leftButtonDisabledColor = self.leftButtonDisabledColor - settings.leftButtonBackgroundColor = self.leftButtonBackgroundColor - settings.leftButtonBackgroundImage = self.leftButtonBackgroundImage - settings.leftButtonCornerRadius = self.leftButtonCornerRadius - - settings.rightButtonText = self.rightButtonText - settings.rightButtonShow = self.rightButtonShow - settings.rightButtonShowAnimated = self.rightButtonShowAnimated - settings.rightButtonTextColor = self.rightButtonTextColor - settings.rightButtonDisabledColor = self.rightButtonDisabledColor - settings.rightButtonBackgroundColor = self.rightButtonBackgroundColor - settings.rightButtonBackgroundImage = self.rightButtonBackgroundImage - settings.rightButtonCornerRadius = self.rightButtonCornerRadius + + settings.leftMessageCellTextColor = leftMessageCellTextColor + settings.leftMessageCellBackgroundColor = leftMessageCellBackgroundColor + settings.rightMessageCellTextColor = rightMessageCellTextColor + settings.rightMessageCellBackgroundColor = rightMessageCellBackgroundColor + + settings.collectionViewBackgroundColor = collectionViewBackgroundColor + + settings.textInputFieldTextColor = textInputFieldTextColor + settings.textInputFieldBackgroundColor = textInputFieldBackgroundColor + settings.textInputTintColor = textInputTintColor + settings.textInputFieldTextPlaceholderText = textInputFieldTextPlaceholderText + settings.textInputFieldCornerRadius = textInputFieldCornerRadius + settings.textInputFieldFont = textInputFieldFont + + settings.textInputFieldTopSeparatorLineHeight = textInputFieldTopSeparatorLineHeight + settings.textInputFieldTopSeparatorLineColor = textInputFieldTopSeparatorLineColor + settings.textInputFieldTopSeparatorLineAlpha = textInputFieldTopSeparatorLineAlpha + + settings.inputToolbarBackgroundColor = inputToolbarBackgroundColor + + settings.leftButtonText = leftButtonText + settings.leftButtonShow = leftButtonShow + settings.leftButtonShowAnimated = leftButtonShowAnimated + settings.leftButtonTextColor = leftButtonTextColor + settings.leftButtonDisabledColor = leftButtonDisabledColor + settings.leftButtonBackgroundColor = leftButtonBackgroundColor + settings.leftButtonBackgroundImage = leftButtonBackgroundImage + settings.leftButtonCornerRadius = leftButtonCornerRadius + + settings.rightButtonText = rightButtonText + settings.rightButtonShow = rightButtonShow + settings.rightButtonShowAnimated = rightButtonShowAnimated + settings.rightButtonTextColor = rightButtonTextColor + settings.rightButtonDisabledColor = rightButtonDisabledColor + settings.rightButtonBackgroundColor = rightButtonBackgroundColor + settings.rightButtonBackgroundImage = rightButtonBackgroundImage + settings.rightButtonCornerRadius = rightButtonCornerRadius + + settings.buttonSlideAnimationDuration = buttonSlideAnimationDuration } private func apply(settings: MessagesViewSettings) { diff --git a/MessagesView/MessagesViewSettings.swift b/MessagesView/MessagesViewSettings.swift index c41bd2b..bc08252 100644 --- a/MessagesView/MessagesViewSettings.swift +++ b/MessagesView/MessagesViewSettings.swift @@ -10,121 +10,71 @@ import Foundation import UIKit public class MessagesViewSettings { - var leftButtonActionName = "" - var rightButtonActionName = "" - - var leftButtonHidesKeyboard = false - var rightButtonHidesKeyboard = false - var shouldAdjustToKeyboard = true - - var textInputScrollsToRecentMessage = true - - var messageCollectionViewHeaderHeight = 100.0 - var messageCollectionViewFooterHeight = 100.0 - var messageCollectionViewHeaderBackgroundColor = UIColor.green - var messageCollectionViewFooterBackgroundColor = UIColor.blue - - var groupSeparationSpacing: CGFloat = 12 - var groupInternalSpacing: CGFloat = 1 - var minimalHorizontalSpacing: CGFloat = 80 - - //MARK: IBInspectables from MessageView - var leftMessageCellTextColor: UIColor = UIColor.pastelGrey - var leftMessageCellBackgroundColor: UIColor = UIColor.pumpkin - var rightMessageCellTextColor: UIColor = UIColor.pastelGrey - var rightMessageCellBackgroundColor: UIColor = UIColor.pumpkin - - var textInputFieldTopSeparatorLineHeight: CGFloat = 1.0 - var textInputFieldTopSeparatorLineAlpha: CGFloat = 1.0 - var textInputFieldTopSeparatorLineColor: UIColor = UIColor.pumpkin - - var collectionViewBackgroundColor: UIColor = UIColor.yellow - var textInputFieldTextColor: UIColor = UIColor.yellow - var textInputFieldTextPlaceholderText: String = "Write your message here" - var textInputFieldBackgroundColor: UIColor = UIColor.yellow - var textInputFieldFont: UIFont = UIFont.systemFont(ofSize: 10) - - var buttonSlideAnimationDuration = TimeInterval(0.5) - var inputToolbarBackgroundColor = UIColor.white - var textInputFieldCornerRadius = CGFloat(0.0) - - var leftButtonText: String = "Left" - var leftButtonShow: Bool = true - var leftButtonShowAnimated: Bool = true - var leftButtonTextColor: UIColor = UIColor.black - var leftButtonDisabledColor: UIColor = .darkGray - var leftButtonBackgroundColor: UIColor = UIColor.gray - var leftButtonBackgroundImage: UIImage? - var leftButtonCornerRadius: CGFloat = 0.0 - - var rightButtonText: String = "Right" - var rightButtonShow: Bool = true - var rightButtonShowAnimated: Bool = true - var rightButtonTextColor: UIColor = UIColor.black - var rightButtonDisabledColor: UIColor = .darkGray - var rightButtonBackgroundColor: UIColor = UIColor.gray - var rightButtonBackgroundImage: UIImage? - var rightButtonCornerRadius: CGFloat = 0.0 - - // MARK: Presets - var action : [String : (Void)->() ] = [:] - var rightButtonAction : (Void)->() { return action[rightButtonActionName] ?? {} } - var leftButtonAction : (Void)->() { return action[leftButtonActionName] ?? {}} - - struct Action { - static let send = "SEND" - static let sendFromMyself = "SEND_TO_MYSELF" - static let addPicture = "ADD_PICTURE" - static let printLeftDebug = "PRINT_LEFT_DEBUG" - static let printRightDebug = "PRINT_RIGHT_DEBUG" - } - - init() { - action[Action.send] = { print("sending message") } - action = [ - Action.send : { print("sending message") }, - Action.sendFromMyself : { print("sending message from myself") }, - Action.addPicture : { print("adding picture") }, - Action.printLeftDebug: { print("left action") }, - Action.printRightDebug: { print("right action") } - ] - } - - public func setLeftButtonAction(newAction: @escaping (Void)->()) { - action[leftButtonActionName] = newAction - } - - public func setRightButtonAction(newAction: @escaping (Void)->()) { - action[rightButtonActionName] = newAction - } - - public static func defaultMessageViewSettings() -> MessagesViewSettings { - let settings = MessagesViewSettings() - settings.leftButtonActionName = Action.printLeftDebug - settings.rightButtonActionName = Action.send + public var leftButtonActionName = "" + public var rightButtonActionName = "" + + public var leftButtonHidesKeyboard = false + public var rightButtonHidesKeyboard = false + public var shouldAdjustToKeyboard = true + public var shouldDoRightActionWithReturnKey = true + + public var keyboardType: UIKeyboardType = .default + public var keyboardAppearance: UIKeyboardAppearance = .default + public var returnKeyType: UIReturnKeyType = .done + public var enablesReturnKeyAutomatically = false + + public var textInputScrollsToRecentMessage = true + + public var messageCollectionViewHeaderHeight = 5.0 + public var messageCollectionViewFooterHeight = 20.0 + public var messageCollectionViewHeaderBackgroundColor = UIColor.clear + public var messageCollectionViewFooterBackgroundColor = UIColor.clear + + public var leftMessageCellTextColor: UIColor = .black + public var leftMessageCellBackgroundColor: UIColor = .antiflashWhite + public var rightMessageCellTextColor: UIColor = .antiflashWhite + public var rightMessageCellBackgroundColor: UIColor = .pumpkin + + public var collectionViewBackgroundColor: UIColor = .white + + public var textInputFieldTextColor: UIColor = .black + public var textInputFieldBackgroundColor: UIColor = .clear + public var textInputTintColor: UIColor = .pumpkin + public var textInputFieldTextPlaceholderText: String = "Write your message here" + public var textInputFieldCornerRadius: CGFloat = 0.0 + public var textInputFieldFont: UIFont = .systemFont(ofSize: 10) + + public var textInputFieldTopSeparatorLineHeight: CGFloat = 1.0 + public var textInputFieldTopSeparatorLineColor: UIColor = .pumpkin + public var textInputFieldTopSeparatorLineAlpha: CGFloat = 1.0 + + public var inputToolbarBackgroundColor = UIColor.white + + public var leftButtonText: String = "" + public var leftButtonShow: Bool = false + public var leftButtonShowAnimated: Bool = false + public var leftButtonTextColor: UIColor = .pumpkin + public var leftButtonDisabledColor: UIColor = .antiflashWhite + public var leftButtonBackgroundColor: UIColor = .clear + public var leftButtonBackgroundImage: UIImage? + public var leftButtonCornerRadius: CGFloat = 0.0 - //settings.leftButtonHidesKeyboard = true - settings.rightButtonHidesKeyboard = true - settings.messageCollectionViewHeaderHeight = 5 - settings.messageCollectionViewHeaderBackgroundColor = UIColor.clear - settings.messageCollectionViewFooterHeight = 20 - settings.messageCollectionViewFooterBackgroundColor = UIColor.clear - - return settings - } - - public static func testChatSettings() -> MessagesViewSettings { - let settings = MessagesViewSettings() - settings.leftButtonActionName = Action.sendFromMyself - settings.rightButtonActionName = Action.send - - //settings.leftButtonHidesKeyboard = true - settings.rightButtonHidesKeyboard = true - settings.messageCollectionViewHeaderHeight = 5 - settings.messageCollectionViewHeaderBackgroundColor = UIColor.clear - settings.messageCollectionViewFooterHeight = 20 - settings.messageCollectionViewFooterBackgroundColor = UIColor.clear - - return settings + public var rightButtonText: String = "Send" + public var rightButtonShow: Bool = true + public var rightButtonShowAnimated: Bool = true + public var rightButtonTextColor: UIColor = .pumpkin + public var rightButtonDisabledColor: UIColor = .antiflashWhite + public var rightButtonBackgroundColor: UIColor = .clear + public var rightButtonBackgroundImage: UIImage? + public var rightButtonCornerRadius: CGFloat = 0.0 + + public var buttonSlideAnimationDuration: TimeInterval = 0.5 + + public var groupSeparationSpacing: CGFloat = 12 + public var groupInternalSpacing: CGFloat = 1 + public var minimalHorizontalSpacing: CGFloat = 80 + + public static var defaultSettings: MessagesViewSettings { + return MessagesViewSettings() } } diff --git a/README.md b/README.md index f66cbfe..0694df7 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,221 @@ +![Made with love by PGS](https://cloud.githubusercontent.com/assets/16896355/25438562/3c14f0f2-2a9a-11e7-82f1-53f49a48393e.png) # MessagesView -View for displaying messages similarly to iOS Messages system app -### Using Carthage +[![Swift 3.0](https://img.shields.io/badge/Swift-3.0-orange.svg?style=flat)](https://swift.org/) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + +View for displaying messages similarly to iOS Messages system app. This view when deployed within your application will handle incoming and outgoing messages display. + +While using this module you don't need to handle messages view on your own. You're getting complete solution easy to configure and customize to your needs. If you think you can have more customisation, please tell us what you think via addess below. + +![Messages View](https://cloud.githubusercontent.com/assets/16896355/25438510/0b81e86e-2a9a-11e7-9981-df9030cda73d.png) + + +## Getting started + +In order to start using this framework you need to: +1. Embed this framework +2. Style your view +3. Communicate with view via ViewController + + + +### 1. Embedding framework + + +#### 1.1 Using Carthage In Cartfile put -`github "pgs-dkanak/MessagesView"` +`github "PGSSoft/MessagesView"` + +In project root directory say: +> carthage update --no-use-binaries --platform iOS + +This will fetch the project and compile it to library form. When using carthage, you have two options: + +### a) Standard carthage module + +![embedding carthage framework](https://cloud.githubusercontent.com/assets/16896355/24654187/f16728c6-1938-11e7-806b-5ea14b4c7284.gif) + +Using standard cathage module requires you to go to Carthage/Build folder within your project and drag it into _**Embedded Binaries**_ section int your project **General settings**. +This solution no 1. is pretty standard. Unfortunately it doesn't work with storyboard as we intended and you will not be able to customize MessagesView from storyboard directly. It will be working but have to be customized from code. Considering all conditions above we recommend _Embedding into your project_. + +### b) Embedding sources into your project + +![embedding source project](https://cloud.githubusercontent.com/assets/16896355/24654176/e46736a2-1938-11e7-9425-c856cc9de166.gif) + +to embed this project as source code you need to: +1. Go to folder `Carthage/Checkouts/MessagesView` and drag `MessagesView.scodeproj` into your own project. +2. In MessagesView project find `Products/MessagesView.framework` and drag it into `Embedded Frameworks` section in your project general settings + +*NOTE: To be able to track changes from storyboard at design time, you need to embed framework in non-standard way as described in 1b.* + + +### 2. Styling your View + +Let's design! + +1. Go to storyboard +2. Set up a `UIView` with constraints of your choice +3. Change owner class from `UIView` to `MessagesView`. Don't forget to change module name underneath too into `MessagesView`. Xcode will now recompile source code necessary to show rendered MessagesView in Storyboard. Completely rendered messages view will appear in storyboard in seconds! + +Now you are free to style your messages view as you wish! + +Example: +- change messages background color +- change button label caption +- change button background color +- change button background image +![styling input field background and text field rounding](https://cloud.githubusercontent.com/assets/16896355/24654216/10f95768-1939-11e7-9163-79acc0753d62.gif) +You can find full list of customizable properties in the Wiki. This will be prepared soon. + +### 3. Communicating with View via ViewController + +Create example ViewController From example below. ViewController has to conform protocols `MessagesViewDataSource` and `MessagesViewDelegate`. + +In order to communicate with MessagesView, your ViewController should contain: +- `MessagesViewDelegate` to take action when user taps a button +- `MessagesViewDataSource` to feed the view +- `IBOutlet MessagesView` to read information from view + +##### Don't forget do connect MessagesView to its `MessagesViewDataSource` and `MessagesViewDelegate`! + +#### 3.1 MessagesViewDelegate + + +```swift +public protocol MessagesViewDelegate { + func didTapLeftButton() + func didTapRightButton() +} +``` + +Your viewController need to implement actions taken after user taps left or right button + +#### 3.2 MessagesViewDataSource + +To feed view with intormation, your datasource have to provide two sets of information: *peers* and *messages* + +```swift +public protocol MessagesViewDataSource { + var messages : [MessagesViewChatMessage] {get} + var peers : [MessagesViewPeer] {get} +} +``` + +As you can see objects that carry messages have to conform to `MessagesViewChatMessage` protocol and objects that carry peers have to conform to `MessagesViewPeer`protocol. These are listed below: + +```swift +public protocol MessagesViewChatMessage { + var text : String {get} + var sender: MessagesViewPeer {get} + var onRight : Bool {get} +} + +public protocol MessagesViewPeer { + var id : String {get} +} +``` + +In the demo app we created extension to ViewController class that makes it compliant to `MessagesViewDataSource` protocol. In this case the limitation was that extension cannot contain stored properties so we decided to provide information via computed variables only but you can do as you wish in your own project. You need only to have object which is `MessagesViewDataSource` compliant. This is how we dealt with it in demo app: + +```swift +extension ViewController: MessagesViewDataSource { + struct Peer: MessagesViewPeer { + var id: String + } + + struct Message: MessagesViewChatMessage { + var text: String + var sender: MessagesViewPeer + var onRight: Bool + } + + var peers: [MessagesViewPeer] { + return TestData.peerNames.map{ Peer(id: $0) } + } + + var messages: [MessagesViewChatMessage] { + return TestData.exampleMessageText.enumerated().map { (index, element) in + let peer = self.peers[index % peers.count] + return Message(text: element, sender: peer, onRight: index%2 == 0) + } + } +} +``` + +In this example, we have converted on the fly our stored `TestData` information into *Messages* and *Peers*. No other class in the project have to be aware of `MessagesView`. Only `ViewController` is interested. + +#### 3.3 Create IBOutlet messagesView + +Create IBOutlet in standard way +![creating IBOutlet for messagesView](https://cloud.githubusercontent.com/assets/16896355/24657607/2cff52f6-1947-11e7-8840-a6c2bb3d44fb.gif) + +Having your ViewController know about messagesView presence enables it to use view's public API. + +#### 3.4 Connecting delegate and data source + +In our example it is when ViewController loads: + +```swift + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + messagesView.delegate = self + messagesView.dataSource = self + } +``` + +#### 3.5 Custom behaviours + +Additional behaviours that may be useful to you. + +- hiding buttons +```swift +leftButton(show: Bool, animated: Bool) +rightButton(show: Bool, animated: Bool) +``` + +This way you can show or hide button. Animated or not - as you wish. + +### Stay in touch +If you have any ideas how this project can be developed - do not hesitate to contact us at: + +dkanak@pgs-soft.com + +mgocal@pgs-soft.com + +bdudar@pgs-soft.com + +kszwaba@pgs-soft.com + +You are now fully aware of MessagesView capabilities. Good luck! + +### Contributing to development +When contributing to MessagesView you may need to run it from within another project. Embedding this framework as described in paragraph 1.1b will help you work on opened source of this project. + +Made with love in PGS ♥ -### Contributing development -When contributing to MessagesView you may need to run it from within another project. Following steps will help you setting your own project to use MessagesView as external framework: +__Please Use develop branch and fork from there!__ -1. Clone this repository -2. Launch test build -3. In **Build Phases** add _**Run Script - copy framework to project folder**_ with script text +### License ### +MIT License -`cp -rf ${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/MessagesView.framework ${SRCROOT}` +Copyright (c) 2016 PGS Software SA +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: -4. _MessagesView.framework_ will be copied directly into _MessagesView project root directory_. -5. Link it into project you're actually working on. -6. In **Build Settings** update **Framework Search Paths** and add path to framework. By default it will be MessagesView project root directory. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -__Please Use develop branch!__ +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.