UITableView based component designed to display a hierarchy of expandable/foldable comments.
Just copy the .swift
files from the SwiftyComments/Classes
folder in your project.
Add the following line to your Podfile.
For Swift 5:
pod 'SwiftyComments', :git => 'https://github.com/tsucres/SwiftyComments.git'
For swift 4:
pod 'SwiftyComments', :git => 'https://github.com/tsucres/SwiftyComments.git', :branch => 'swift4'
Basically, all you have to do is
- Import
import SwiftyComments
- define a view for your comments (typically, a view containing the poster's name, a datetime, the content of the comment, ...). This view will be the main subview of each
CommentCell
(subclass ofUITableViewCell
); - customize the appearance of the tableView through the available properties of the
CommentCell
class; - feed your models to the
CommentsViewController
.
Here are more details about the previous steps:
A CommentCell
is basically 1 UIView showing your comment + an indentation. Here's a basic implementation:
class YourCommentCell: CommentCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// YourCommentView is a UIView aimed to contain all the info about a comment (poster's name, date, body, ...)
self.commentViewContent = YourCommentView()
// Customisation...
// ...
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The surroundings of your comment view (such as the indentation, the margins) are customizable through several properties of the CommentCell
class :
// Margin on top of a root comment
self.rootCommentMarginColor = ...
self.rootCommentMargin = ...
// Margin on top of any non-root comment
self.commentMarginColor = ...
self.commentMargin = ...
// Space on the left of any indented comment
self.identationColor = ...
// Vertical line between the indentation space and the comment
self.indentationIndicatorColor = ...
self.indentationIndicatorThickness = ...
// Whether to continue the indentyation indicator on all the child comments or not
self.extendIndentationIndicators = ...
// thickness of the indentation offset per level
self.indentationUnit = ...
The CommentsViewController
class inherits from UITableViewController
and takes care of loading your cells. All you have to do is fill the allComments
property with your models and override the tableView(_:cellForRowAt:)
method:
class YourCommentsViewController: CommentsViewController {
private let commentCellId = "commentCellId"
var allComments: [YourCommentModel] = [] // All the comments (nested, not in a linear format)
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(SimpleCommentCell.self, forCellReuseIdentifier: commentCellId)
currentlyDisplayed = allComments // Assuming your models are loaded in allComments
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> CommentCell {
let commentCell = tableView.dequeueReusableCell(withIdentifier: commentCellId, for: indexPath) as! YourCommentCell
let comment = currentlyDisplayed[indexPath.row] as! YourCommentModel
commentCell.level = comment.level
// commentCell.body = ...
return commentCell
}
}
Note that your model must satisfy the AbstractComment
protocol:
public protocol AbstractComment: class {
var replies: [AbstractComment]! { get set }
var level: Int! { get set }
weak var replyTo: AbstractComment? { get set }
}
By default, when a comment is expanded, the tableView will scroll to make the replies visible. You can turn this feature off throught the property makeExpandedCellsVisible
.
Additionally, you can start with a fully-expanded tableView by setting the fullyExpanded
property to true
.
The following image shows the structure of the tableView:
1
shows the wholeCommentCell
2
is theUIView
assigned to thecommentViewContent
property of the cell3
is the indentation space, which you can personalize through theindentationUnit
andidentationColor
properties of theCommentCell
- The color and the thickness of
4
are personalizable through the propertiescommentMarginColor
andcommentMargin
of theCommentCell
5
is similarly modifiable throughindentationIndicatorColor
andindentationIndicatorThickness
- same for
6
withrootCommentMarginColor
androotCommentMargin
You can easily add special styles (titles, links, paragraph spacing, bold/italic font for some words, ..) in your commentContentView using a UITextView
with a NSAttributedString
.
Though, if your comment contents are in html format you may want to reconsider your eventual plan of using the DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html
option. Even if this way is really simple and straightforward, it's not efficient and takes a long time (.15 - .2 s in my experience) to load the string. Since the height of the cells are automatically calculated based on their content, that delay leads to glitches/lagging.
An alternative (illustrated in the HNCommentsViewController
example) is to parse the html yourself and build the NSAttributedString by adding the attributes "manually".
This may be more difficult to implement depending on how complex the format of your comments is, but it's waaaay more efficient.
If you don't trust me, go in the HNCommentsViewController.swift
and uncomment the part that loads the comments the easy way...
The example makes use of SwiftScanner to parse the html of the hackernews comments. Check the HNCommentContentParser.swift
file for more details on how the AttributedStrings are built.
public protocol CommentsViewDelegate: class {
func commentCellExpanded(atIndex index: Int)
func commentCellFolded(atIndex index: Int)
}
You can implement this protocol and correctly set the delegate
attribute of CommentsViewController
to be notified when a cell is expanded/folded.
You can use this to, for example, minimize a comment when it has been folded. Check out the RedditCommentsViewController
example for more details.
SwipeCellKit is used to add a "Swipe to Hide" action on each comment cell.
This functionality can be disabled using the swipeToHide
property of the CommentsViewController
.
The appearance of the SwipeAction is Customizable through the swipeActionAppearance
property of the CommentsViewController
. This way you can change all the following properties of the SwipeActions:
open var swipeActionColor: UIColor
open var swipeActionHighlightedColor: UIColor
open var swipeActionText: String
open var swipeActionTextColor: UIColor
open var swipeActionHighlightedTextColor: UIColor
open var swipeActionTextFont: UIFont
open var swipeActionImage: UIImage
open var swipeActionHighlightedImage: UIImage
For more information, reefer to the documentation of SwipeCellKit.
You can push the customization further by (carefully) overriding the following method:
public func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]?
To add actions, you have to override the editActionsForRowAt
method:
override public func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
// Retrieve the "collapseAction"
var actions = super.tableView(tableView, editActionsForRowAt: indexPath, for: orientation)
// Create you additional action(s) according to the documentation of SwipeCellKit
let oneMoreAction = SwipeAction(...)
actions.append(oneMoreAction)
return actions
}
SwipeCellKit was preferred over the default UITableViewRowAction introduced in iOS 8. The main reason for this is that the UITableViewRowAction doesn't handle well the resize animation of the cells: as the size of the cell is reduced (when it folds), the UITableViewRowAction keeps it initial size and overlaps the other cells. Another reason is the better customizability of SwipeCellKit.
To run the example project, clone the repo, and run pod install
from the Example directory.
There are four different implementations of the CommentsViewController
in this project:
SimpleCommentsViewController
illustrates a basic implementation;ImgurCommentsViewController
is a clone of the commenting system on the iOS app of Imgur;RedditCommentsViewController
is a clone of the commenting system on the iOS app of Reddit;HNCommentsViewController
illustrates the use of AttributedStrings.
- More abstraction on top of the tableview:
- getting rid of the
init(style: UITableViewCellStyle, reuseIdentifier: String?)
- override the
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> CommentCell
- getting rid of the
- The animation for adding/deleting rows in iOS 11 is a bit weird.. any solution?
- mark comments as expanded/folded so that, when expanding a comment, the state (expanded/folded) of its replies is maintained. Currently, when a comment having folded replies is folded and then unfolded, its replies are being unfolded too.
- Better documentation/README
SwiftyComments is available under the MIT license. See the LICENSE file for more info.