diff --git a/.swiftlint.yml b/.swiftlint.yml index edf246970..c94593c56 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,6 +1,5 @@ -line_length: - warning: 150 - ignores_comments: true disabled_rules: - identifier_name +- identifier_name +- trailing_whitespace +- line_length diff --git a/Example/Sources/ConversationViewController.swift b/Example/Sources/ConversationViewController.swift index af1a34414..d34af97f7 100644 --- a/Example/Sources/ConversationViewController.swift +++ b/Example/Sources/ConversationViewController.swift @@ -111,9 +111,9 @@ extension ConversationViewController: MessagesDataSource { extension ConversationViewController: MessagesDisplayDataSource { - func avatarForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar { + func avatarForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> AvatarView { let image = isFromCurrentSender(message: message) ? #imageLiteral(resourceName: "Steve-Jobs") : #imageLiteral(resourceName: "Tim-Cook") - return Avatar(placeholderImage: image) + return AvatarView(image: image) } func headerForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageHeaderView? { diff --git a/MessageKit.xcodeproj/project.pbxproj b/MessageKit.xcodeproj/project.pbxproj index 5fd8f5d0b..3a25e9fc6 100644 --- a/MessageKit.xcodeproj/project.pbxproj +++ b/MessageKit.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 372F6AEB1F36C15600B57FBD /* AvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372F6AEA1F36C15600B57FBD /* AvatarView.swift */; }; + 372F6AEF1F36C61000B57FBD /* AvatarViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372F6AEE1F36C61000B57FBD /* AvatarViewTests.swift */; }; 882D75841DE507320033F95F /* MessagesDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882D75831DE507320033F95F /* MessagesDataSource.swift */; }; 888CEBFC1D3FD525005178DE /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 888CEBFB1D3FD525005178DE /* MessagesViewController.swift */; }; 88916B2D1CF0DF2F00469F91 /* MessageKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88916B221CF0DF2F00469F91 /* MessageKit.framework */; }; @@ -16,7 +18,6 @@ B015E8191F24623D007EDFB6 /* MessagesCollectionViewLayoutAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B015E8181F24623D007EDFB6 /* MessagesCollectionViewLayoutAttributes.swift */; }; B015E81F1F259D8E007EDFB6 /* MessageInputBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B015E81E1F259D8E007EDFB6 /* MessageInputBarDelegate.swift */; }; B03FF9AF1F31BB1200754FE5 /* MessageCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03FF9AE1F31BB1200754FE5 /* MessageCellDelegate.swift */; }; - B0655A261F23D6C500542A83 /* Avatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A251F23D6C500542A83 /* Avatar.swift */; }; B0655A281F23D71400542A83 /* MessageDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A271F23D71400542A83 /* MessageDirection.swift */; }; B0655A2A1F23D77200542A83 /* Sender.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A291F23D77200542A83 /* Sender.swift */; }; B0655A2C1F23D81600542A83 /* MessageData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A2B1F23D81600542A83 /* MessageData.swift */; }; @@ -43,6 +44,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 372F6AEA1F36C15600B57FBD /* AvatarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarView.swift; sourceTree = ""; }; + 372F6AED1F36C1C100B57FBD /* AvatarView.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = AvatarView.playground; sourceTree = ""; }; + 372F6AEE1F36C61000B57FBD /* AvatarViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarViewTests.swift; sourceTree = ""; }; 882D75831DE507320033F95F /* MessagesDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesDataSource.swift; sourceTree = ""; }; 888CEBFB1D3FD525005178DE /* MessagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; 88916B221CF0DF2F00469F91 /* MessageKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MessageKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -55,7 +59,6 @@ B015E8181F24623D007EDFB6 /* MessagesCollectionViewLayoutAttributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesCollectionViewLayoutAttributes.swift; sourceTree = ""; }; B015E81E1F259D8E007EDFB6 /* MessageInputBarDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageInputBarDelegate.swift; sourceTree = ""; }; B03FF9AE1F31BB1200754FE5 /* MessageCellDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCellDelegate.swift; sourceTree = ""; }; - B0655A251F23D6C500542A83 /* Avatar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Avatar.swift; sourceTree = ""; }; B0655A271F23D71400542A83 /* MessageDirection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDirection.swift; sourceTree = ""; }; B0655A291F23D77200542A83 /* Sender.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sender.swift; sourceTree = ""; }; B0655A2B1F23D81600542A83 /* MessageData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageData.swift; sourceTree = ""; }; @@ -127,6 +130,7 @@ children = ( 88916B421CF0DF5900469F91 /* Info.plist */, 88916B431CF0DF5900469F91 /* MessageKitTests.swift */, + 372F6AEE1F36C61000B57FBD /* AvatarViewTests.swift */, ); path = Tests; sourceTree = SOURCE_ROOT; @@ -143,7 +147,6 @@ B09643991F295D58004D0129 /* Models */ = { isa = PBXGroup; children = ( - B0655A251F23D6C500542A83 /* Avatar.swift */, B0655A291F23D77200542A83 /* Sender.swift */, B0655A2B1F23D81600542A83 /* MessageData.swift */, B0655A271F23D71400542A83 /* MessageDirection.swift */, @@ -156,6 +159,8 @@ children = ( B0655A2D1F23D8BC00542A83 /* MessagesCollectionView.swift */, B0655A4C1F244C0600542A83 /* MessageCollectionViewCell.swift */, + 372F6AEA1F36C15600B57FBD /* AvatarView.swift */, + 372F6AED1F36C1C100B57FBD /* AvatarView.playground */, B0655A371F23EE8B00542A83 /* MessageInputBar.swift */, B074EE921F35587100ABB8C8 /* MessageHeaderView.swift */, B074EE941F35588A00ABB8C8 /* MessageFooterView.swift */, @@ -335,7 +340,6 @@ B0655A2C1F23D81600542A83 /* MessageData.swift in Sources */, B09643861F286C9E004D0129 /* String+Extensions.swift in Sources */, B0655A281F23D71400542A83 /* MessageDirection.swift in Sources */, - B0655A261F23D6C500542A83 /* Avatar.swift in Sources */, B015E81F1F259D8E007EDFB6 /* MessageInputBarDelegate.swift in Sources */, B0655A2A1F23D77200542A83 /* Sender.swift in Sources */, B074EE931F35587100ABB8C8 /* MessageHeaderView.swift in Sources */, @@ -344,6 +348,7 @@ B074EE951F35588A00ABB8C8 /* MessageFooterView.swift in Sources */, B09643901F289142004D0129 /* UIColor+Extensions.swift in Sources */, B0655A381F23EE8B00542A83 /* MessageInputBar.swift in Sources */, + 372F6AEB1F36C15600B57FBD /* AvatarView.swift in Sources */, 88916B471CF0DFE600469F91 /* MessageType.swift in Sources */, B03FF9AF1F31BB1200754FE5 /* MessageCellDelegate.swift in Sources */, B096438E1F2890FB004D0129 /* MessagesDisplayDataSource.swift in Sources */, @@ -356,6 +361,7 @@ buildActionMask = 2147483647; files = ( 88916B451CF0DF5900469F91 /* MessageKitTests.swift in Sources */, + 372F6AEF1F36C61000B57FBD /* AvatarViewTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/Avatar.swift b/Sources/Avatar.swift deleted file mode 100644 index ecc8eaf6b..000000000 --- a/Sources/Avatar.swift +++ /dev/null @@ -1,51 +0,0 @@ -/* - 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? - - public let highlightedImage: UIImage? - - public let placeholderImage: UIImage - - public init(image: UIImage? = nil, highlightedImage: UIImage? = nil, placeholderImage: UIImage) { - self.image = image - self.highlightedImage = highlightedImage - self.placeholderImage = placeholderImage - } - - public func image(highlighted: Bool) -> UIImage { - - switch highlighted { - case true: - return highlightedImage ?? image ?? placeholderImage - case false: - return image ?? placeholderImage - } - - } -} diff --git a/Sources/AvatarView.playground/Contents.swift b/Sources/AvatarView.playground/Contents.swift new file mode 100644 index 000000000..e5e0b2d83 --- /dev/null +++ b/Sources/AvatarView.playground/Contents.swift @@ -0,0 +1,37 @@ +import UIKit +import MessageKit +import PlaygroundSupport + +//: Discover what is possible with the Avatar Class +//Get an image +let testImage = #imageLiteral(resourceName: "NiceSelfi.jpg") + +let view = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 200)) + +view.backgroundColor = UIColor.white + +//: Uncomment any line to see how it changes the `Avatar`. +//: By default its a circlular avatar with a gray background and initals of ? +let avatar = AvatarView() + +//: Configure any one of the initilization parameters and delete the ones you dont want to set. +//let avatar = AvatarView(size: 50, image: testImage, highlightedImage: testImage, initals: "PL", cornerRounding: 9) + +//: Throw in just an image. +//let avatar = AvatarView(image: testImage) + +//: Dont have an image just add the users initals +//let avatar = AvatarView(initals: "PL") + +//: Want rounded squares instead of circles just change the `cornderRounding`. +//let avatar = AvatarView(image: testImage, cornerRounding: 9) + +//:Change its size +//let avatar = AvatarView(size: 5) + +//let avatar = AvatarView(size: 100) + +//: Everything has a default so if you dont want to set it then you dont have to. + +//Helper method. +PlaygroundPage.current.liveView = avatar diff --git a/Sources/AvatarView.playground/Resources/NiceSelfi.jpg b/Sources/AvatarView.playground/Resources/NiceSelfi.jpg new file mode 100644 index 000000000..bec3cfeca Binary files /dev/null and b/Sources/AvatarView.playground/Resources/NiceSelfi.jpg differ diff --git a/Sources/AvatarView.playground/contents.xcplayground b/Sources/AvatarView.playground/contents.xcplayground new file mode 100644 index 000000000..5da2641c9 --- /dev/null +++ b/Sources/AvatarView.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Sources/AvatarView.swift b/Sources/AvatarView.swift new file mode 100644 index 000000000..79c41c964 --- /dev/null +++ b/Sources/AvatarView.swift @@ -0,0 +1,115 @@ +/* + 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 + +open class AvatarView: UIView { + // MARK: - Properties + internal var initalsLabel = UILabel() + internal var imageView = UIImageView() + internal var initals: String = "?" + + // MARK: - initializers + override init(frame: CGRect) { + super.init(frame: frame) + prepareView() + } + + convenience public init(size: CGFloat = 30, image: UIImage? = nil, highlightedImage: UIImage? = nil, initals inInitals: String = "?", cornerRounding: CGFloat? = nil) { + let frame = CGRect(x: 0, y: 0, width: size, height: size) + self.init(frame: frame) + setCorner(radius: cornerRounding) + setBackground(color: UIColor.gray) + imageView.image = image + imageView.highlightedImage = highlightedImage + initals = inInitals + prepareView() + } + + convenience public init() { + let frame = CGRect(x: 0, y: 0, width: 30, height: 30) + self.init(frame: frame) + setBackground(color: UIColor.gray) + setCorner(radius: nil) + prepareView() + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - internal methods + + internal func prepareView() { + prepareInitalsLabel() + prepareImageView() + imageView.isHidden = imageView.image == nil + } + + internal func prepareInitalsLabel() { + initalsLabel.text = initals + initalsLabel.textAlignment = .center + setInitalsFont() + addSubview(initalsLabel) + initalsLabel.center = center + initalsLabel.frame = frame + } + + internal func prepareImageView() { + contentMode = .scaleAspectFill + layer.masksToBounds = true + clipsToBounds = true + addSubview(imageView) + imageView.contentMode = .scaleAspectFill + imageView.frame = frame + } + + // MARK: - Open methods + + open func set(image: UIImage) { + imageView.image = image + } + + open func setInitalsFont(size: CGFloat = 16, color: UIColor = .white) { + initalsLabel.font = UIFont.systemFont(ofSize: size) + initalsLabel.textColor = color + } + + open func setBackground(color: UIColor) { + backgroundColor = color + } + + open func getImage() -> UIImage? { + return imageView.image + } + + open func setCorner(radius: CGFloat?) { + guard let radius = radius else { + //if corner radius not set default to Circle + layer.cornerRadius = frame.height/2 + return + } + layer.cornerRadius = radius + } +} diff --git a/Sources/MessagesDisplayDataSource.swift b/Sources/MessagesDisplayDataSource.swift index 35c92b825..067df3b6d 100644 --- a/Sources/MessagesDisplayDataSource.swift +++ b/Sources/MessagesDisplayDataSource.swift @@ -28,7 +28,7 @@ public protocol MessagesDisplayDataSource: class, MessagesDataSource { func messageColorFor(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor - func avatarForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar + func avatarForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> AvatarView func headerForMessage(_ message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageHeaderView? diff --git a/Sources/MessagesViewController.swift b/Sources/MessagesViewController.swift index 1105322ed..6e4ea2df3 100644 --- a/Sources/MessagesViewController.swift +++ b/Sources/MessagesViewController.swift @@ -172,9 +172,8 @@ extension MessagesViewController: UICollectionViewDataSource { 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) - - cell.avatarImageView.image = avatar.image(highlighted: false) - cell.avatarImageView.highlightedImage = avatar.image(highlighted: true) + //TODO: replace this completely + cell.avatarImageView.image = avatar.getImage() cell.messageContainerView.backgroundColor = messageColor cell.configure(with: message) diff --git a/Tests/AvatarViewTests.swift b/Tests/AvatarViewTests.swift new file mode 100644 index 000000000..1387f5e4a --- /dev/null +++ b/Tests/AvatarViewTests.swift @@ -0,0 +1,80 @@ +// +// AvatarViewTests.swift +// MessageKit +// +// Created by Dan Leonard on 8/5/17. +// Copyright © 2017 Hexed Bits. All rights reserved. +// + +import XCTest +@testable import MessageKit + +class AvatarViewTests: XCTestCase { + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testNoParams() { + let avatar = AvatarView() + XCTAssertEqual(avatar.initals, "?") + XCTAssertEqual(avatar.initalsLabel.text, "?") + XCTAssertEqual(avatar.layer.cornerRadius, 15.0) + XCTAssertNil(avatar.imageView.image) + XCTAssertTrue(avatar.imageView.isHidden) + XCTAssertEqual(avatar.initalsLabel.textColor, UIColor.white) + } + + func testWithImage() { + let avatar = AvatarView(image: UIImage()) + XCTAssertEqual(avatar.initals, "?") + XCTAssertEqual(avatar.initalsLabel.text, "?") + XCTAssertEqual(avatar.layer.cornerRadius, 15.0) + XCTAssertFalse(avatar.imageView.isHidden) + XCTAssertEqual(avatar.initalsLabel.textColor, UIColor.white) + XCTAssertEqual(avatar.backgroundColor, UIColor.gray) + } + + func testCustom() { + let avatar = AvatarView(size: 50, image: UIImage(), highlightedImage: nil, initals: "lol", cornerRounding: 6) + XCTAssertEqual(avatar.initals, "lol") + XCTAssertEqual(avatar.initalsLabel.text, "lol") + XCTAssertEqual(avatar.layer.cornerRadius, 6.0) + XCTAssertFalse(avatar.imageView.isHidden) + XCTAssertEqual(avatar.initalsLabel.textColor, UIColor.white) + XCTAssertEqual(avatar.backgroundColor, UIColor.gray) + } + + func testSetBackground() { + let avatar = AvatarView(image: UIImage()) + XCTAssertEqual(avatar.backgroundColor, UIColor.gray) + avatar.setBackground(color: UIColor.red) + XCTAssertEqual(avatar.backgroundColor, UIColor.red) + } + + func testGetImage() { + let image = UIImage() + let avatar = AvatarView(image: image) + XCTAssertEqual(avatar.getImage(), image) + } + + func testRoundedCorners() { + let avatar = AvatarView(image: UIImage()) + XCTAssertEqual(avatar.layer.cornerRadius, 15.0) + avatar.setCorner(radius: 2) + XCTAssertEqual(avatar.layer.cornerRadius, 2.0) + } + + func testInitalsFont() { + let avatar = AvatarView(image: UIImage()) + XCTAssertEqual(avatar.initalsLabel.textColor, UIColor.white) + XCTAssertEqual(avatar.initalsLabel.font.pointSize, 16) + avatar.setInitalsFont(size: 20, color: UIColor.blue) + XCTAssertEqual(avatar.initalsLabel.textColor, UIColor.blue) + XCTAssertEqual(avatar.initalsLabel.font.pointSize, 20) + } +}