From f1719597029ef84ef67fe51bdbb4ac97d75cf4ad Mon Sep 17 00:00:00 2001 From: Damian Kanak Date: Tue, 4 Apr 2017 13:14:56 +0200 Subject: [PATCH 1/8] Added embedding from carthage description --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f66cbfe..f966e21 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,64 @@ # MessagesView -View for displaying messages similarly to iOS Messages system app +View for displaying messages similarly to iOS Messages system app. This view when deployed within your application will handle incoming and outgoing messages display. -### Using Carthage +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. + + +## Getting started + +In order to start using this framework you need to: +1. Embed this framework into your project using carthage +2. Create example ViewController From example below. ViewController has to conform protocols `MessagesViewDataSource` and `MessagesViewDelegate`. + +### 1. Embedding framework + + +#### 1.1 Using Carthage In Cartfile put `github "pgs-dkanak/MessagesView"` -### 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: +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: +1. Use as you would use standard carthage module +2. Embedding sources into your project + +### a) Standard carthage module + +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_. -1. Clone this repository -2. Launch test build -3. In **Build Phases** add _**Run Script - copy framework to project folder**_ with script text +### b) Embedding sources into your project -`cp -rf ${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/MessagesView.framework ${SRCROOT}` +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. Using framework + +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 +You can find full list of customizable properties in the Wiki. This will be prepared soon. + + + +### Contributing 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. -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. -__Please Use develop branch!__ +__Please Use develop branch and fork from there!__ From a2dc3da948622b0833638fe39323199af84dd208 Mon Sep 17 00:00:00 2001 From: Damian Kanak Date: Tue, 4 Apr 2017 13:27:56 +0200 Subject: [PATCH 2/8] Added animated gifs as tutorial --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f966e21..841c814 100644 --- a/README.md +++ b/README.md @@ -21,16 +21,18 @@ 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: -1. Use as you would use standard carthage module -2. Embedding sources into your project ### 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 @@ -53,6 +55,7 @@ Example: - 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. From d47575882ee18edc9aff3777a71966b8e243b919 Mon Sep 17 00:00:00 2001 From: Damian Kanak Date: Tue, 4 Apr 2017 15:29:41 +0200 Subject: [PATCH 3/8] Communicating with View via ViewController License Contact information --- README.md | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 151 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 841c814..7dff5d5 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,11 @@ While using this module you don't need to handle messages view on your own. You' ## Getting started In order to start using this framework you need to: -1. Embed this framework into your project using carthage -2. Create example ViewController From example below. ViewController has to conform protocols `MessagesViewDataSource` and `MessagesViewDelegate`. +1. Embed this framework +2. Style your view +3. Communicate with view via ViewController + + ### 1. Embedding framework @@ -40,7 +43,7 @@ to embed this project as source code you need to: *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. Using framework +### 2. Styling your View Let's design! @@ -58,10 +61,154 @@ Example: ![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) -### Contributing development +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 ♥ __Please Use develop branch and fork from there!__ + +### License ### +MIT License + +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: + +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. From f4c213445d29405ce95647345421852986712031 Mon Sep 17 00:00:00 2001 From: Damian Kanak Date: Wed, 26 Apr 2017 16:15:31 +0200 Subject: [PATCH 4/8] Update README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 7dff5d5..8eabe93 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,15 @@ +![Made with love by PGS](https://cloud.githubusercontent.com/assets/16896355/25438562/3c14f0f2-2a9a-11e7-82f1-53f49a48393e.png) # MessagesView + +[![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 From 8b1c135f962080772ef7b72ee92b050b6e6f2738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Thu, 27 Apr 2017 08:26:49 +0200 Subject: [PATCH 5/8] change path after repo migration --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8eabe93..0694df7 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ In order to start using this framework you need to: #### 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 From d007b8ea07954c9c421ac0d6aa47947958a1187f Mon Sep 17 00:00:00 2001 From: Damian Kanak Date: Thu, 27 Apr 2017 13:46:27 +0200 Subject: [PATCH 6/8] bubble image flipped by public placeholder text in settings works corrected access to button actions scaleAspectFit for button images organised settings order settings made all public --- MessagesView/BubbleImage.swift | 4 +- MessagesView/MessageEditorTextView.swift | 27 +-- MessagesView/MessagesInputToolbar.swift | 33 +++- MessagesView/MessagesToolbarContentView.swift | 16 +- MessagesView/MessagesToolbarContentView.xib | 6 +- MessagesView/MessagesView.swift | 100 +++++----- MessagesView/MessagesViewSettings.swift | 174 ++++++------------ 7 files changed, 161 insertions(+), 199 deletions(-) diff --git a/MessagesView/BubbleImage.swift b/MessagesView/BubbleImage.swift index 9dd563c..7fd861d 100644 --- a/MessagesView/BubbleImage.swift +++ b/MessagesView/BubbleImage.swift @@ -27,7 +27,7 @@ public class BubbleImage { public lazy var middle: UIImage? = self.cropAndResize(slice: .middle) public lazy var bottom: UIImage? = self.cropAndResize(slice: .bottom) - var flipped: BubbleImage { + public var flipped: BubbleImage { let flippedImage = image.flipped let flippedResizeInsets = insetsFlippedHorizontally(resizeInsets) @@ -132,7 +132,7 @@ public class BubbleImage { return result } - public static func createTailPathIn(origin: CGPoint, size: CGSize) -> 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..284c208 100644 --- a/MessagesView/MessageEditorTextView.swift +++ b/MessagesView/MessageEditorTextView.swift @@ -8,29 +8,14 @@ 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 } } diff --git a/MessagesView/MessagesInputToolbar.swift b/MessagesView/MessagesInputToolbar.swift index 99aa511..2b18fc9 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 leftButtonAction: ()->() { + get { + return toolbarContentView.leftButtonAction + } + set { + toolbarContentView.leftButtonAction = newValue + } + } + var rightButtonAction: ()->() { + get { + return toolbarContentView.rightButtonAction + } + set { + toolbarContentView.rightButtonAction = newValue + } } - */ - var messageText : String { - return toolbarContentView.messageText + + 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 e25c8ba..d6da7b2 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: (Void) -> () = {} + var rightButtonAction: (Void) -> () = {} + 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 { @@ -87,8 +90,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) { diff --git a/MessagesView/MessagesToolbarContentView.xib b/MessagesView/MessagesToolbarContentView.xib index 9b061c1..1f18889 100644 --- a/MessagesView/MessagesToolbarContentView.xib +++ b/MessagesView/MessagesToolbarContentView.xib @@ -14,7 +14,7 @@ - + - + - + diff --git a/MessagesView/MessagesView.swift b/MessagesView/MessagesView.swift index 66d0c51..eef3764 100644 --- a/MessagesView/MessagesView.swift +++ b/MessagesView/MessagesView.swift @@ -56,13 +56,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 @@ -101,13 +102,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) } } @@ -187,11 +193,11 @@ 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 @@ -307,42 +313,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..ef469d3 100644 --- a/MessagesView/MessagesViewSettings.swift +++ b/MessagesView/MessagesViewSettings.swift @@ -10,121 +10,65 @@ 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 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() } } From 8fa7b8b3a822ff97907d9b10d60c98cbd4daad43 Mon Sep 17 00:00:00 2001 From: Bartosz Dudar Date: Wed, 18 Oct 2017 13:32:13 +0200 Subject: [PATCH 7/8] Xcode 9 auto-changes --- MessagesView.xcodeproj/project.pbxproj | 14 +++++++++++++- .../xcshareddata/xcschemes/MessagesView.xcscheme | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) 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 @@ Date: Thu, 19 Oct 2017 18:03:16 +0200 Subject: [PATCH 8/8] Fixed adjusting to changing keyboard types, added keyboard settings (type, appearance, return type key, return key auto-enabled, right action by return key), trimming and preventing empty text in demo --- Demo/ViewController.swift | 8 ++- MessagesView/MessageEditorTextView.swift | 6 ++ MessagesView/MessagesToolbarContentView.swift | 9 ++- MessagesView/MessagesToolbarContentView.xib | 7 ++- MessagesView/MessagesView.swift | 56 ++++++++++++------- MessagesView/MessagesViewSettings.swift | 6 ++ 6 files changed, 66 insertions(+), 26 deletions(-) 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/MessageEditorTextView.swift b/MessagesView/MessageEditorTextView.swift index 284c208..7cc24fe 100644 --- a/MessagesView/MessageEditorTextView.swift +++ b/MessagesView/MessageEditorTextView.swift @@ -11,11 +11,17 @@ import UIKit class MessageEditorTextView: UITextField { func applySettings(settings: MessagesViewSettings) { + 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/MessagesToolbarContentView.swift b/MessagesView/MessagesToolbarContentView.swift index d3377f5..2f39493 100644 --- a/MessagesView/MessagesToolbarContentView.swift +++ b/MessagesView/MessagesToolbarContentView.swift @@ -34,8 +34,8 @@ class MessagesToolbarContentView: UIView { private var leftButtonEnabled: Bool = true private var rightButtonEnabled: Bool = true - var leftButtonAction: (Void) -> () = {} - var rightButtonAction: (Void) -> () = {} + var leftButtonAction: () -> () = {} + var rightButtonAction: () -> () = {} private var leftTintColor: UIColor { return leftButtonEnabled ? settings.leftButtonTextColor : settings.leftButtonDisabledColor @@ -196,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 1f18889..9138c15 100644 --- a/MessagesView/MessagesToolbarContentView.xib +++ b/MessagesView/MessagesToolbarContentView.xib @@ -1,10 +1,11 @@ - - + + - + + diff --git a/MessagesView/MessagesView.swift b/MessagesView/MessagesView.swift index f82efa4..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 @@ -97,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 @@ -207,49 +210,62 @@ public class MessagesView: UIView { 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() } } diff --git a/MessagesView/MessagesViewSettings.swift b/MessagesView/MessagesViewSettings.swift index ef469d3..bc08252 100644 --- a/MessagesView/MessagesViewSettings.swift +++ b/MessagesView/MessagesViewSettings.swift @@ -16,6 +16,12 @@ public class MessagesViewSettings { 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