From d3423639f326eb36151729a90089eb811efd4424 Mon Sep 17 00:00:00 2001 From: Frederic Barthelemy Date: Tue, 3 Oct 2017 18:21:46 -0700 Subject: [PATCH] Simplify Cell/Header/Footer Class Registration Also simplify / remove boilerplate from dequeueing reusable cells. --- MessageKit.xcodeproj/project.pbxproj | 4 +++ Sources/LocationMessageCell.swift | 1 + Sources/MediaMessageCell.swift | 1 + Sources/MessageCollectionViewCell.swift | 3 +- Sources/MessageDateHeaderView.swift | 1 + Sources/MessageFooterView.swift | 3 +- Sources/MessageHeaderView.swift | 5 ++- Sources/MessagesViewController.swift | 37 ++++++----------------- Sources/TextMessageCell.swift | 1 + Sources/UICollectionView+Extensions.swift | 36 ++++++++++++++++++++++ 10 files changed, 59 insertions(+), 33 deletions(-) create mode 100644 Sources/UICollectionView+Extensions.swift diff --git a/MessageKit.xcodeproj/project.pbxproj b/MessageKit.xcodeproj/project.pbxproj index e7bd42fa..ef4c109d 100644 --- a/MessageKit.xcodeproj/project.pbxproj +++ b/MessageKit.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 171D5AB91F36712B0053DF69 /* InputTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171D5AB81F36712B0053DF69 /* InputTextView.swift */; }; + 2EB618EF1F8462CA007FBA0E /* UICollectionView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB618EE1F8462CA007FBA0E /* UICollectionView+Extensions.swift */; }; 372F6AEB1F36C15600B57FBD /* AvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372F6AEA1F36C15600B57FBD /* AvatarView.swift */; }; 372F6AEF1F36C61000B57FBD /* AvatarViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372F6AEE1F36C61000B57FBD /* AvatarViewTests.swift */; }; 376AD1821F4258D80083072A /* TestMessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376AD1811F4258D80083072A /* TestMessageModel.swift */; }; @@ -74,6 +75,7 @@ /* Begin PBXFileReference section */ 171D5AB81F36712B0053DF69 /* InputTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputTextView.swift; sourceTree = ""; }; + 2EB618EE1F8462CA007FBA0E /* UICollectionView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Extensions.swift"; sourceTree = ""; }; 372F6AEA1F36C15600B57FBD /* AvatarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarView.swift; sourceTree = ""; }; 372F6AEE1F36C61000B57FBD /* AvatarViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarViewTests.swift; sourceTree = ""; }; 376AD1811F4258D80083072A /* TestMessageModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestMessageModel.swift; sourceTree = ""; }; @@ -218,6 +220,7 @@ children = ( 38C867991F50EA1000811974 /* UIView+Extensions.swift */, B09643851F286C9E004D0129 /* String+Extensions.swift */, + 2EB618EE1F8462CA007FBA0E /* UICollectionView+Extensions.swift */, B096438F1F289142004D0129 /* UIColor+Extensions.swift */, B0AA1F521F44388900BAE583 /* NSAttributedString+Extensions.swift */, B0147C821F5BE9220035B36E /* Bundle+Extensions.swift */, @@ -456,6 +459,7 @@ B09643861F286C9E004D0129 /* String+Extensions.swift in Sources */, B015E81F1F259D8E007EDFB6 /* MessageInputBarDelegate.swift in Sources */, B01280F31F4E8798004BCD3E /* MessageLabelDelegate.swift in Sources */, + 2EB618EF1F8462CA007FBA0E /* UICollectionView+Extensions.swift in Sources */, B0147C981F61AF930035B36E /* LabelAlignment.swift in Sources */, 376AD1881F4259D20083072A /* TestMessagesViewControllerModel.swift in Sources */, B0655A2A1F23D77200542A83 /* Sender.swift in Sources */, diff --git a/Sources/LocationMessageCell.swift b/Sources/LocationMessageCell.swift index 7f449ff8..c8048ba2 100644 --- a/Sources/LocationMessageCell.swift +++ b/Sources/LocationMessageCell.swift @@ -26,6 +26,7 @@ import UIKit import MapKit open class LocationMessageCell: MessageCollectionViewCell { + open override class func reuseIdentifier() -> String { return "messagekit.cell.location" } // MARK: - Properties diff --git a/Sources/MediaMessageCell.swift b/Sources/MediaMessageCell.swift index 001cc1b3..768a65b9 100644 --- a/Sources/MediaMessageCell.swift +++ b/Sources/MediaMessageCell.swift @@ -25,6 +25,7 @@ import UIKit open class MediaMessageCell: MessageCollectionViewCell { + open override class func reuseIdentifier() -> String { return "messagekit.cell.mediamessage" } // MARK: - Properties diff --git a/Sources/MessageCollectionViewCell.swift b/Sources/MessageCollectionViewCell.swift index e7b9c666..0d30ea5a 100644 --- a/Sources/MessageCollectionViewCell.swift +++ b/Sources/MessageCollectionViewCell.swift @@ -24,7 +24,8 @@ import UIKit -open class MessageCollectionViewCell: UICollectionViewCell { +open class MessageCollectionViewCell: UICollectionViewCell, CollectionViewReusable { + open class func reuseIdentifier() -> String { return "messagekit.cell.base-cell" } // MARK: - Properties diff --git a/Sources/MessageDateHeaderView.swift b/Sources/MessageDateHeaderView.swift index 60add5f4..e1229ffd 100644 --- a/Sources/MessageDateHeaderView.swift +++ b/Sources/MessageDateHeaderView.swift @@ -25,6 +25,7 @@ import UIKit open class MessageDateHeaderView: MessageHeaderView { + open override class func reuseIdentifier() -> String { return "messagekit.header.date" } // MARK: - Properties diff --git a/Sources/MessageFooterView.swift b/Sources/MessageFooterView.swift index ac6f8556..72a46108 100644 --- a/Sources/MessageFooterView.swift +++ b/Sources/MessageFooterView.swift @@ -24,7 +24,8 @@ import UIKit -open class MessageFooterView: UICollectionReusableView { +open class MessageFooterView: UICollectionReusableView, CollectionViewReusable { + open class func reuseIdentifier() -> String { return "messagekit.footer.base" } // MARK: - Initializers diff --git a/Sources/MessageHeaderView.swift b/Sources/MessageHeaderView.swift index 406d3f97..18b7d595 100644 --- a/Sources/MessageHeaderView.swift +++ b/Sources/MessageHeaderView.swift @@ -24,12 +24,11 @@ import UIKit -open class MessageHeaderView: UICollectionReusableView { +open class MessageHeaderView: UICollectionReusableView, CollectionViewReusable { + open class func reuseIdentifier() -> String { return "messagekit.header.base" } // MARK: - Properties - static let identifier = "MessageHeaderView" - public override init(frame: CGRect) { super.init(frame: frame) } diff --git a/Sources/MessagesViewController.swift b/Sources/MessagesViewController.swift index da2d6177..fb95b72e 100644 --- a/Sources/MessagesViewController.swift +++ b/Sources/MessagesViewController.swift @@ -107,26 +107,13 @@ open class MessagesViewController: UIViewController { private func registerReusableViews() { - messagesCollectionView.register(TextMessageCell.self, - forCellWithReuseIdentifier: "TextMessageCell") + messagesCollectionView.register(TextMessageCell.self) + messagesCollectionView.register(MediaMessageCell.self) + messagesCollectionView.register(LocationMessageCell.self) - messagesCollectionView.register(MediaMessageCell.self, - forCellWithReuseIdentifier: "MediaMessageCell") - - messagesCollectionView.register(LocationMessageCell.self, - forCellWithReuseIdentifier: "LocationMessageCell") - - messagesCollectionView.register(MessageFooterView.self, - forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, - withReuseIdentifier: "MessageFooterView") - - messagesCollectionView.register(MessageHeaderView.self, - forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, - withReuseIdentifier: "MessageHeaderView") - - messagesCollectionView.register(MessageDateHeaderView.self, - forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, - withReuseIdentifier: "MessageDateHeaderView") + messagesCollectionView.register(MessageFooterView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter) + messagesCollectionView.register(MessageHeaderView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader) + messagesCollectionView.register(MessageDateHeaderView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader) } @@ -186,21 +173,15 @@ extension MessagesViewController: UICollectionViewDataSource { switch message.data { case .text, .attributedText: - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TextMessageCell", for: indexPath) as? TextMessageCell else { - fatalError("Unable to dequeue TextMessageCell") - } + let cell = collectionView.dequeueReusableCell(TextMessageCell.self, for: indexPath) cell.configure(with: message, at: indexPath, and: messagesCollectionView) return cell case .photo, .video: - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MediaMessageCell", for: indexPath) as? MediaMessageCell else { - fatalError("Unable to dequeue MediaMessageCell") - } + let cell = collectionView.dequeueReusableCell(MediaMessageCell.self, for: indexPath) cell.configure(with: message, at: indexPath, and: messagesCollectionView) return cell case .location: - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LocationMessageCell", for: indexPath) as? LocationMessageCell else { - fatalError("Unable to dequeue LocationMessageCell") - } + let cell = collectionView.dequeueReusableCell(LocationMessageCell.self, for: indexPath) cell.configure(with: message, at: indexPath, and: messagesCollectionView) return cell } diff --git a/Sources/TextMessageCell.swift b/Sources/TextMessageCell.swift index 2c0c64e0..7a21cbc5 100644 --- a/Sources/TextMessageCell.swift +++ b/Sources/TextMessageCell.swift @@ -25,6 +25,7 @@ import UIKit open class TextMessageCell: MessageCollectionViewCell { + open override class func reuseIdentifier() -> String { return "messagekit.cell.text" } // MARK: - Properties diff --git a/Sources/UICollectionView+Extensions.swift b/Sources/UICollectionView+Extensions.swift new file mode 100644 index 00000000..410a9a84 --- /dev/null +++ b/Sources/UICollectionView+Extensions.swift @@ -0,0 +1,36 @@ +// +// UICollectionView+Extensions.swift +// MessageKit +// +// Created by Frederic Barthelemy on 10/3/17. +// Copyright © 2017 MessageKit. All rights reserved. +// + +import Foundation + +/// Optional Cell Protocol to Simplify registration/cell type loading in a generic way +protocol CollectionViewReusable: class { + static func reuseIdentifier() -> String +} + +extension UICollectionView { + /// Registers a particular cell using its reuse-identifier + func register(_ cellClass: CellType.Type) { + register(cellClass, forCellWithReuseIdentifier: CellType.reuseIdentifier()) + } + + /// Registers a reusable view for a specific SectionKind + func register(_ headerFooterClass: ViewType.Type, forSupplementaryViewOfKind kind: String) { + register(headerFooterClass, + forSupplementaryViewOfKind: kind, + withReuseIdentifier: ViewType.reuseIdentifier()) + } + + /// Generically dequeues a cell of the correct type allowing you to avoid scattering your code with guard-let-else-fatal + func dequeueReusableCell(_ cellClass: CellType.Type, for indexPath: IndexPath) -> CellType { + guard let cell = dequeueReusableCell(withReuseIdentifier: cellClass.reuseIdentifier(), for: indexPath) as? CellType else { + fatalError("Unable to dequeue \(String(describing: cellClass)) with reuseId of \(cellClass.reuseIdentifier())") + } + return cell + } +}