Implement new timeline layout.
This commit is contained in:
parent
2efbd44811
commit
e37d4ddd2f
|
@ -12,13 +12,13 @@ extension String {
|
|||
|
||||
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
|
||||
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
|
||||
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
|
||||
let boundingBox = self.boundingRect(with: constraintRect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedString.Key.font: font], context: nil)
|
||||
return ceil(boundingBox.height)
|
||||
}
|
||||
|
||||
func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
|
||||
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
|
||||
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
|
||||
let boundingBox = self.boundingRect(with: constraintRect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedString.Key.font: font], context: nil)
|
||||
return ceil(boundingBox.width)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import Articles
|
|||
struct MasterTimelineCellData {
|
||||
|
||||
let title: String
|
||||
let text: String
|
||||
let summary: String
|
||||
let dateString: String
|
||||
let feedName: String
|
||||
let showFeedName: Bool
|
||||
|
@ -25,7 +25,7 @@ struct MasterTimelineCellData {
|
|||
init(article: Article, showFeedName: Bool, feedName: String?, avatar: UIImage?, showAvatar: Bool, featuredImage: UIImage?) {
|
||||
|
||||
self.title = TimelineStringFormatter.truncatedTitle(article)
|
||||
self.text = TimelineStringFormatter.truncatedSummary(article)
|
||||
self.summary = TimelineStringFormatter.truncatedSummary(article)
|
||||
|
||||
self.dateString = TimelineStringFormatter.dateString(article.logicalDatePublished)
|
||||
|
||||
|
@ -48,7 +48,7 @@ struct MasterTimelineCellData {
|
|||
|
||||
init() { //Empty
|
||||
self.title = ""
|
||||
self.text = ""
|
||||
self.summary = ""
|
||||
self.dateString = ""
|
||||
self.feedName = ""
|
||||
self.showFeedName = false
|
||||
|
|
|
@ -11,114 +11,100 @@ import RSCore
|
|||
|
||||
struct MasterTimelineCellLayout {
|
||||
|
||||
static let maxNumberOfLines = 2
|
||||
static let cellPadding = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
|
||||
|
||||
static let feedColor = AppAssets.timelineTextSecondaryColor
|
||||
static let feedNameFont = UIFont.systemFont(ofSize: UIFont.systemFontSize)
|
||||
|
||||
static let dateColor = AppAssets.timelineTextSecondaryColor
|
||||
static let dateFont = UIFont.systemFont(ofSize: UIFont.systemFontSize, weight: UIFont.Weight.bold)
|
||||
static let dateMarginBottom = CGFloat(integerLiteral: 1)
|
||||
|
||||
static let titleColor = AppAssets.timelineTextPrimaryColor
|
||||
static let titleFont = UIFont.systemFont(ofSize: UIFont.systemFontSize, weight: .semibold)
|
||||
static let titleBottomMargin = CGFloat(integerLiteral: 1)
|
||||
static let titleNumberOfLines = 2
|
||||
|
||||
static let textColor = AppAssets.timelineTextPrimaryColor
|
||||
static let textFont = UIFont.systemFont(ofSize: UIFont.systemFontSize)
|
||||
|
||||
static let textOnlyFont = UIFont.systemFont(ofSize: UIFont.systemFontSize)
|
||||
|
||||
static let unreadCircleMarginLeft = CGFloat(integerLiteral: 8)
|
||||
static let unreadCircleDimension = CGFloat(integerLiteral: 8)
|
||||
static let unreadCircleMarginRight = CGFloat(integerLiteral: 8)
|
||||
|
||||
static let starDimension = CGFloat(integerLiteral: 13)
|
||||
|
||||
static let avatarSize = CGSize(width: 48.0, height: 48.0)
|
||||
static let avatarMarginLeft = CGFloat(integerLiteral: 8)
|
||||
static let avatarMarginRight = CGFloat(integerLiteral: 8)
|
||||
static let avatarCornerRadius = CGFloat(integerLiteral: 4)
|
||||
|
||||
static let titleColor = AppAssets.timelineTextPrimaryColor
|
||||
static let titleFont = UIFont.preferredFont(forTextStyle: .headline)
|
||||
static let titleBottomMargin = CGFloat(integerLiteral: 1)
|
||||
|
||||
|
||||
static let feedColor = AppAssets.timelineTextSecondaryColor
|
||||
static let feedNameFont = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
static let feedRightMargin = CGFloat(integerLiteral: 8)
|
||||
|
||||
static let dateColor = AppAssets.timelineTextSecondaryColor
|
||||
static let dateFont = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
static let dateMarginBottom = CGFloat(integerLiteral: 1)
|
||||
|
||||
static let summaryColor = AppAssets.timelineTextPrimaryColor
|
||||
static let summaryFont = UIFont.preferredFont(forTextStyle: .body)
|
||||
|
||||
static let chevronWidth = CGFloat(integerLiteral: 28)
|
||||
|
||||
let width: CGFloat
|
||||
let insets: UIEdgeInsets
|
||||
|
||||
let height: CGFloat
|
||||
let feedNameRect: CGRect
|
||||
let dateRect: CGRect
|
||||
let titleRect: CGRect
|
||||
let numberOfLinesForTitle: Int
|
||||
let summaryRect: CGRect
|
||||
let textRect: CGRect
|
||||
let unreadIndicatorRect: CGRect
|
||||
let starRect: CGRect
|
||||
let avatarImageRect: CGRect
|
||||
let paddingBottom: CGFloat
|
||||
let titleRect: CGRect
|
||||
let summaryRect: CGRect
|
||||
let feedNameRect: CGRect
|
||||
let dateRect: CGRect
|
||||
|
||||
let separatorInsets: UIEdgeInsets
|
||||
|
||||
init(width: CGFloat, height: CGFloat, feedNameRect: CGRect, dateRect: CGRect, titleRect: CGRect, numberOfLinesForTitle: Int, summaryRect: CGRect, textRect: CGRect, unreadIndicatorRect: CGRect, starRect: CGRect, avatarImageRect: CGRect, paddingBottom: CGFloat, separatorInsets: UIEdgeInsets) {
|
||||
init(width: CGFloat, insets: UIEdgeInsets, cellData: MasterTimelineCellData, showAvatar: Bool) {
|
||||
|
||||
self.width = width - MasterTimelineCellLayout.chevronWidth
|
||||
self.feedNameRect = feedNameRect
|
||||
self.dateRect = dateRect
|
||||
self.titleRect = titleRect
|
||||
self.numberOfLinesForTitle = numberOfLinesForTitle
|
||||
self.summaryRect = summaryRect
|
||||
self.textRect = textRect
|
||||
self.unreadIndicatorRect = unreadIndicatorRect
|
||||
self.starRect = starRect
|
||||
self.avatarImageRect = avatarImageRect
|
||||
self.paddingBottom = paddingBottom
|
||||
self.separatorInsets = separatorInsets
|
||||
self.width = width
|
||||
self.insets = insets
|
||||
|
||||
if height > 0.1 {
|
||||
self.height = height
|
||||
var currentPoint = CGPoint.zero
|
||||
currentPoint.x = MasterTimelineCellLayout.cellPadding.left + insets.left + MasterTimelineCellLayout.unreadCircleMarginLeft
|
||||
currentPoint.y = MasterTimelineCellLayout.cellPadding.top
|
||||
|
||||
// Unread Indicator and Star
|
||||
self.unreadIndicatorRect = MasterTimelineCellLayout.rectForUnreadIndicator(currentPoint)
|
||||
self.starRect = MasterTimelineCellLayout.rectForStar(currentPoint)
|
||||
|
||||
// Start the point at the beginning position of the main block
|
||||
currentPoint.x += MasterTimelineCellLayout.unreadCircleDimension + MasterTimelineCellLayout.unreadCircleMarginRight
|
||||
|
||||
// Separator Insets
|
||||
self.separatorInsets = UIEdgeInsets(top: 0, left: currentPoint.x, bottom: 0, right: 0)
|
||||
|
||||
// Avatar
|
||||
if showAvatar {
|
||||
self.avatarImageRect = MasterTimelineCellLayout.rectForAvatar(currentPoint)
|
||||
currentPoint.x = self.avatarImageRect.maxX + MasterTimelineCellLayout.avatarMarginRight
|
||||
} else {
|
||||
self.height = [feedNameRect, dateRect, titleRect, summaryRect, textRect, unreadIndicatorRect, avatarImageRect].maxY() + paddingBottom
|
||||
self.avatarImageRect = CGRect.zero
|
||||
}
|
||||
|
||||
let textAreaWidth = width - (currentPoint.x + MasterTimelineCellLayout.chevronWidth + MasterTimelineCellLayout.cellPadding.right + insets.right)
|
||||
|
||||
// Title Text Block
|
||||
let (titleRect, numberOfLinesForTitle) = MasterTimelineCellLayout.rectForTitle(cellData, currentPoint, textAreaWidth)
|
||||
self.titleRect = titleRect
|
||||
|
||||
// Summary Text Block
|
||||
if self.titleRect != CGRect.zero {
|
||||
currentPoint.y = self.titleRect.maxY + MasterTimelineCellLayout.titleBottomMargin
|
||||
}
|
||||
self.summaryRect = MasterTimelineCellLayout.rectForSummary(cellData, currentPoint, textAreaWidth, numberOfLinesForTitle)
|
||||
|
||||
init(width: CGFloat, height: CGFloat, cellData: MasterTimelineCellData, hasAvatar: Bool) {
|
||||
currentPoint.y = [self.titleRect, self.summaryRect].maxY()
|
||||
|
||||
let width = width - MasterTimelineCellLayout.chevronWidth
|
||||
// Feed Name and Pub Date
|
||||
self.dateRect = MasterTimelineCellLayout.rectForDate(cellData, currentPoint, textAreaWidth)
|
||||
|
||||
// If height == 0.0, then height is calculated.
|
||||
let feedNameWidth = textAreaWidth - (MasterTimelineCellLayout.feedRightMargin + self.dateRect.size.width)
|
||||
self.feedNameRect = MasterTimelineCellLayout.rectForFeedName(cellData, currentPoint, feedNameWidth)
|
||||
|
||||
let showAvatar = hasAvatar && cellData.showAvatar
|
||||
var textBoxRect = MasterTimelineCellLayout.rectForTextBox(cellData, showAvatar, width)
|
||||
self.height = [self.avatarImageRect, self.feedNameRect].maxY() + MasterTimelineCellLayout.cellPadding.bottom
|
||||
|
||||
let (titleRect, numberOfLinesForTitle) = MasterTimelineCellLayout.rectForTitle(textBoxRect, cellData)
|
||||
let summaryRect = numberOfLinesForTitle > 0 ? MasterTimelineCellLayout.rectForSummary(textBoxRect, titleRect, numberOfLinesForTitle, cellData) : CGRect.zero
|
||||
let textRect = numberOfLinesForTitle > 0 ? CGRect.zero : MasterTimelineCellLayout.rectForText(textBoxRect, cellData)
|
||||
|
||||
var lastTextRect = titleRect
|
||||
if numberOfLinesForTitle == 0 {
|
||||
lastTextRect = textRect
|
||||
} else if numberOfLinesForTitle == 1 {
|
||||
if summaryRect.height > 0.1 {
|
||||
lastTextRect = summaryRect
|
||||
}
|
||||
}
|
||||
|
||||
let dateRect = MasterTimelineCellLayout.rectForDate(textBoxRect, lastTextRect, cellData)
|
||||
let feedNameRect = MasterTimelineCellLayout.rectForFeedName(textBoxRect, dateRect, cellData)
|
||||
|
||||
textBoxRect.size.height = ceil([titleRect, summaryRect, textRect, dateRect, feedNameRect].maxY() - textBoxRect.origin.y)
|
||||
let avatarImageRect = MasterTimelineCellLayout.rectForAvatar(cellData, showAvatar, textBoxRect, width, height)
|
||||
let unreadIndicatorRect = MasterTimelineCellLayout.rectForUnreadIndicator(textBoxRect)
|
||||
let starRect = MasterTimelineCellLayout.rectForStar(unreadIndicatorRect)
|
||||
|
||||
let paddingBottom = MasterTimelineCellLayout.cellPadding.bottom
|
||||
|
||||
let separatorInsets = UIEdgeInsets(top: 0, left: unreadIndicatorRect.maxX + MasterTimelineCellLayout.unreadCircleMarginRight, bottom: 0, right: 0)
|
||||
|
||||
self.init(width: width, height: height, feedNameRect: feedNameRect, dateRect: dateRect, titleRect: titleRect, numberOfLinesForTitle: numberOfLinesForTitle, summaryRect: summaryRect, textRect: textRect, unreadIndicatorRect: unreadIndicatorRect, starRect: starRect, avatarImageRect: avatarImageRect, paddingBottom: paddingBottom, separatorInsets: separatorInsets)
|
||||
|
||||
}
|
||||
|
||||
static func height(for width: CGFloat, cellData: MasterTimelineCellData) -> CGFloat {
|
||||
let layout = MasterTimelineCellLayout(width: width, height: 0.0, cellData: cellData, hasAvatar: true)
|
||||
return layout.height
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -127,123 +113,102 @@ struct MasterTimelineCellLayout {
|
|||
|
||||
private extension MasterTimelineCellLayout {
|
||||
|
||||
static func rectForTextBox(_ cellData: MasterTimelineCellData, _ showAvatar: Bool, _ width: CGFloat) -> CGRect {
|
||||
|
||||
// Returned height is a placeholder. Not needed when this is calculated.
|
||||
|
||||
let textBoxOriginX = MasterTimelineCellLayout.cellPadding.left + MasterTimelineCellLayout.unreadCircleDimension + MasterTimelineCellLayout.unreadCircleMarginRight
|
||||
let textBoxMaxX = floor((width - MasterTimelineCellLayout.cellPadding.right) - (showAvatar ? MasterTimelineCellLayout.avatarSize.width + MasterTimelineCellLayout.avatarMarginLeft : 0.0))
|
||||
let textBoxWidth = floor(textBoxMaxX - textBoxOriginX)
|
||||
let textBoxRect = CGRect(x: textBoxOriginX, y: MasterTimelineCellLayout.cellPadding.top, width: textBoxWidth, height: 1000000)
|
||||
|
||||
return textBoxRect
|
||||
|
||||
}
|
||||
|
||||
static func rectForTitle(_ textBoxRect: CGRect, _ cellData: MasterTimelineCellData) -> (CGRect, Int) {
|
||||
|
||||
var r = textBoxRect
|
||||
|
||||
if cellData.title.isEmpty {
|
||||
r.size.height = 0
|
||||
return (r, 0)
|
||||
}
|
||||
|
||||
let sizeInfo = MultilineUILabelSizer.size(for: cellData.title, font: MasterTimelineCellLayout.titleFont, numberOfLines: MasterTimelineCellLayout.titleNumberOfLines, width: Int(textBoxRect.width))
|
||||
r.size.height = sizeInfo.size.height
|
||||
if sizeInfo.numberOfLinesUsed < 1 {
|
||||
r.size.height = 0
|
||||
}
|
||||
return (r, sizeInfo.numberOfLinesUsed)
|
||||
}
|
||||
|
||||
static func rectForSummary(_ textBoxRect: CGRect, _ titleRect: CGRect, _ titleNumberOfLines: Int, _ cellData: MasterTimelineCellData) -> CGRect {
|
||||
|
||||
if titleNumberOfLines >= MasterTimelineCellLayout.titleNumberOfLines || cellData.text.isEmpty {
|
||||
return CGRect.zero
|
||||
}
|
||||
|
||||
return rectOfLineBelow(titleRect, titleRect, 0, cellData.text, MasterTimelineCellLayout.textFont)
|
||||
}
|
||||
|
||||
static func rectForText(_ textBoxRect: CGRect, _ cellData: MasterTimelineCellData) -> CGRect {
|
||||
|
||||
var r = textBoxRect
|
||||
|
||||
if cellData.text.isEmpty {
|
||||
r.size.height = 0
|
||||
return r
|
||||
}
|
||||
|
||||
let sizeInfo = MultilineUILabelSizer.size(for: cellData.text, font: MasterTimelineCellLayout.textOnlyFont, numberOfLines: MasterTimelineCellLayout.titleNumberOfLines, width: Int(textBoxRect.width))
|
||||
r.size.height = sizeInfo.size.height
|
||||
if sizeInfo.numberOfLinesUsed < 1 {
|
||||
r.size.height = 0
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
static func rectForDate(_ textBoxRect: CGRect, _ rectAbove: CGRect, _ cellData: MasterTimelineCellData) -> CGRect {
|
||||
|
||||
return rectOfLineBelow(textBoxRect, rectAbove, MasterTimelineCellLayout.titleBottomMargin, cellData.dateString, MasterTimelineCellLayout.dateFont)
|
||||
}
|
||||
|
||||
static func rectForFeedName(_ textBoxRect: CGRect, _ dateRect: CGRect, _ cellData: MasterTimelineCellData) -> CGRect {
|
||||
|
||||
if !cellData.showFeedName {
|
||||
return CGRect.zero
|
||||
}
|
||||
|
||||
return rectOfLineBelow(textBoxRect, dateRect, MasterTimelineCellLayout.dateMarginBottom, cellData.feedName, MasterTimelineCellLayout.feedNameFont)
|
||||
}
|
||||
|
||||
static func rectOfLineBelow(_ textBoxRect: CGRect, _ rectAbove: CGRect, _ topMargin: CGFloat, _ value: String, _ font: UIFont) -> CGRect {
|
||||
|
||||
let textFieldSize = SingleLineUILabelSizer.size(for: value, font: font)
|
||||
var r = CGRect.zero
|
||||
r.size = textFieldSize
|
||||
r.origin.y = rectAbove.maxY + topMargin
|
||||
r.origin.x = textBoxRect.origin.x
|
||||
|
||||
var width = textFieldSize.width
|
||||
width = min(width, textBoxRect.size.width)
|
||||
width = max(width, 0.0)
|
||||
r.size.width = width
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
static func rectForUnreadIndicator(_ titleRect: CGRect) -> CGRect {
|
||||
|
||||
static func rectForUnreadIndicator(_ point: CGPoint) -> CGRect {
|
||||
var r = CGRect.zero
|
||||
r.size = CGSize(width: MasterTimelineCellLayout.unreadCircleDimension, height: MasterTimelineCellLayout.unreadCircleDimension)
|
||||
r.origin.x = MasterTimelineCellLayout.cellPadding.left
|
||||
r.origin.y = titleRect.minY + 6
|
||||
r.origin.x = point.x
|
||||
r.origin.y = point.y + 9
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
static func rectForStar(_ unreadIndicatorRect: CGRect) -> CGRect {
|
||||
|
||||
static func rectForStar(_ point: CGPoint) -> CGRect {
|
||||
var r = CGRect.zero
|
||||
r.size.width = MasterTimelineCellLayout.starDimension
|
||||
r.size.height = MasterTimelineCellLayout.starDimension
|
||||
r.origin.x = floor(unreadIndicatorRect.origin.x - ((MasterTimelineCellLayout.starDimension - MasterTimelineCellLayout.unreadCircleDimension) / 2.0))
|
||||
r.origin.y = unreadIndicatorRect.origin.y - 4.0
|
||||
r.origin.x = floor(point.x - ((MasterTimelineCellLayout.starDimension - MasterTimelineCellLayout.unreadCircleDimension) / 2.0))
|
||||
r.origin.y = point.y + 5
|
||||
return r
|
||||
}
|
||||
|
||||
static func rectForAvatar(_ cellData: MasterTimelineCellData, _ showAvatar: Bool, _ textBoxRect: CGRect, _ width: CGFloat, _ height: CGFloat) -> CGRect {
|
||||
static func rectForAvatar(_ point: CGPoint) -> CGRect {
|
||||
var r = CGRect.zero
|
||||
r.size = MasterTimelineCellLayout.avatarSize
|
||||
r.origin = point
|
||||
return r
|
||||
}
|
||||
|
||||
static func rectForTitle(_ cellData: MasterTimelineCellData, _ point: CGPoint, _ textAreaWidth: CGFloat) -> (CGRect, Int) {
|
||||
|
||||
var r = CGRect.zero
|
||||
if !showAvatar {
|
||||
if cellData.title.isEmpty {
|
||||
return (r, 0)
|
||||
}
|
||||
|
||||
r.origin = point
|
||||
|
||||
let sizeInfo = MultilineUILabelSizer.size(for: cellData.title, font: MasterTimelineCellLayout.titleFont, numberOfLines: MasterTimelineCellLayout.maxNumberOfLines, width: Int(textAreaWidth))
|
||||
|
||||
r.size.width = textAreaWidth
|
||||
r.size.height = sizeInfo.size.height
|
||||
if sizeInfo.numberOfLinesUsed < 1 {
|
||||
r.size.height = 0
|
||||
}
|
||||
|
||||
return (r, sizeInfo.numberOfLinesUsed)
|
||||
|
||||
}
|
||||
|
||||
static func rectForSummary(_ cellData: MasterTimelineCellData, _ point: CGPoint, _ textAreaWidth: CGFloat, _ linesUsed: Int) -> CGRect {
|
||||
|
||||
let linesLeft = MasterTimelineCellLayout.maxNumberOfLines - linesUsed
|
||||
|
||||
var r = CGRect.zero
|
||||
if cellData.summary.isEmpty || linesLeft < 1 {
|
||||
return r
|
||||
}
|
||||
|
||||
r.size = MasterTimelineCellLayout.avatarSize
|
||||
r.origin.x = (width - MasterTimelineCellLayout.cellPadding.right) - r.size.width
|
||||
r.origin.y = textBoxRect.origin.y + 4.0
|
||||
r.origin = point
|
||||
|
||||
let sizeInfo = MultilineUILabelSizer.size(for: cellData.summary, font: MasterTimelineCellLayout.summaryFont, numberOfLines: linesLeft, width: Int(textAreaWidth))
|
||||
|
||||
r.size.width = textAreaWidth
|
||||
r.size.height = sizeInfo.size.height
|
||||
if sizeInfo.numberOfLinesUsed < 1 {
|
||||
r.size.height = 0
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
static func rectForDate(_ cellData: MasterTimelineCellData, _ point: CGPoint, _ textAreaWidth: CGFloat) -> CGRect {
|
||||
|
||||
var r = CGRect.zero
|
||||
|
||||
let size = SingleLineUILabelSizer.size(for: cellData.dateString, font: MasterTimelineCellLayout.dateFont)
|
||||
r.size = size
|
||||
r.origin.x = (point.x + textAreaWidth) - size.width
|
||||
r.origin.y = point.y
|
||||
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
static func rectForFeedName(_ cellData: MasterTimelineCellData, _ point: CGPoint, _ textAreaWidth: CGFloat) -> CGRect {
|
||||
|
||||
var r = CGRect.zero
|
||||
r.origin = point
|
||||
|
||||
let size = SingleLineUILabelSizer.size(for: cellData.feedName, font: MasterTimelineCellLayout.feedNameFont)
|
||||
r.size = size
|
||||
|
||||
if r.size.width > textAreaWidth {
|
||||
r.size.width = textAreaWidth
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,8 +12,7 @@ import RSCore
|
|||
class MasterTimelineTableViewCell: UITableViewCell {
|
||||
|
||||
private let titleView = MasterTimelineTableViewCell.multiLineUILabel()
|
||||
private let summaryView = MasterTimelineTableViewCell.singleLineUILabel()
|
||||
private let textView = MasterTimelineTableViewCell.multiLineUILabel()
|
||||
private let summaryView = MasterTimelineTableViewCell.multiLineUILabel()
|
||||
private let unreadIndicatorView = MasterUnreadIndicatorView(frame: CGRect.zero)
|
||||
private let dateView = MasterTimelineTableViewCell.singleLineUILabel()
|
||||
private let feedNameView = MasterTimelineTableViewCell.singleLineUILabel()
|
||||
|
@ -28,10 +27,6 @@ class MasterTimelineTableViewCell: UITableViewCell {
|
|||
return NonIntrinsicImageView(image: AppAssets.timelineStarImage)
|
||||
}()
|
||||
|
||||
private lazy var textFields = {
|
||||
return [self.dateView, self.feedNameView, self.titleView, self.summaryView, self.textView]
|
||||
}()
|
||||
|
||||
var cellData: MasterTimelineCellData! {
|
||||
didSet {
|
||||
updateSubviews()
|
||||
|
@ -49,21 +44,24 @@ class MasterTimelineTableViewCell: UITableViewCell {
|
|||
}
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
let layout = updatedLayout()
|
||||
return CGSize(width: bounds.width, height: layout.height)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
|
||||
super.layoutSubviews()
|
||||
|
||||
let layout = updatedLayout()
|
||||
|
||||
unreadIndicatorView.setFrameIfNotEqual(layout.unreadIndicatorRect)
|
||||
starView.setFrameIfNotEqual(layout.starRect)
|
||||
avatarImageView.setFrameIfNotEqual(layout.avatarImageRect)
|
||||
setFrame(for: titleView, rect: layout.titleRect)
|
||||
setFrame(for: summaryView, rect: layout.summaryRect)
|
||||
setFrame(for: textView, rect: layout.textRect)
|
||||
|
||||
dateView.setFrameIfNotEqual(layout.dateRect)
|
||||
unreadIndicatorView.setFrameIfNotEqual(layout.unreadIndicatorRect)
|
||||
feedNameView.setFrameIfNotEqual(layout.feedNameRect)
|
||||
avatarImageView.setFrameIfNotEqual(layout.avatarImageRect)
|
||||
starView.setFrameIfNotEqual(layout.starRect)
|
||||
dateView.setFrameIfNotEqual(layout.dateRect)
|
||||
|
||||
separatorInset = layout.separatorInsets
|
||||
|
||||
|
@ -79,6 +77,7 @@ private extension MasterTimelineTableViewCell {
|
|||
let label = NonIntrinsicLabel()
|
||||
label.lineBreakMode = .byTruncatingTail
|
||||
label.allowsDefaultTighteningForTruncation = false
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
return label
|
||||
}
|
||||
|
||||
|
@ -87,6 +86,7 @@ private extension MasterTimelineTableViewCell {
|
|||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.allowsDefaultTighteningForTruncation = false
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
return label
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,6 @@ private extension MasterTimelineTableViewCell {
|
|||
addAccessoryView()
|
||||
addSubviewAtInit(titleView, hidden: false)
|
||||
addSubviewAtInit(summaryView, hidden: true)
|
||||
addSubviewAtInit(textView, hidden: true)
|
||||
addSubviewAtInit(unreadIndicatorView, hidden: true)
|
||||
addSubviewAtInit(dateView, hidden: false)
|
||||
addSubviewAtInit(feedNameView, hidden: true)
|
||||
|
@ -133,8 +132,7 @@ private extension MasterTimelineTableViewCell {
|
|||
}
|
||||
|
||||
func updatedLayout() -> MasterTimelineCellLayout {
|
||||
|
||||
return MasterTimelineCellLayout(width: bounds.width, height: bounds.height, cellData: cellData, hasAvatar: avatarImageView.image != nil)
|
||||
return MasterTimelineCellLayout(width: bounds.width, insets: safeAreaInsets, cellData: cellData, showAvatar: avatarImageView.image != nil)
|
||||
}
|
||||
|
||||
func updateTitleView() {
|
||||
|
@ -144,15 +142,9 @@ private extension MasterTimelineTableViewCell {
|
|||
}
|
||||
|
||||
func updateSummaryView() {
|
||||
summaryView.font = MasterTimelineCellLayout.textFont
|
||||
summaryView.textColor = MasterTimelineCellLayout.textColor
|
||||
updateTextFieldText(summaryView, cellData?.text)
|
||||
}
|
||||
|
||||
func updateTextView() {
|
||||
textView.font = MasterTimelineCellLayout.textFont
|
||||
textView.textColor = MasterTimelineCellLayout.textColor
|
||||
updateTextFieldText(textView, cellData?.text)
|
||||
summaryView.font = MasterTimelineCellLayout.summaryFont
|
||||
summaryView.textColor = MasterTimelineCellLayout.summaryColor
|
||||
updateTextFieldText(summaryView, cellData?.summary)
|
||||
}
|
||||
|
||||
func updateDateView() {
|
||||
|
@ -234,7 +226,6 @@ private extension MasterTimelineTableViewCell {
|
|||
func updateSubviews() {
|
||||
updateTitleView()
|
||||
updateSummaryView()
|
||||
updateTextView()
|
||||
updateDateView()
|
||||
updateFeedNameView()
|
||||
updateUnreadIndicator()
|
||||
|
|
|
@ -14,12 +14,6 @@ import Articles
|
|||
class MasterTimelineViewController: ProgressTableViewController, UndoableCommandRunner {
|
||||
|
||||
private static var minAvatarDimension: CGFloat = 20.0
|
||||
private var rowHeightWithFeedName: CGFloat = 0.0
|
||||
private var rowHeightWithoutFeedName: CGFloat = 0.0
|
||||
|
||||
private var currentRowHeight: CGFloat {
|
||||
return navState?.showFeedNames ?? false ? rowHeightWithFeedName : rowHeightWithoutFeedName
|
||||
}
|
||||
|
||||
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
||||
@IBOutlet weak var firstUnreadButton: UIBarButtonItem!
|
||||
|
@ -34,7 +28,6 @@ class MasterTimelineViewController: ProgressTableViewController, UndoableCommand
|
|||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad()
|
||||
updateRowHeights()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||
|
@ -312,28 +305,6 @@ class MasterTimelineViewController: ProgressTableViewController, UndoableCommand
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Cell Configuring
|
||||
|
||||
private func calculateRowHeight(showingFeedNames: Bool) -> CGFloat {
|
||||
|
||||
let longTitle = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?"
|
||||
|
||||
let prototypeID = "prototype"
|
||||
let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, userDeleted: false, dateArrived: Date())
|
||||
let prototypeArticle = Article(accountID: prototypeID, articleID: prototypeID, feedID: prototypeID, uniqueID: prototypeID, title: longTitle, contentHTML: nil, contentText: nil, url: nil, externalURL: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: nil, dateModified: nil, authors: nil, attachments: nil, status: status)
|
||||
|
||||
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: showingFeedNames, feedName: "Prototype Feed Name", avatar: nil, showAvatar: false, featuredImage: nil)
|
||||
let height = MasterTimelineCellLayout.height(for: 100, cellData: prototypeCellData)
|
||||
return height
|
||||
|
||||
}
|
||||
|
||||
private func updateRowHeights() {
|
||||
rowHeightWithFeedName = calculateRowHeight(showingFeedNames: true)
|
||||
rowHeightWithoutFeedName = calculateRowHeight(showingFeedNames: false)
|
||||
updateTableViewRowHeight()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
@ -347,7 +318,6 @@ private extension MasterTimelineViewController {
|
|||
|
||||
func resetUI() {
|
||||
|
||||
updateTableViewRowHeight()
|
||||
title = navState?.timelineName
|
||||
navigationController?.title = navState?.timelineName
|
||||
|
||||
|
@ -431,11 +401,6 @@ private extension MasterTimelineViewController {
|
|||
CoalescingQueue.standard.add(self, #selector(reloadAllVisibleCells))
|
||||
}
|
||||
|
||||
func updateTableViewRowHeight() {
|
||||
tableView.rowHeight = currentRowHeight
|
||||
tableView.estimatedRowHeight = currentRowHeight
|
||||
}
|
||||
|
||||
func performBlockAndRestoreSelection(_ block: (() -> Void)) {
|
||||
let indexPaths = tableView.indexPathsForSelectedRows
|
||||
block()
|
||||
|
|
Loading…
Reference in New Issue