diff --git a/Assets/logo.svg b/Assets/logo.svg new file mode 100644 index 00000000..5a1f1da3 --- /dev/null +++ b/Assets/logo.svg @@ -0,0 +1,32 @@ + + + + logo + Created with Sketch. + + + + + + + + + + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..80f4b83f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,75 @@ +# Contributing Guidelines + +### Code of Conduct + +Please read our [Code of Conduct](https://github.com/MessageKit/MessageKit/blob/974cb068f40a4959ec3b7bbf5894d405925079e4/Code_of_Conduct.md). +The MessageKit maintainers take this Code of Conduct very seriously. Intolereance, disrespect, harassment, and any form of negavity will not be tolerated. + +### Ways to Contribute + +You can contribute to MessageKit in a variety of ways: + +- Fixing or reporting bugs :scream: +- Improving documentation :heart: +- Suggesting new features :smiley: +- Increasing unit test coverage :pray: +- Resolving any open issues :+1: + +If you're new to Open Source or Swift, the MessageKit community is a great place to get involved. + +**Your contributions are always welcome, no contribution is too small.** + +### Opening a New Issue + +- Please check the [README](https://github.com/MessageKit/MessageKit/blob/master/README.md) to see if your question is answered there. +- Search [open issues](https://github.com/MessageKit/MessageKit/issues?q=is%3Aopen+is%3Aissue) and [closed issues](https://github.com/MessageKit/MessageKit/issues?q=is%3Aissue+is%3Aclosed) to avoid opening a duplicate issue. +- Avoiding duplicate issues organizes all relevant information for project maintainers and other users. +- If no issues represent your problem, please open a new issue with a good tile and useful description. + +- Information to Provide When Opening an Issue: + - MessageKit version(s) + - iOS version(s) + - Devices/Simulators affected + - Full crash log, if applicable + - A well written description of the problem you are experiencing + - *Please provide complete steps to reproduce the issue* + - For UI related issues, please provide a screenshot/GIF/video showing the issue + - Link to a project or demo project that exhibits the issue + - Search for an list any issues that might be related + +The more information you can provide, the easier it will be for us to resolve your issue in a timely manner. + +### Submitting a Pull Request + +- Please read the MessageKit [VISION.md](https://github.com/MessageKit/MessageKit/blob/master/VISION.md) document to understand the project goals, scope, and other technical considerations. +- We strongly encourage you to open an issue discussing any potential new features before an implementation is provided. + - This ensures the feature is in scope and no ones time is wasted. + +- **Please DO NOT submit pull requests to the `master` branch** + - This branch is always stable and represents a relase. + +- **Please DO submit your pull request to the branch repersenting the next release version** + +1. Link to any issues the pull request resolves. If none exist, create one. +2. Write unit tests for new functionality or fix any broken by your changes. +3. If your changes affect documentation, please update them accordingly. +4. Avoid pull requests with a large number of commits. +5. Write clean code and follow the MessageKit [style guidelines](#style-guidelines). + +**You should submit one pull request per feature, the smaller the pull request the better chances it will be merged.** +Enormous pull requests take a signficant time to review and understand their implications on the existing codebase. + +### Style Guidelines + +Writing clean code and upholding project standards is as important as adding new features. To ensure this, MessageKit employs a few practices: + +1. We use [SwiftLint](https://github.com/realm/SwiftLint) to enforce style and conventions at compile time. +2. We adhere to the [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/). +3. We follow the [Raywenderlich Swift Style Guide ](https://github.com/raywenderlich/swift-style-guide) but may have slight variations. + +### Questions and Contact + +If any of the sections above are unclear and require further explanation, *do not hesitate to reach out*. +MessageKit strives to build an inclusive open source community and to make contributing as easy as possible for memebers of all experience levels. + +**You can get in touch with the MessageKit core team directly by joining our open Slack community channel: [here](https://join.slack.com/t/messagekit/shared_invite/MjI0NDkxNjgwMzA3LTE1MDIzMTU0MjUtMzJhZDZlNTkxMA).** diff --git a/Example/Sources/ConversationViewController.swift b/Example/Sources/ConversationViewController.swift index e4453d26..2015e2c6 100644 --- a/Example/Sources/ConversationViewController.swift +++ b/Example/Sources/ConversationViewController.swift @@ -62,15 +62,15 @@ extension ConversationViewController: MessagesDataSource { extension ConversationViewController: MessagesDisplayDataSource { - func avatarForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar { + func avatar(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar { return SampleData().getAvatarFor(sender: message.sender) } - func headerForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageHeaderView? { + func messageHeaderView(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageHeaderView? { return messagesCollectionView.dequeueMessageHeaderView(for: indexPath) } - func footerForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageFooterView? { + func messageFooterView(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageFooterView? { return messagesCollectionView.dequeueMessageFooterView(for: indexPath) } @@ -92,11 +92,11 @@ extension ConversationViewController: MessagesDisplayDataSource { extension ConversationViewController: MessagesLayoutDelegate { - func headerSizeFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize { + func headerViewSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize { return CGSize(width: messagesCollectionView.bounds.width, height: 4) } - func footerSizeFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize { + func footerViewSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize { return CGSize(width: messagesCollectionView.bounds.width, height: 4) } diff --git a/Example/Sources/MockMessage.swift b/Example/Sources/MockMessage.swift index 355eb82d..d8d36c62 100644 --- a/Example/Sources/MockMessage.swift +++ b/Example/Sources/MockMessage.swift @@ -26,17 +26,25 @@ import Foundation import MessageKit struct MockMessage: MessageType { - - var messageId: String - var sender: Sender - var sentDate: Date - var data: MessageData - - init(text: String, sender: Sender, messageId: String) { - data = .text(text) - self.sender = sender - self.messageId = messageId - self.sentDate = Date() - } - + + var messageId: String + var sender: Sender + var sentDate: Date + var data: MessageData + + private init(data: MessageData, sender: Sender, messageId: String) { + self.data = data + self.sender = sender + self.messageId = messageId + self.sentDate = Date() + } + + init(text: String, sender: Sender, messageId: String) { + self.init(data: .text(text), sender: sender, messageId: messageId) + } + + init(attributedText: NSAttributedString, sender: Sender, messageId: String) { + self.init(data: .attributedText(attributedText), sender: sender, messageId: messageId) + } + } diff --git a/Example/Sources/SampleData.swift b/Example/Sources/SampleData.swift index 5fbf6a95..c7dccf8e 100644 --- a/Example/Sources/SampleData.swift +++ b/Example/Sources/SampleData.swift @@ -39,8 +39,28 @@ struct SampleData { let msg6 = MockMessage(text: "I think if you do something and it turns out pretty good, then you should go do something else wonderful, not dwell on it for too long. Just figure out what’s next.", sender: Jobs, messageId: UUID().uuidString) let msg7 = MockMessage(text: "Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big choices in life. Because almost everything - all external expectations, all pride, all fear of embarrassment or failure - these things just fall away in the face of death, leaving only what is truly important.", sender: Jobs, messageId: UUID().uuidString) let msg8 = MockMessage(text: "Price is rarely the most important thing. A cheap product might sell some units. Somebody gets it home and they feel great when they pay the money, but then they get it home and use it and the joy is gone.", sender: Cook, messageId: UUID().uuidString) - - return [msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8] + + let msg9Text = NSString(string: "Use .attributedText() to add bold, italic, colored text and more...") + let msg9AttributedText = NSMutableAttributedString(string: String(msg9Text)) + + if #available(iOS 9.0, *) { + msg9AttributedText.addAttributes([NSFontAttributeName: UIFont.monospacedDigitSystemFont(ofSize: UIFont.systemFontSize, weight: UIFontWeightBold)], range: msg9Text.range(of: ".attributedText()")) + } else { + msg9AttributedText.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle], range: msg9Text.range(of: ".attributedText()")) + } + + msg9AttributedText.addAttributes([NSFontAttributeName: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)], + range: msg9Text.range(of: "bold")) + + msg9AttributedText.addAttributes([NSFontAttributeName: UIFont.italicSystemFont(ofSize: UIFont.systemFontSize)], + range: msg9Text.range(of: "italic")) + + msg9AttributedText.addAttributes([NSForegroundColorAttributeName: UIColor.red], + range: msg9Text.range(of: "colored")) + + let msg9 = MockMessage(attributedText: msg9AttributedText, sender: Jobs, messageId: UUID().uuidString) + + return [msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9] } func getCurrentSender() -> Sender { diff --git a/MessageKit.podspec b/MessageKit.podspec index 722e6954..faca6b81 100644 --- a/MessageKit.podspec +++ b/MessageKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'MessageKit' - s.version = '0.3.0' + s.version = '0.4.0' s.license = { :type => "MIT", :file => "LICENSE.md" } s.summary = 'An elegant messages UI library for iOS.' diff --git a/README.md b/README.md index fb7159f1..e6ce94b2 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,78 @@ # MessageKit +

+ +

+ [![Build Status](https://travis-ci.org/MessageKit/MessageKit.svg)](https://travis-ci.org/MessageKit/MessageKit) [![codecov](https://codecov.io/gh/MessageKit/MessageKit/branch/master/graph/badge.svg)](https://codecov.io/gh/MessageKit/MessageKit) -**In-progress**, this project is intended to be a Swift rewrite of the recently deprecated [JSQMessagesViewController](https://github.com/jessesquires/JSQMessagesViewController). -## Want to contribute? +## Table of Contents -Great! :+1: Because we could really use the help of the iOS community. +* [**Goal**](#goal)📈 +* [**Contributing**](#contributing) +* [**Getting started**](#getting-started) +* [**Requirements**](#requirements) +* [**Code of Conduct**](#code-of-conduct) +* [**What's Next**](#what's-next) +* [**Apps using this library**](#apps-using-this-library) +* [**License**](#license) -See [#1](https://github.com/MessageKit/MessageKit/issues/1) to familiarize yourself with the effort. -See [#12](https://github.com/MessageKit/MessageKit/issues/12) to discuss v0.2.0. +## Goals -## Submitting a Pull Request - -This project is in active development and branches are likely out of sync with their local counterparts. -If you want to submit a pull request, please join our [Slack channel](https://github.com/MessageKit/MessageKit/issues/22) to discuss your proposed changes. +- Provide a :rotating_light:safe:rotating_light: enviroment for others to learn an grow through Open Source. +- Make adding Chat:speech_balloon: to a project easy. +- Enable beautiful and customizable Chat UI's. +- Provide an awesome Open Source project for the iOS open source community. +- Help others learn. ## Vision - See [VISION.md](https://github.com/MessageKit/MessageKit/blob/master/VISION.md) for Goals, Scope, & Technical Considerations. + + +## Installation +### [CocoaPods](https://cocoapods.org/) **Recomended** +````ruby +pod 'MessageKit' +```` + + +## Requirements + +- **iOS9** or later + + +## Contributing + +Great! Look over these things first. +- Please read our [Code of Conduct](https://github.com/MessageKit/MessageKit/blob/master/Code_of_Conduct.md) +- Check the [Contributing Guide Lines](https://github.com/MessageKit/MessageKit/blob/master/CONTRIBUTING.md). +- Come join us on [Slack](https://join.slack.com/t/messagekit/shared_invite/MjI0NDkxNjgwMzA3LTE1MDIzMTU0MjUtMzJhZDZlNTkxMA) and 🗣 don't be a stranger. +- Check out the [current issues](https://github.com/MessageKit/MessageKit/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](#what's-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/MessageKit/releases) to see what we are working on next. + +### 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.* + +## Thanks + +Many thanks to [**the contributors**](https://github.com/MessageKit/MessageKit/graphs/contributors) of this project. + +## License +MessageKit is released under the [MIT License](). + +## Inspiration +Inspired by [JSQMessagesViewController](https://github.com/jessesquires/JSQMessagesViewController) :point_left: :100: diff --git a/Sources/Avatar.swift b/Sources/Avatar.swift index e9bb305a..3d05b1a5 100644 --- a/Sources/Avatar.swift +++ b/Sources/Avatar.swift @@ -1,12 +1,29 @@ -// -// Avatar.swift -// MessageKit -// -// Created by Dan Leonard on 8/7/17. -// Copyright © 2017 MessageKit. All rights reserved. -// +/* + MIT License + + Copyright (c) 2017 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 + public struct Avatar { public let image: UIImage? diff --git a/Sources/AvatarView.swift b/Sources/AvatarView.swift index 537cc32a..9b6dbf2c 100644 --- a/Sources/AvatarView.swift +++ b/Sources/AvatarView.swift @@ -25,9 +25,11 @@ import Foundation open class AvatarView: UIView { + // MARK: - Properties - internal var avatar: Avatar = Avatar() - internal var imageView = UIImageView() + + open var avatar: Avatar = Avatar() + open var imageView = UIImageView() // MARK: - initializers override init(frame: CGRect) { @@ -41,7 +43,7 @@ open class AvatarView: UIView { prepareView() } - func getImageFrom(initals: String, withColor color: UIColor = UIColor.white, fontSize: CGFloat = 14) -> UIImage { + private func getImageFrom(initals: String, withColor color: UIColor = UIColor.white, fontSize: CGFloat = 14) -> UIImage { _ = UIGraphicsBeginImageContext(CGSize(width: 30, height: 30)) let context = UIGraphicsGetCurrentContext()! diff --git a/Sources/InputTextView.swift b/Sources/InputTextView.swift index 55ae79ca..9821732c 100644 --- a/Sources/InputTextView.swift +++ b/Sources/InputTextView.swift @@ -95,7 +95,7 @@ open class InputTextView: UITextView { } - fileprivate func addObservers() { + private func addObservers() { NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: Notification.Name.UITextViewTextDidChange, diff --git a/Sources/MessageCollectionViewCell.swift b/Sources/MessageCollectionViewCell.swift index 8b8f8fcc..6ba295c3 100644 --- a/Sources/MessageCollectionViewCell.swift +++ b/Sources/MessageCollectionViewCell.swift @@ -28,7 +28,7 @@ open class MessageCollectionViewCell: UICollectionViewCell { // MARK: - Properties - open let messageContainerView: UIView = { + open var messageContainerView: UIView = { let messageContainerView = UIView() messageContainerView.layer.cornerRadius = 12.0 messageContainerView.layer.masksToBounds = true @@ -98,29 +98,29 @@ open class MessageCollectionViewCell: UICollectionViewCell { } - func cellTopLabelFrame(for attributes: MessagesCollectionViewLayoutAttributes) -> CGRect { + private func cellTopLabelFrame(for attributes: MessagesCollectionViewLayoutAttributes) -> CGRect { var origin: CGPoint = .zero - if attributes.topLabelPinnedUnderMessage { + if !attributes.topLabelExtendsPastAvatar { origin = CGPoint(x: attributes.avatarSize.width + attributes.avatarMessagePadding, y: 0) } return CGRect(origin: origin, size: attributes.cellTopLabelSize) } - func cellBottomLabelFrame(for attributes: MessagesCollectionViewLayoutAttributes) -> CGRect { + private func cellBottomLabelFrame(for attributes: MessagesCollectionViewLayoutAttributes) -> CGRect { var origin: CGPoint = CGPoint(x: 0, y: contentView.frame.height - attributes.cellBottomLabelSize.height) - if attributes.bottomLabelPinnedUnderMessage { + if !attributes.bottomLabelExtendsPastAvatar { origin.x = attributes.avatarSize.width + attributes.avatarMessagePadding } return CGRect(origin: origin, size: attributes.cellBottomLabelSize) } - func messageContainerFrame(for attributes: MessagesCollectionViewLayoutAttributes) -> CGRect { + private func messageContainerFrame(for attributes: MessagesCollectionViewLayoutAttributes) -> CGRect { var origin: CGPoint = .zero @@ -139,7 +139,7 @@ open class MessageCollectionViewCell: UICollectionViewCell { } - func avatarViewFrame(for attributes: MessagesCollectionViewLayoutAttributes) -> CGRect { + private func avatarViewFrame(for attributes: MessagesCollectionViewLayoutAttributes) -> CGRect { var origin: CGPoint = .zero @@ -157,7 +157,7 @@ open class MessageCollectionViewCell: UICollectionViewCell { } - func configure(with message: MessageType) { + public func configure(with message: MessageType) { switch message.data { case .text(let text): @@ -168,7 +168,7 @@ open class MessageCollectionViewCell: UICollectionViewCell { } - func setupGestureRecognizers() { + private func setupGestureRecognizers() { let avatarTapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapAvatar)) avatarView.addGestureRecognizer(avatarTapGesture) diff --git a/Sources/MessageDirection.swift b/Sources/MessageDirection.swift index a2a7f310..cb24a46a 100644 --- a/Sources/MessageDirection.swift +++ b/Sources/MessageDirection.swift @@ -22,7 +22,7 @@ SOFTWARE. */ -public enum MessageDirection { +enum MessageDirection { case incoming case outgoing diff --git a/Sources/MessagesCollectionView.swift b/Sources/MessagesCollectionView.swift index e420a8f7..ea60e5ce 100644 --- a/Sources/MessagesCollectionView.swift +++ b/Sources/MessagesCollectionView.swift @@ -34,9 +34,7 @@ open class MessagesCollectionView: UICollectionView { open weak var messageCellDelegate: MessageCellDelegate? - //open weak var messagesDisplayDataSource: MessagesDisplayDataSource? - - var indexPathForLastItem: IndexPath? { + private var indexPathForLastItem: IndexPath? { let lastSection = numberOfSections > 0 ? numberOfSections - 1 : 0 guard numberOfItems(inSection: lastSection) > 0 else { return nil } @@ -57,7 +55,7 @@ open class MessagesCollectionView: UICollectionView { // MARK: - Methods - func scrollToBottom(animated: Bool = false) { + public func scrollToBottom(animated: Bool = false) { guard let indexPath = indexPathForLastItem else { return } scrollToItem(at: indexPath, at: .bottom, animated: animated) } diff --git a/Sources/MessagesCollectionViewFlowLayout.swift b/Sources/MessagesCollectionViewFlowLayout.swift index 3f2c85e6..8e578c0d 100644 --- a/Sources/MessagesCollectionViewFlowLayout.swift +++ b/Sources/MessagesCollectionViewFlowLayout.swift @@ -1,18 +1,18 @@ /* MIT License - + Copyright (c) 2017 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 @@ -33,10 +33,10 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout { open var messageToViewEdgePadding: CGFloat open var cellTopLabelInsets: UIEdgeInsets - open var topLabelPinnedUnderMessage: Bool + open var topLabelExtendsPastAvatar: Bool open var cellBottomLabelInsets: UIEdgeInsets - open var bottomLabelPinnedUnderMessage: Bool + open var bottomLabelExtendsPastAvatar: Bool open var incomingAvatarSize: CGSize open var outgoingAvatarSize: CGSize @@ -66,10 +66,10 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout { messageToViewEdgePadding = 30.0 cellTopLabelInsets = .zero - topLabelPinnedUnderMessage = true + topLabelExtendsPastAvatar = false cellBottomLabelInsets = .zero - bottomLabelPinnedUnderMessage = true + bottomLabelExtendsPastAvatar = false incomingAvatarSize = CGSize(width: 30, height: 30) outgoingAvatarSize = CGSize(width: 30, height: 30) @@ -126,11 +126,11 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout { attributes.cellTopLabelSize = cellTopLabelSize(for: message, at: indexPath) attributes.cellTopLabelInsets = cellTopLabelInsets - attributes.topLabelPinnedUnderMessage = topLabelPinnedUnderMessage + attributes.topLabelExtendsPastAvatar = topLabelExtendsPastAvatar attributes.cellBottomLabelSize = cellBottomLabelSize(for: message, at: indexPath) attributes.cellBottomLabelInsets = cellBottomLabelInsets - attributes.bottomLabelPinnedUnderMessage = bottomLabelPinnedUnderMessage + attributes.bottomLabelExtendsPastAvatar = bottomLabelExtendsPastAvatar attributes.avatarSize = avatarSize(for: message) attributes.avatarBottomPadding = avatarBottomPadding @@ -159,7 +159,7 @@ extension MessagesCollectionViewFlowLayout { // MARK: - Size Calculations - func avatarSize(for message: MessageType) -> CGSize { + fileprivate func avatarSize(for message: MessageType) -> CGSize { guard let messagesCollectionView = messagesCollectionView else { return .zero } guard let messagesDataSource = messagesCollectionView.messagesDataSource else { return .zero } @@ -169,7 +169,7 @@ extension MessagesCollectionViewFlowLayout { return isOutgoingMessage ? outgoingAvatarSize : incomingAvatarSize } - func messageContainerSize(for message: MessageType, at indexPath: IndexPath) -> CGSize { + fileprivate func messageContainerSize(for message: MessageType, at indexPath: IndexPath) -> CGSize { let messageWidth = messageContainerWidth(for: message, at: indexPath) let messageHeight = messageContainerHeight(for: message, at: indexPath) @@ -177,7 +177,7 @@ extension MessagesCollectionViewFlowLayout { return CGSize(width: messageWidth, height: messageHeight) } - func cellTopLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize { + fileprivate func cellTopLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize { let topLabelWidth = cellTopLabelWidth(for: message) let topLabelHeight = cellTopLabelHeight(for: message, at: indexPath) @@ -186,7 +186,7 @@ extension MessagesCollectionViewFlowLayout { } - func cellBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize { + fileprivate func cellBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize { let bottomLabelWidth = cellBottomLabelWidth(for: message) let bottomLabelHeight = cellBottomLabelHeight(for: message, at: indexPath) @@ -197,23 +197,24 @@ extension MessagesCollectionViewFlowLayout { // MARK: - Width Calculations - func cellTopLabelWidth(for message: MessageType) -> CGFloat { - if topLabelPinnedUnderMessage { - return itemWidth - avatarSize(for: message).width - avatarMessagePadding - messageToViewEdgePadding - } else { + private func cellTopLabelWidth(for message: MessageType) -> CGFloat { + if topLabelExtendsPastAvatar { return itemWidth + } else { + return itemWidth - avatarSize(for: message).width - avatarMessagePadding - messageToViewEdgePadding } } - func cellBottomLabelWidth(for message: MessageType) -> CGFloat { - if bottomLabelPinnedUnderMessage { - return itemWidth - avatarSize(for: message).width - avatarMessagePadding - messageToViewEdgePadding - } else { + private func cellBottomLabelWidth(for message: MessageType) -> CGFloat { + if bottomLabelExtendsPastAvatar { return itemWidth + } else { + return itemWidth - avatarSize(for: message).width - avatarMessagePadding - messageToViewEdgePadding + } } - func availableWidthForMessageContainer(considering message: MessageType) -> CGFloat { + private func availableWidthForMessageContainer(considering message: MessageType) -> CGFloat { let avatarWidth = avatarSize(for: message).width let avatarWidthPlusPadding = avatarWidth == 0 ? 0 : avatarWidth + avatarMessagePadding let horizontalMessageInsets = messageLabelInsets.left + messageLabelInsets.right @@ -223,7 +224,7 @@ extension MessagesCollectionViewFlowLayout { // MARK: - View Height Calculations - func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { + private func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { guard let messagesCollectionView = messagesCollectionView else { return 0 } guard let displayDataSource = messagesCollectionView.messagesDataSource as? MessagesDisplayDataSource else { return 0 } @@ -237,7 +238,7 @@ extension MessagesCollectionViewFlowLayout { } - func cellBottomLabelHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { + private func cellBottomLabelHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { guard let messagesCollectionView = messagesCollectionView else { return 0 } guard let displayDataSource = messagesCollectionView.messagesDataSource as? MessagesDisplayDataSource else { return 0 } @@ -250,7 +251,7 @@ extension MessagesCollectionViewFlowLayout { return estimatedHeight.rounded(.up) } - func minimumCellHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { + private func minimumCellHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { let size = avatarSize(for: message) let avatarHeightPlusBottomPadding = size.height == 0 ? 0 : size.height + avatarBottomPadding @@ -263,7 +264,7 @@ extension MessagesCollectionViewFlowLayout { } - func messageContainerHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { + private func messageContainerHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { let availableWidth = availableWidthForMessageContainer(considering: message) let verticalMessageInsets = messageLabelInsets.top + messageLabelInsets.bottom @@ -284,7 +285,7 @@ extension MessagesCollectionViewFlowLayout { // MARK: - View Width Calculations - func messageContainerWidth(for message: MessageType, at indexPath: IndexPath) -> CGFloat { + private func messageContainerWidth(for message: MessageType, at indexPath: IndexPath) -> CGFloat { let containerHeight = messageContainerHeight(for: message, at: indexPath) let availableWidth = availableWidthForMessageContainer(considering: message) @@ -308,7 +309,7 @@ extension MessagesCollectionViewFlowLayout { // MARK: - Cell Size Calculations - func estimatedCellHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { + private func estimatedCellHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { let containerHeight = messageContainerHeight(for: message, at: indexPath) let topLabelHeight = cellTopLabelHeight(for: message, at: indexPath) @@ -318,19 +319,19 @@ extension MessagesCollectionViewFlowLayout { } - func sizeForItem(at indexPath: IndexPath) -> CGSize { - + open func sizeForItem(at indexPath: IndexPath) -> CGSize { + guard let messagesCollectionView = messagesCollectionView else { return .zero } guard let dataSource = messagesCollectionView.messagesDataSource else { return .zero } - + let message = dataSource.messageForItem(at: indexPath, in: messagesCollectionView) - + let minimumHeight = minimumCellHeight(for: message, at: indexPath) let estimatedHeight = estimatedCellHeight(for: message, at: indexPath) let finalHeight = estimatedHeight < minimumHeight ? minimumHeight : estimatedHeight - + return CGSize(width: itemWidth, height: finalHeight) - + } - + } diff --git a/Sources/MessagesCollectionViewLayoutAttributes.swift b/Sources/MessagesCollectionViewLayoutAttributes.swift index f7b2628c..462992f7 100644 --- a/Sources/MessagesCollectionViewLayoutAttributes.swift +++ b/Sources/MessagesCollectionViewLayoutAttributes.swift @@ -35,11 +35,11 @@ final class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttrib var cellTopLabelSize: CGSize = .zero var cellTopLabelInsets: UIEdgeInsets = .zero - var topLabelPinnedUnderMessage = true + var topLabelExtendsPastAvatar = false var cellBottomLabelSize: CGSize = .zero var cellBottomLabelInsets: UIEdgeInsets = .zero - var bottomLabelPinnedUnderMessage = true + var bottomLabelExtendsPastAvatar = false var avatarSize: CGSize = CGSize(width: 30, height: 30) var avatarBottomPadding: CGFloat = 4.0 @@ -58,10 +58,10 @@ final class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttrib copy.messageToViewEdgePadding = messageToViewEdgePadding copy.cellTopLabelSize = cellTopLabelSize copy.cellTopLabelInsets = cellTopLabelInsets - copy.topLabelPinnedUnderMessage = topLabelPinnedUnderMessage + copy.topLabelExtendsPastAvatar = topLabelExtendsPastAvatar copy.cellBottomLabelSize = cellBottomLabelSize copy.cellBottomLabelInsets = cellBottomLabelInsets - copy.bottomLabelPinnedUnderMessage = bottomLabelPinnedUnderMessage + copy.bottomLabelExtendsPastAvatar = bottomLabelExtendsPastAvatar copy.avatarSize = avatarSize copy.avatarBottomPadding = avatarBottomPadding copy.avatarMessagePadding = avatarMessagePadding diff --git a/Sources/MessagesDisplayDataSource.swift b/Sources/MessagesDisplayDataSource.swift index cdc33650..cafc2431 100644 --- a/Sources/MessagesDisplayDataSource.swift +++ b/Sources/MessagesDisplayDataSource.swift @@ -26,13 +26,13 @@ import Foundation public protocol MessagesDisplayDataSource: class, MessagesDataSource { - func messageColorFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor + func messageColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor - func avatarForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar + func avatar(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar - func headerForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageHeaderView? + func messageHeaderView(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageHeaderView? - func footerForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageFooterView? + func messageFooterView(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageFooterView? func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? @@ -42,15 +42,15 @@ public protocol MessagesDisplayDataSource: class, MessagesDataSource { public extension MessagesDisplayDataSource { - func messageColorFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor { + func messageColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor { return isFromCurrentSender(message: message) ? .outgoingGreen : .incomingGray } - func headerForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageHeaderView? { + func messageHeaderView(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageHeaderView? { return nil } - func footerForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageFooterView? { + func messageFooterView(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageFooterView? { return nil } diff --git a/Sources/MessagesLayoutDelegate.swift b/Sources/MessagesLayoutDelegate.swift index 4ee3753a..b165f19e 100644 --- a/Sources/MessagesLayoutDelegate.swift +++ b/Sources/MessagesLayoutDelegate.swift @@ -26,19 +26,19 @@ import Foundation public protocol MessagesLayoutDelegate: class { - func headerSizeFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize + func headerViewSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize - func footerSizeFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize + func footerViewSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize } extension MessagesLayoutDelegate { - func headerSizeFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize { + func headerViewSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize { return .zero } - func footerSizeFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize { + func footerViewSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize { return .zero } diff --git a/Sources/MessagesViewController.swift b/Sources/MessagesViewController.swift index 5ad5c1ad..1d0eda26 100644 --- a/Sources/MessagesViewController.swift +++ b/Sources/MessagesViewController.swift @@ -165,8 +165,8 @@ extension MessagesViewController: UICollectionViewDataSource { guard let displayDataSource = messagesDataSource as? MessagesDisplayDataSource else { return cell } let message = displayDataSource.messageForItem(at: indexPath, in: messagesCollectionView) - let messageColor = displayDataSource.messageColorFor(message, at: indexPath, in: messagesCollectionView) - let avatar = displayDataSource.avatarForMessage(message, at: indexPath, in: messagesCollectionView) + let messageColor = displayDataSource.messageColor(for: message, at: indexPath, in: messagesCollectionView) + let avatar = displayDataSource.avatar(for: message, at: indexPath, in: messagesCollectionView) let topLabelText = displayDataSource.cellTopLabelAttributedText(for: message, at: indexPath) let bottomLabelText = displayDataSource.cellBottomLabelAttributedText(for: message, at: indexPath) @@ -189,9 +189,9 @@ extension MessagesViewController: UICollectionViewDataSource { switch kind { case UICollectionElementKindSectionHeader: - return displayDataSource.headerForMessage(message, at: indexPath, in: messagesCollectionView) ?? MessageHeaderView() + return displayDataSource.messageHeaderView(for: message, at: indexPath, in: messagesCollectionView) ?? MessageHeaderView() case UICollectionElementKindSectionFooter: - return displayDataSource.footerForMessage(message, at: indexPath, in: messagesCollectionView) ?? MessageFooterView() + return displayDataSource.messageFooterView(for: message, at: indexPath, in: messagesCollectionView) ?? MessageFooterView() default: fatalError("Unrecognized element of kind: \(kind)") } @@ -205,7 +205,7 @@ extension MessagesViewController: UICollectionViewDataSource { // Could pose a problem if subclass behaviors allows more than one item per section let indexPath = IndexPath(item: 0, section: section) let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView) - return messagesLayoutDelegate.headerSizeFor(message, at: indexPath, in: messagesCollectionView) + return messagesLayoutDelegate.headerViewSize(for: message, at: indexPath, in: messagesCollectionView) } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { @@ -215,7 +215,7 @@ extension MessagesViewController: UICollectionViewDataSource { // Could pose a problem if subclass behaviors allows more than one item per section let indexPath = IndexPath(item: 0, section: section) let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView) - return messagesLayoutDelegate.footerSizeFor(message, at: indexPath, in: messagesCollectionView) + return messagesLayoutDelegate.footerViewSize(for: message, at: indexPath, in: messagesCollectionView) } }