Add IconImage to encapsulate our icon processing logic

This commit is contained in:
Maurice Parker
2019-11-05 18:05:57 -06:00
parent 05e0e34f6b
commit 560f36621f
46 changed files with 336 additions and 323 deletions

View File

@@ -14,7 +14,7 @@ struct MasterTimelineAccessibilityCellLayout: MasterTimelineCellLayout {
let height: CGFloat
let unreadIndicatorRect: CGRect
let starRect: CGRect
let avatarImageRect: CGRect
let iconImageRect: CGRect
let titleRect: CGRect
let summaryRect: CGRect
let feedNameRect: CGRect
@@ -37,12 +37,12 @@ struct MasterTimelineAccessibilityCellLayout: MasterTimelineCellLayout {
// Separator Insets
self.separatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
// Avatar
if cellData.showAvatar {
self.avatarImageRect = MasterTimelineAccessibilityCellLayout.rectForAvatar(currentPoint)
currentPoint.y = self.avatarImageRect.maxY
// Icon Image
if cellData.showIcon {
self.iconImageRect = MasterTimelineAccessibilityCellLayout.rectForIconView(currentPoint)
currentPoint.y = self.iconImageRect.maxY
} else {
self.avatarImageRect = CGRect.zero
self.iconImageRect = CGRect.zero
}
let textAreaWidth = width - (currentPoint.x + MasterTimelineDefaultCellLayout.cellPadding.right + insets.right)

View File

@@ -16,14 +16,14 @@ struct MasterTimelineCellData {
let dateString: String
let feedName: String
let showFeedName: Bool
let avatar: UIImage? // feed icon, user avatar, or favicon
let showAvatar: Bool // Make space even when avatar is nil
let iconImage: IconImage? // feed icon, user avatar, or favicon
let showIcon: Bool // Make space even when icon is nil
let featuredImage: UIImage? // image from within the article
let read: Bool
let starred: Bool
let numberOfLines: Int
init(article: Article, showFeedName: Bool, feedName: String?, avatar: UIImage?, showAvatar: Bool, featuredImage: UIImage?, numberOfLines: Int) {
init(article: Article, showFeedName: Bool, feedName: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: UIImage?, numberOfLines: Int) {
self.title = ArticleStringFormatter.truncatedTitle(article)
self.summary = ArticleStringFormatter.truncatedSummary(article)
@@ -39,8 +39,8 @@ struct MasterTimelineCellData {
self.showFeedName = showFeedName
self.showAvatar = showAvatar
self.avatar = avatar
self.showIcon = showIcon
self.iconImage = iconImage
self.featuredImage = featuredImage
self.read = article.status.read
@@ -55,8 +55,8 @@ struct MasterTimelineCellData {
self.dateString = ""
self.feedName = ""
self.showFeedName = false
self.showAvatar = false
self.avatar = nil
self.showIcon = false
self.iconImage = nil
self.featuredImage = nil
self.read = true
self.starred = false

View File

@@ -13,7 +13,7 @@ protocol MasterTimelineCellLayout {
var height: CGFloat {get}
var unreadIndicatorRect: CGRect {get}
var starRect: CGRect {get}
var avatarImageRect: CGRect {get}
var iconImageRect: CGRect {get}
var titleRect: CGRect {get}
var summaryRect: CGRect {get}
var feedNameRect: CGRect {get}
@@ -42,9 +42,9 @@ extension MasterTimelineCellLayout {
return r
}
static func rectForAvatar(_ point: CGPoint) -> CGRect {
static func rectForIconView(_ point: CGPoint) -> CGRect {
var r = CGRect.zero
r.size = MasterTimelineDefaultCellLayout.avatarSize
r.size = MasterTimelineDefaultCellLayout.iconImageSize
r.origin.x = point.x
r.origin.y = point.y + 4
return r

View File

@@ -21,9 +21,9 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
static let starDimension = CGFloat(integerLiteral: 16)
static let starSize = CGSize(width: MasterTimelineDefaultCellLayout.starDimension, height: MasterTimelineDefaultCellLayout.starDimension)
static let avatarSize = CGSize(width: 32.0, height: 32.0)
static let avatarMarginRight = CGFloat(integerLiteral: 8)
static let avatarCornerRadius = CGFloat(integerLiteral: 4)
static let iconImageSize = CGSize(width: 32.0, height: 32.0)
static let iconMarginRight = CGFloat(integerLiteral: 8)
static let iconCornerRadius = CGFloat(integerLiteral: 4)
static var titleFont: UIFont {
return UIFont.preferredFont(forTextStyle: .headline)
@@ -47,7 +47,7 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
let height: CGFloat
let unreadIndicatorRect: CGRect
let starRect: CGRect
let avatarImageRect: CGRect
let iconImageRect: CGRect
let titleRect: CGRect
let summaryRect: CGRect
let feedNameRect: CGRect
@@ -70,12 +70,12 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
// Separator Insets
self.separatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
// Avatar
if cellData.showAvatar {
self.avatarImageRect = MasterTimelineDefaultCellLayout.rectForAvatar(currentPoint)
currentPoint.x = self.avatarImageRect.maxX + MasterTimelineDefaultCellLayout.avatarMarginRight
// Icon Image
if cellData.showIcon {
self.iconImageRect = MasterTimelineDefaultCellLayout.rectForIconView(currentPoint)
currentPoint.x = self.iconImageRect.maxX + MasterTimelineDefaultCellLayout.iconMarginRight
} else {
self.avatarImageRect = CGRect.zero
self.iconImageRect = CGRect.zero
}
let textAreaWidth = width - (currentPoint.x + MasterTimelineDefaultCellLayout.cellPadding.right + insets.right)
@@ -98,7 +98,7 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
let feedNameWidth = textAreaWidth - (MasterTimelineDefaultCellLayout.feedRightMargin + self.dateRect.size.width)
self.feedNameRect = MasterTimelineDefaultCellLayout.rectForFeedName(cellData, currentPoint, feedNameWidth)
self.height = [self.avatarImageRect, self.feedNameRect].maxY() + MasterTimelineDefaultCellLayout.cellPadding.bottom
self.height = [self.iconImageRect, self.feedNameRect].maxY() + MasterTimelineDefaultCellLayout.cellPadding.bottom
}

View File

@@ -17,7 +17,7 @@ class MasterTimelineTableViewCell: VibrantTableViewCell {
private let dateView = MasterTimelineTableViewCell.singleLineUILabel()
private let feedNameView = MasterTimelineTableViewCell.singleLineUILabel()
private lazy var avatarView = AvatarView()
private lazy var iconView = IconView()
private lazy var starView = {
return NonIntrinsicImageView(image: AppAssets.timelineStarImage)
@@ -68,7 +68,7 @@ class MasterTimelineTableViewCell: VibrantTableViewCell {
unreadIndicatorView.setFrameIfNotEqual(layout.unreadIndicatorRect)
starView.setFrameIfNotEqual(layout.starRect)
avatarView.setFrameIfNotEqual(layout.avatarImageRect)
iconView.setFrameIfNotEqual(layout.iconImageRect)
setFrame(for: titleView, rect: layout.titleRect)
setFrame(for: summaryView, rect: layout.summaryRect)
feedNameView.setFrameIfNotEqual(layout.feedNameRect)
@@ -77,8 +77,8 @@ class MasterTimelineTableViewCell: VibrantTableViewCell {
separatorInset = layout.separatorInsets
}
func setAvatarImage(_ image: UIImage) {
avatarView.image = image
func setIconImage(_ image: IconImage) {
iconView.iconImage = image
}
}
@@ -128,7 +128,7 @@ private extension MasterTimelineTableViewCell {
addSubviewAtInit(unreadIndicatorView, hidden: true)
addSubviewAtInit(dateView, hidden: false)
addSubviewAtInit(feedNameView, hidden: true)
addSubviewAtInit(avatarView, hidden: true)
addSubviewAtInit(iconView, hidden: true)
addSubviewAtInit(starView, hidden: true)
}
@@ -167,7 +167,6 @@ private extension MasterTimelineTableViewCell {
}
func updateFeedNameView() {
if cellData.showFeedName {
showView(feedNameView)
feedNameView.font = MasterTimelineDefaultCellLayout.feedNameFont
@@ -187,28 +186,26 @@ private extension MasterTimelineTableViewCell {
showOrHideView(starView, !cellData.starred)
}
func updateAvatar() {
guard let image = cellData.avatar, cellData.showAvatar else {
makeAvatarEmpty()
func updateIconImage() {
guard let image = cellData.iconImage, cellData.showIcon else {
makeIconEmpty()
return
}
showView(avatarView)
showView(iconView)
if avatarView.image !== cellData.avatar {
avatarView.image = image
if iconView.iconImage !== cellData.iconImage {
iconView.iconImage = image
setNeedsLayout()
}
}
func makeAvatarEmpty() {
if avatarView.image != nil {
avatarView.image = nil
func makeIconEmpty() {
if iconView.iconImage != nil {
iconView.iconImage = nil
setNeedsLayout()
}
hideView(avatarView)
hideView(iconView)
}
func hideView(_ view: UIView) {
@@ -234,7 +231,7 @@ private extension MasterTimelineTableViewCell {
updateFeedNameView()
updateUnreadIndicator()
updateStarView()
updateAvatar()
updateIconImage()
}
}

View File

@@ -10,7 +10,7 @@ import UIKit
class MasterTimelineTitleView: UIView {
@IBOutlet weak var avatarView: AvatarView!
@IBOutlet weak var iconView: IconView!
@IBOutlet weak var label: UILabel!
@IBOutlet weak var unreadCountView: MasterTimelineUnreadCountView!

View File

@@ -23,7 +23,7 @@
<rect key="frame" x="79.5" y="9" width="110.5" height="20"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5gI-Wl-lnK" customClass="AvatarView" customModule="NetNewsWire" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5gI-Wl-lnK" customClass="IconView" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="9" width="20" height="20"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
@@ -47,7 +47,7 @@
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<connections>
<outlet property="avatarView" destination="5gI-Wl-lnK" id="u0l-N7-Kbp"/>
<outlet property="iconView" destination="5gI-Wl-lnK" id="IiR-qS-d22"/>
<outlet property="label" destination="5F6-2v-qSS" id="ec7-8Y-PRv"/>
<outlet property="unreadCountView" destination="z9o-XA-3t4" id="JBy-aa-feL"/>
</connections>

View File

@@ -310,7 +310,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
}
@objc func feedIconDidBecomeAvailable(_ note: Notification) {
titleView?.avatarView.image = coordinator.timelineAvatar
titleView?.iconView.iconImage = coordinator.timelineIconImage
guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed else {
return
}
@@ -318,14 +318,14 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
guard let article = dataSource.itemIdentifier(for: indexPath) else {
return
}
if article.feed == feed, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = avatarFor(article) {
cell.setAvatarImage(image)
if article.feed == feed, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = iconImageFor(article) {
cell.setIconImage(image)
}
}
}
@objc func avatarDidBecomeAvailable(_ note: Notification) {
guard coordinator.showAvatars, let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
guard coordinator.showIcons, let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
return
}
tableView.indexPathsForVisibleRows?.forEach { indexPath in
@@ -333,16 +333,16 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
return
}
for author in authors {
if author.avatarURL == avatarURL, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = avatarFor(article) {
cell.setAvatarImage(image)
if author.avatarURL == avatarURL, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = iconImageFor(article) {
cell.setIconImage(image)
}
}
}
}
@objc func faviconDidBecomeAvailable(_ note: Notification) {
titleView?.avatarView.image = coordinator.timelineAvatar
if coordinator.showAvatars {
titleView?.iconView.iconImage = coordinator.timelineIconImage
if coordinator.showIcons {
queueReloadAvailableCells()
}
}
@@ -392,7 +392,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
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: true, feedName: "Prototype Feed Name", avatar: nil, showAvatar: false, featuredImage: nil, numberOfLines: numberOfTextLines)
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: true, feedName: "Prototype Feed Name", iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines)
if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory {
let layout = MasterTimelineAccessibilityCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
@@ -448,7 +448,7 @@ private extension MasterTimelineViewController {
if let titleView = Bundle.main.loadNibNamed("MasterTimelineTitleView", owner: self, options: nil)?[0] as? MasterTimelineTitleView {
self.titleView = titleView
titleView.avatarView.image = coordinator.timelineAvatar
titleView.iconView.iconImage = coordinator.timelineIconImage
titleView.label.text = coordinator.timelineName
updateTitleUnreadCount()
@@ -508,20 +508,20 @@ private extension MasterTimelineViewController {
func configure(_ cell: MasterTimelineTableViewCell, article: Article) {
let avatar = avatarFor(article)
let iconImage = iconImageFor(article)
let featuredImage = featuredImageFor(article)
let showFeedNames = coordinator.showFeedNames
let showAvatar = coordinator.showAvatars && avatar != nil
cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.feed?.nameForDisplay, avatar: avatar, showAvatar: showAvatar, featuredImage: featuredImage, numberOfLines: numberOfTextLines)
let showIcon = coordinator.showIcons && iconImage != nil
cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.feed?.nameForDisplay, iconImage: iconImage, showIcon: showIcon, featuredImage: featuredImage, numberOfLines: numberOfTextLines)
}
func avatarFor(_ article: Article) -> RSImage? {
if !coordinator.showAvatars {
func iconImageFor(_ article: Article) -> IconImage? {
if !coordinator.showIcons {
return nil
}
return article.avatarImage()
return article.iconImage()
}
func featuredImageFor(_ article: Article) -> UIImage? {