mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Add IconImage to encapsulate our icon processing logic
This commit is contained in:
@@ -49,8 +49,8 @@ struct AppAssets {
|
||||
return image.maskWithColor(color: AppAssets.primaryAccentColor.cgColor)!
|
||||
}()
|
||||
|
||||
static var avatarBackgroundColor: UIColor = {
|
||||
return UIColor(named: "avatarBackgroundColor")!
|
||||
static var iconBackgroundColor: UIColor = {
|
||||
return UIColor(named: "iconBackgroundColor")!
|
||||
}()
|
||||
|
||||
static var barBackgroundColor: UIColor = {
|
||||
@@ -105,8 +105,8 @@ struct AppAssets {
|
||||
return UIImage(systemName: "arrowtriangle.up.circle")!
|
||||
}()
|
||||
|
||||
static var masterFolderImage: UIImage = {
|
||||
return UIImage(systemName: "folder.fill")!
|
||||
static var masterFolderImage: IconImage = {
|
||||
return IconImage(UIImage(systemName: "folder.fill")!)
|
||||
}()
|
||||
|
||||
static var moreImage: UIImage = {
|
||||
@@ -125,8 +125,8 @@ struct AppAssets {
|
||||
return UIImage(systemName: "safari")!
|
||||
}()
|
||||
|
||||
static var searchFeedImage: UIImage = {
|
||||
return UIImage(systemName: "magnifyingglass")!
|
||||
static var searchFeedImage: IconImage = {
|
||||
return IconImage(UIImage(systemName: "magnifyingglass")!)
|
||||
}()
|
||||
|
||||
static var secondaryAccentColor: UIColor = {
|
||||
@@ -157,8 +157,8 @@ struct AppAssets {
|
||||
return UIImage(systemName: "star")!
|
||||
}()
|
||||
|
||||
static var starredFeedImage: UIImage = {
|
||||
return UIImage(systemName: "star.fill")!
|
||||
static var starredFeedImage: IconImage = {
|
||||
return IconImage(UIImage(systemName: "star.fill")!)
|
||||
}()
|
||||
|
||||
static var timelineStarImage: UIImage = {
|
||||
@@ -166,16 +166,16 @@ struct AppAssets {
|
||||
return image.withTintColor(AppAssets.starColor, renderingMode: .alwaysOriginal)
|
||||
}()
|
||||
|
||||
static var todayFeedImage: UIImage = {
|
||||
return UIImage(systemName: "sun.max.fill")!
|
||||
static var todayFeedImage: IconImage = {
|
||||
return IconImage(UIImage(systemName: "sun.max.fill")!)
|
||||
}()
|
||||
|
||||
static var trashImage: UIImage = {
|
||||
return UIImage(systemName: "trash")!
|
||||
}()
|
||||
|
||||
static var unreadFeedImage: UIImage = {
|
||||
return UIImage(systemName: "largecircle.fill.circle")!
|
||||
static var unreadFeedImage: IconImage = {
|
||||
return IconImage(UIImage(systemName: "largecircle.fill.circle")!)
|
||||
}()
|
||||
|
||||
static var vibrantTextColor: UIColor = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// AvatarView.swift
|
||||
// IconView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/17/19.
|
||||
@@ -8,16 +8,16 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
final class AvatarView: UIView {
|
||||
final class IconView: UIView {
|
||||
|
||||
var image: UIImage? = nil {
|
||||
var iconImage: IconImage? = nil {
|
||||
didSet {
|
||||
if image !== oldValue {
|
||||
imageView.image = image
|
||||
if iconImage !== oldValue {
|
||||
imageView.image = iconImage?.image
|
||||
|
||||
if self.traitCollection.userInterfaceStyle == .dark {
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
if self.image?.isDark() ?? false {
|
||||
if self.iconImage?.isDark ?? false {
|
||||
DispatchQueue.main.async {
|
||||
self.isDisconcernable = false
|
||||
self.setNeedsLayout()
|
||||
@@ -74,8 +74,8 @@ final class AvatarView: UIView {
|
||||
|
||||
override func layoutSubviews() {
|
||||
imageView.setFrameIfNotEqual(rectForImageView())
|
||||
if (image != nil && isVerticalBackgroundExposed && !isSymbolImage) || !isDisconcernable {
|
||||
backgroundColor = AppAssets.avatarBackgroundColor
|
||||
if (iconImage != nil && isVerticalBackgroundExposed && !isSymbolImage) || !isDisconcernable {
|
||||
backgroundColor = AppAssets.iconBackgroundColor
|
||||
} else {
|
||||
backgroundColor = nil
|
||||
}
|
||||
@@ -83,16 +83,16 @@ final class AvatarView: UIView {
|
||||
|
||||
}
|
||||
|
||||
private extension AvatarView {
|
||||
private extension IconView {
|
||||
|
||||
func commonInit() {
|
||||
layer.cornerRadius = MasterTimelineDefaultCellLayout.avatarCornerRadius
|
||||
layer.cornerRadius = MasterTimelineDefaultCellLayout.iconCornerRadius
|
||||
clipsToBounds = true
|
||||
addSubview(imageView)
|
||||
}
|
||||
|
||||
func rectForImageView() -> CGRect {
|
||||
guard let image = image else {
|
||||
guard let image = iconImage?.image else {
|
||||
return CGRect.zero
|
||||
}
|
||||
|
||||
@@ -22,28 +22,28 @@ struct FeedInspectorView : View {
|
||||
Section(header:
|
||||
HStack {
|
||||
Spacer()
|
||||
if self.viewModel.image.size.width < 32 || self.viewModel.image.size.height < 32 {
|
||||
if colorScheme == .dark && self.viewModel.image.isDark() {
|
||||
Image(uiImage: self.viewModel.image)
|
||||
if self.viewModel.iconImage.image.size.width < 32 || self.viewModel.iconImage.image.size.height < 32 {
|
||||
if colorScheme == .dark && self.viewModel.iconImage.isDark {
|
||||
Image(uiImage: self.viewModel.iconImage.image)
|
||||
.resizable()
|
||||
.background(Color(AppAssets.avatarBackgroundColor))
|
||||
.background(Color(AppAssets.iconBackgroundColor))
|
||||
.frame(width: 24.0, height: 24.0)
|
||||
.cornerRadius(2.0)
|
||||
} else {
|
||||
Image(uiImage: self.viewModel.image)
|
||||
Image(uiImage: self.viewModel.iconImage.image)
|
||||
.resizable()
|
||||
.frame(width: 24.0, height: 24.0)
|
||||
.cornerRadius(2.0)
|
||||
}
|
||||
} else {
|
||||
if colorScheme == .dark && self.viewModel.image.isDark() {
|
||||
Image(uiImage: self.viewModel.image)
|
||||
if colorScheme == .dark && self.viewModel.iconImage.isDark {
|
||||
Image(uiImage: self.viewModel.iconImage.image)
|
||||
.resizable()
|
||||
.background(Color(AppAssets.avatarBackgroundColor))
|
||||
.background(Color(AppAssets.iconBackgroundColor))
|
||||
.frame(width: 48.0, height: 48.0)
|
||||
.cornerRadius(5.0)
|
||||
} else {
|
||||
Image(uiImage: self.viewModel.image)
|
||||
Image(uiImage: self.viewModel.iconImage.image)
|
||||
.resizable()
|
||||
.frame(width: 48.0, height: 48.0)
|
||||
.cornerRadius(5.0)
|
||||
@@ -88,7 +88,7 @@ struct FeedInspectorView : View {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
|
||||
}
|
||||
|
||||
var image: UIImage {
|
||||
var iconImage: IconImage {
|
||||
if let feedIcon = appDelegate.feedIconDownloader.icon(for: feed) {
|
||||
return feedIcon
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ class MasterFeedTableViewCell : VibrantTableViewCell {
|
||||
}
|
||||
}
|
||||
|
||||
var avatarImage: UIImage? {
|
||||
var iconImage: IconImage? {
|
||||
didSet {
|
||||
avatarView.image = avatarImage
|
||||
iconView.iconImage = iconImage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class MasterFeedTableViewCell : VibrantTableViewCell {
|
||||
return label
|
||||
}()
|
||||
|
||||
private let avatarView = AvatarView()
|
||||
private let iconView = IconView()
|
||||
|
||||
private let bottomSeparatorView: UIView = {
|
||||
let view = UIView()
|
||||
@@ -154,9 +154,9 @@ class MasterFeedTableViewCell : VibrantTableViewCell {
|
||||
|
||||
override func updateVibrancy(animated: Bool) {
|
||||
super.updateVibrancy(animated: animated)
|
||||
let avatarTintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor
|
||||
let iconTintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor
|
||||
UIView.animate(withDuration: duration(animated: animated)) {
|
||||
self.avatarView.tintColor = avatarTintColor
|
||||
self.iconView.tintColor = iconTintColor
|
||||
}
|
||||
updateLabelVibrancy(titleView, color: labelColor, animated: animated)
|
||||
}
|
||||
@@ -167,7 +167,7 @@ private extension MasterFeedTableViewCell {
|
||||
|
||||
func commonInit() {
|
||||
addSubviewAtInit(unreadCountView)
|
||||
addSubviewAtInit(avatarView)
|
||||
addSubviewAtInit(iconView)
|
||||
addSubviewAtInit(titleView)
|
||||
addDisclosureView()
|
||||
addSubviewAtInit(bottomSeparatorView)
|
||||
@@ -189,7 +189,7 @@ private extension MasterFeedTableViewCell {
|
||||
}
|
||||
|
||||
func layoutWith(_ layout: MasterFeedTableViewCellLayout) {
|
||||
avatarView.setFrameIfNotEqual(layout.faviconRect)
|
||||
iconView.setFrameIfNotEqual(layout.faviconRect)
|
||||
titleView.setFrameIfNotEqual(layout.titleRect)
|
||||
unreadCountView.setFrameIfNotEqual(layout.unreadCountRect)
|
||||
disclosureButton?.setFrameIfNotEqual(layout.disclosureButtonRect)
|
||||
|
||||
@@ -105,11 +105,11 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
}
|
||||
|
||||
@objc func faviconDidBecomeAvailable(_ note: Notification) {
|
||||
applyToAvailableCells(configureAvatar)
|
||||
applyToAvailableCells(configureIcon)
|
||||
}
|
||||
|
||||
@objc func feedIconDidBecomeAvailable(_ note: Notification) {
|
||||
applyToAvailableCells(configureAvatar)
|
||||
applyToAvailableCells(configureIcon)
|
||||
}
|
||||
|
||||
@objc func feedSettingDidChange(_ note: Notification) {
|
||||
@@ -640,7 +640,7 @@ private extension MasterFeedViewController {
|
||||
|
||||
cell.name = nameFor(node)
|
||||
cell.unreadCount = coordinator.unreadCountFor(node)
|
||||
configureAvatar(cell, node)
|
||||
configureIcon(cell, node)
|
||||
|
||||
guard let indexPath = dataSource.indexPath(for: node) else { return }
|
||||
let rowsInSection = tableView.numberOfRows(inSection: indexPath.section)
|
||||
@@ -652,11 +652,11 @@ private extension MasterFeedViewController {
|
||||
|
||||
}
|
||||
|
||||
func configureAvatar(_ cell: MasterFeedTableViewCell, _ node: Node) {
|
||||
cell.avatarImage = imageFor(node)
|
||||
func configureIcon(_ cell: MasterFeedTableViewCell, _ node: Node) {
|
||||
cell.iconImage = imageFor(node)
|
||||
}
|
||||
|
||||
func imageFor(_ node: Node) -> UIImage? {
|
||||
func imageFor(_ node: Node) -> IconImage? {
|
||||
if let feed = node.representedObject as? Feed {
|
||||
|
||||
let feedIconImage = appDelegate.feedIconDownloader.icon(for: feed)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -111,7 +111,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
|
||||
private(set) var currentFeedIndexPath: IndexPath?
|
||||
|
||||
var timelineAvatar: RSImage? {
|
||||
var timelineIconImage: IconImage? {
|
||||
if let feed = timelineFetcher as? Feed {
|
||||
|
||||
let feedIconImage = appDelegate.feedIconDownloader.icon(for: feed)
|
||||
@@ -119,8 +119,8 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
return feedIconImage
|
||||
}
|
||||
|
||||
if let faviconImage = appDelegate.faviconDownloader.faviconAsAvatar(for: feed) {
|
||||
return faviconImage
|
||||
if let faviconIconImage = appDelegate.faviconDownloader.faviconAsIcon(for: feed) {
|
||||
return faviconIconImage
|
||||
}
|
||||
|
||||
}
|
||||
@@ -154,7 +154,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
}
|
||||
|
||||
private(set) var showFeedNames = false
|
||||
private(set) var showAvatars = false
|
||||
private(set) var showIcons = false
|
||||
|
||||
var isPrevFeedAvailable: Bool {
|
||||
guard let indexPath = currentFeedIndexPath else {
|
||||
@@ -1087,10 +1087,10 @@ private extension SceneCoordinator {
|
||||
return indexPathFor(node)
|
||||
}
|
||||
|
||||
func updateShowAvatars() {
|
||||
func updateShowIcons() {
|
||||
|
||||
if showFeedNames {
|
||||
self.showAvatars = true
|
||||
self.showIcons = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1098,14 +1098,14 @@ private extension SceneCoordinator {
|
||||
if let authors = article.authors {
|
||||
for author in authors {
|
||||
if author.avatarURL != nil {
|
||||
self.showAvatars = true
|
||||
self.showIcons = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.showAvatars = false
|
||||
self.showIcons = false
|
||||
}
|
||||
|
||||
// MARK: Select Prev Unread
|
||||
@@ -1350,7 +1350,7 @@ private extension SceneCoordinator {
|
||||
if articles != sortedArticles {
|
||||
|
||||
articles = sortedArticles
|
||||
updateShowAvatars()
|
||||
updateShowIcons()
|
||||
updateUnreadCount()
|
||||
|
||||
masterTimelineViewController?.reloadArticles(animate: animate)
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
//
|
||||
// UIImage-Extensions.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 9/29/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension CGImage {
|
||||
|
||||
func isDark() -> Bool {
|
||||
guard let imageData = self.dataProvider?.data else { return false }
|
||||
guard let ptr = CFDataGetBytePtr(imageData) else { return false }
|
||||
|
||||
let length = CFDataGetLength(imageData)
|
||||
var pixelCount = 0
|
||||
var totalLuminance = 0.0
|
||||
|
||||
for i in stride(from: 0, to: length, by: 4) {
|
||||
|
||||
let r = ptr[i]
|
||||
let g = ptr[i + 1]
|
||||
let b = ptr[i + 2]
|
||||
let luminance = (0.299 * Double(r) + 0.587 * Double(g) + 0.114 * Double(b))
|
||||
|
||||
totalLuminance += luminance
|
||||
pixelCount += 1
|
||||
|
||||
}
|
||||
|
||||
let avgLuminance = totalLuminance / Double(pixelCount)
|
||||
return avgLuminance < 37.5
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UIImage {
|
||||
func isDark() -> Bool {
|
||||
return self.cgImage?.isDark() ?? false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user