From 3b94565c48c35ea728c8afe85a6e766dfaa9696e Mon Sep 17 00:00:00 2001 From: Dan Leonard Date: Sat, 5 Aug 2017 23:55:59 -0600 Subject: [PATCH] AvatarView (#31) * Add AvatarView along with `AvatarView.Playground` Implemented code review changes. * Add tests because your are supposed to add tests for everything. * Replace references to the Avatar with AvatarView. Update Example Project. * Updated my `swiftLint` and fixed issues. --- .swiftlint.yml | 7 +- .../Sources/ConversationViewController.swift | 4 +- MessageKit.xcodeproj/project.pbxproj | 14 ++- Sources/Avatar.swift | 51 -------- Sources/AvatarView.playground/Contents.swift | 37 ++++++ .../Resources/NiceSelfi.jpg | Bin 0 -> 41110 bytes .../contents.xcplayground | 4 + Sources/AvatarView.swift | 115 ++++++++++++++++++ Sources/MessagesDisplayDataSource.swift | 2 +- Sources/MessagesViewController.swift | 5 +- Tests/AvatarViewTests.swift | 80 ++++++++++++ 11 files changed, 254 insertions(+), 65 deletions(-) delete mode 100644 Sources/Avatar.swift create mode 100644 Sources/AvatarView.playground/Contents.swift create mode 100644 Sources/AvatarView.playground/Resources/NiceSelfi.jpg create mode 100644 Sources/AvatarView.playground/contents.xcplayground create mode 100644 Sources/AvatarView.swift create mode 100644 Tests/AvatarViewTests.swift 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 0000000000000000000000000000000000000000..bec3cfeca48df384efb960dba611610227bedcb6 GIT binary patch literal 41110 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 literal 0 HcmV?d00001 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) + } +}