Implement new timeline layout.

This commit is contained in:
Maurice Parker 2019-04-29 14:40:14 -05:00
parent 2efbd44811
commit e37d4ddd2f
5 changed files with 167 additions and 246 deletions

View File

@ -12,13 +12,13 @@ extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat { func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude) 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) return ceil(boundingBox.height)
} }
func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat { func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height) 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) return ceil(boundingBox.width)
} }

View File

@ -12,7 +12,7 @@ import Articles
struct MasterTimelineCellData { struct MasterTimelineCellData {
let title: String let title: String
let text: String let summary: String
let dateString: String let dateString: String
let feedName: String let feedName: String
let showFeedName: Bool let showFeedName: Bool
@ -25,7 +25,7 @@ struct MasterTimelineCellData {
init(article: Article, showFeedName: Bool, feedName: String?, avatar: UIImage?, showAvatar: Bool, featuredImage: UIImage?) { init(article: Article, showFeedName: Bool, feedName: String?, avatar: UIImage?, showAvatar: Bool, featuredImage: UIImage?) {
self.title = TimelineStringFormatter.truncatedTitle(article) self.title = TimelineStringFormatter.truncatedTitle(article)
self.text = TimelineStringFormatter.truncatedSummary(article) self.summary = TimelineStringFormatter.truncatedSummary(article)
self.dateString = TimelineStringFormatter.dateString(article.logicalDatePublished) self.dateString = TimelineStringFormatter.dateString(article.logicalDatePublished)
@ -48,7 +48,7 @@ struct MasterTimelineCellData {
init() { //Empty init() { //Empty
self.title = "" self.title = ""
self.text = "" self.summary = ""
self.dateString = "" self.dateString = ""
self.feedName = "" self.feedName = ""
self.showFeedName = false self.showFeedName = false

View File

@ -11,114 +11,100 @@ import RSCore
struct MasterTimelineCellLayout { struct MasterTimelineCellLayout {
static let maxNumberOfLines = 2
static let cellPadding = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) static let cellPadding = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
static let feedColor = AppAssets.timelineTextSecondaryColor static let unreadCircleMarginLeft = CGFloat(integerLiteral: 8)
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 unreadCircleDimension = CGFloat(integerLiteral: 8) static let unreadCircleDimension = CGFloat(integerLiteral: 8)
static let unreadCircleMarginRight = CGFloat(integerLiteral: 8) static let unreadCircleMarginRight = CGFloat(integerLiteral: 8)
static let starDimension = CGFloat(integerLiteral: 13) static let starDimension = CGFloat(integerLiteral: 13)
static let avatarSize = CGSize(width: 48.0, height: 48.0) 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 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) static let chevronWidth = CGFloat(integerLiteral: 28)
let width: CGFloat let width: CGFloat
let insets: UIEdgeInsets
let height: CGFloat 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 unreadIndicatorRect: CGRect
let starRect: CGRect let starRect: CGRect
let avatarImageRect: CGRect let avatarImageRect: CGRect
let paddingBottom: CGFloat let titleRect: CGRect
let summaryRect: CGRect
let feedNameRect: CGRect
let dateRect: CGRect
let separatorInsets: UIEdgeInsets 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.width = width
self.feedNameRect = feedNameRect self.insets = insets
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
if height > 0.1 { var currentPoint = CGPoint.zero
self.height = height 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 { } 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)
init(width: CGFloat, height: CGFloat, cellData: MasterTimelineCellData, hasAvatar: Bool) { // Title Text Block
let (titleRect, numberOfLinesForTitle) = MasterTimelineCellLayout.rectForTitle(cellData, currentPoint, textAreaWidth)
self.titleRect = titleRect
let width = width - MasterTimelineCellLayout.chevronWidth // Summary Text Block
if self.titleRect != CGRect.zero {
// If height == 0.0, then height is calculated. currentPoint.y = self.titleRect.maxY + MasterTimelineCellLayout.titleBottomMargin
let showAvatar = hasAvatar && cellData.showAvatar
var textBoxRect = MasterTimelineCellLayout.rectForTextBox(cellData, showAvatar, width)
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
}
} }
self.summaryRect = MasterTimelineCellLayout.rectForSummary(cellData, currentPoint, textAreaWidth, numberOfLinesForTitle)
let dateRect = MasterTimelineCellLayout.rectForDate(textBoxRect, lastTextRect, cellData) currentPoint.y = [self.titleRect, self.summaryRect].maxY()
let feedNameRect = MasterTimelineCellLayout.rectForFeedName(textBoxRect, dateRect, cellData)
textBoxRect.size.height = ceil([titleRect, summaryRect, textRect, dateRect, feedNameRect].maxY() - textBoxRect.origin.y) // Feed Name and Pub Date
let avatarImageRect = MasterTimelineCellLayout.rectForAvatar(cellData, showAvatar, textBoxRect, width, height) self.dateRect = MasterTimelineCellLayout.rectForDate(cellData, currentPoint, textAreaWidth)
let unreadIndicatorRect = MasterTimelineCellLayout.rectForUnreadIndicator(textBoxRect)
let starRect = MasterTimelineCellLayout.rectForStar(unreadIndicatorRect)
let paddingBottom = MasterTimelineCellLayout.cellPadding.bottom let feedNameWidth = textAreaWidth - (MasterTimelineCellLayout.feedRightMargin + self.dateRect.size.width)
self.feedNameRect = MasterTimelineCellLayout.rectForFeedName(cellData, currentPoint, feedNameWidth)
let separatorInsets = UIEdgeInsets(top: 0, left: unreadIndicatorRect.maxX + MasterTimelineCellLayout.unreadCircleMarginRight, bottom: 0, right: 0) self.height = [self.avatarImageRect, self.feedNameRect].maxY() + MasterTimelineCellLayout.cellPadding.bottom
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 { private extension MasterTimelineCellLayout {
static func rectForTextBox(_ cellData: MasterTimelineCellData, _ showAvatar: Bool, _ width: CGFloat) -> CGRect { static func rectForUnreadIndicator(_ point: CGPoint) -> 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 {
var r = CGRect.zero var r = CGRect.zero
r.size = CGSize(width: MasterTimelineCellLayout.unreadCircleDimension, height: MasterTimelineCellLayout.unreadCircleDimension) r.size = CGSize(width: MasterTimelineCellLayout.unreadCircleDimension, height: MasterTimelineCellLayout.unreadCircleDimension)
r.origin.x = MasterTimelineCellLayout.cellPadding.left r.origin.x = point.x
r.origin.y = titleRect.minY + 6 r.origin.y = point.y + 9
return r return r
} }
static func rectForStar(_ unreadIndicatorRect: CGRect) -> CGRect {
static func rectForStar(_ point: CGPoint) -> CGRect {
var r = CGRect.zero var r = CGRect.zero
r.size.width = MasterTimelineCellLayout.starDimension r.size.width = MasterTimelineCellLayout.starDimension
r.size.height = MasterTimelineCellLayout.starDimension r.size.height = MasterTimelineCellLayout.starDimension
r.origin.x = floor(unreadIndicatorRect.origin.x - ((MasterTimelineCellLayout.starDimension - MasterTimelineCellLayout.unreadCircleDimension) / 2.0)) r.origin.x = floor(point.x - ((MasterTimelineCellLayout.starDimension - MasterTimelineCellLayout.unreadCircleDimension) / 2.0))
r.origin.y = unreadIndicatorRect.origin.y - 4.0 r.origin.y = point.y + 5
return r 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 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 return r
} }
r.size = MasterTimelineCellLayout.avatarSize r.origin = point
r.origin.x = (width - MasterTimelineCellLayout.cellPadding.right) - r.size.width
r.origin.y = textBoxRect.origin.y + 4.0 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 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
}
} }

View File

@ -12,8 +12,7 @@ import RSCore
class MasterTimelineTableViewCell: UITableViewCell { class MasterTimelineTableViewCell: UITableViewCell {
private let titleView = MasterTimelineTableViewCell.multiLineUILabel() private let titleView = MasterTimelineTableViewCell.multiLineUILabel()
private let summaryView = MasterTimelineTableViewCell.singleLineUILabel() private let summaryView = MasterTimelineTableViewCell.multiLineUILabel()
private let textView = MasterTimelineTableViewCell.multiLineUILabel()
private let unreadIndicatorView = MasterUnreadIndicatorView(frame: CGRect.zero) private let unreadIndicatorView = MasterUnreadIndicatorView(frame: CGRect.zero)
private let dateView = MasterTimelineTableViewCell.singleLineUILabel() private let dateView = MasterTimelineTableViewCell.singleLineUILabel()
private let feedNameView = MasterTimelineTableViewCell.singleLineUILabel() private let feedNameView = MasterTimelineTableViewCell.singleLineUILabel()
@ -28,10 +27,6 @@ class MasterTimelineTableViewCell: UITableViewCell {
return NonIntrinsicImageView(image: AppAssets.timelineStarImage) return NonIntrinsicImageView(image: AppAssets.timelineStarImage)
}() }()
private lazy var textFields = {
return [self.dateView, self.feedNameView, self.titleView, self.summaryView, self.textView]
}()
var cellData: MasterTimelineCellData! { var cellData: MasterTimelineCellData! {
didSet { didSet {
updateSubviews() 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() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
let layout = updatedLayout() let layout = updatedLayout()
unreadIndicatorView.setFrameIfNotEqual(layout.unreadIndicatorRect)
starView.setFrameIfNotEqual(layout.starRect)
avatarImageView.setFrameIfNotEqual(layout.avatarImageRect)
setFrame(for: titleView, rect: layout.titleRect) setFrame(for: titleView, rect: layout.titleRect)
setFrame(for: summaryView, rect: layout.summaryRect) setFrame(for: summaryView, rect: layout.summaryRect)
setFrame(for: textView, rect: layout.textRect)
dateView.setFrameIfNotEqual(layout.dateRect)
unreadIndicatorView.setFrameIfNotEqual(layout.unreadIndicatorRect)
feedNameView.setFrameIfNotEqual(layout.feedNameRect) feedNameView.setFrameIfNotEqual(layout.feedNameRect)
avatarImageView.setFrameIfNotEqual(layout.avatarImageRect) dateView.setFrameIfNotEqual(layout.dateRect)
starView.setFrameIfNotEqual(layout.starRect)
separatorInset = layout.separatorInsets separatorInset = layout.separatorInsets
@ -79,6 +77,7 @@ private extension MasterTimelineTableViewCell {
let label = NonIntrinsicLabel() let label = NonIntrinsicLabel()
label.lineBreakMode = .byTruncatingTail label.lineBreakMode = .byTruncatingTail
label.allowsDefaultTighteningForTruncation = false label.allowsDefaultTighteningForTruncation = false
label.adjustsFontForContentSizeCategory = true
return label return label
} }
@ -87,6 +86,7 @@ private extension MasterTimelineTableViewCell {
label.numberOfLines = 0 label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping label.lineBreakMode = .byWordWrapping
label.allowsDefaultTighteningForTruncation = false label.allowsDefaultTighteningForTruncation = false
label.adjustsFontForContentSizeCategory = true
return label return label
} }
@ -113,7 +113,6 @@ private extension MasterTimelineTableViewCell {
addAccessoryView() addAccessoryView()
addSubviewAtInit(titleView, hidden: false) addSubviewAtInit(titleView, hidden: false)
addSubviewAtInit(summaryView, hidden: true) addSubviewAtInit(summaryView, hidden: true)
addSubviewAtInit(textView, hidden: true)
addSubviewAtInit(unreadIndicatorView, hidden: true) addSubviewAtInit(unreadIndicatorView, hidden: true)
addSubviewAtInit(dateView, hidden: false) addSubviewAtInit(dateView, hidden: false)
addSubviewAtInit(feedNameView, hidden: true) addSubviewAtInit(feedNameView, hidden: true)
@ -133,8 +132,7 @@ private extension MasterTimelineTableViewCell {
} }
func updatedLayout() -> MasterTimelineCellLayout { func updatedLayout() -> MasterTimelineCellLayout {
return MasterTimelineCellLayout(width: bounds.width, insets: safeAreaInsets, cellData: cellData, showAvatar: avatarImageView.image != nil)
return MasterTimelineCellLayout(width: bounds.width, height: bounds.height, cellData: cellData, hasAvatar: avatarImageView.image != nil)
} }
func updateTitleView() { func updateTitleView() {
@ -144,15 +142,9 @@ private extension MasterTimelineTableViewCell {
} }
func updateSummaryView() { func updateSummaryView() {
summaryView.font = MasterTimelineCellLayout.textFont summaryView.font = MasterTimelineCellLayout.summaryFont
summaryView.textColor = MasterTimelineCellLayout.textColor summaryView.textColor = MasterTimelineCellLayout.summaryColor
updateTextFieldText(summaryView, cellData?.text) updateTextFieldText(summaryView, cellData?.summary)
}
func updateTextView() {
textView.font = MasterTimelineCellLayout.textFont
textView.textColor = MasterTimelineCellLayout.textColor
updateTextFieldText(textView, cellData?.text)
} }
func updateDateView() { func updateDateView() {
@ -234,7 +226,6 @@ private extension MasterTimelineTableViewCell {
func updateSubviews() { func updateSubviews() {
updateTitleView() updateTitleView()
updateSummaryView() updateSummaryView()
updateTextView()
updateDateView() updateDateView()
updateFeedNameView() updateFeedNameView()
updateUnreadIndicator() updateUnreadIndicator()

View File

@ -14,12 +14,6 @@ import Articles
class MasterTimelineViewController: ProgressTableViewController, UndoableCommandRunner { class MasterTimelineViewController: ProgressTableViewController, UndoableCommandRunner {
private static var minAvatarDimension: CGFloat = 20.0 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 markAllAsReadButton: UIBarButtonItem!
@IBOutlet weak var firstUnreadButton: UIBarButtonItem! @IBOutlet weak var firstUnreadButton: UIBarButtonItem!
@ -34,7 +28,6 @@ class MasterTimelineViewController: ProgressTableViewController, UndoableCommand
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
updateRowHeights()
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, 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 // MARK: Private
@ -347,7 +318,6 @@ private extension MasterTimelineViewController {
func resetUI() { func resetUI() {
updateTableViewRowHeight()
title = navState?.timelineName title = navState?.timelineName
navigationController?.title = navState?.timelineName navigationController?.title = navState?.timelineName
@ -431,11 +401,6 @@ private extension MasterTimelineViewController {
CoalescingQueue.standard.add(self, #selector(reloadAllVisibleCells)) CoalescingQueue.standard.add(self, #selector(reloadAllVisibleCells))
} }
func updateTableViewRowHeight() {
tableView.rowHeight = currentRowHeight
tableView.estimatedRowHeight = currentRowHeight
}
func performBlockAndRestoreSelection(_ block: (() -> Void)) { func performBlockAndRestoreSelection(_ block: (() -> Void)) {
let indexPaths = tableView.indexPathsForSelectedRows let indexPaths = tableView.indexPathsForSelectedRows
block() block()