2019-04-15 22:03:05 +02:00
|
|
|
//
|
2024-02-26 17:37:15 +01:00
|
|
|
// TimelineTableViewCell.swift
|
2019-04-15 22:03:05 +02:00
|
|
|
// NetNewsWire
|
|
|
|
//
|
|
|
|
// Created by Brent Simmons on 8/31/15.
|
|
|
|
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
2024-05-07 07:26:11 +02:00
|
|
|
import UIKitExtras
|
2024-04-16 07:21:17 +02:00
|
|
|
import Images
|
2019-04-15 22:03:05 +02:00
|
|
|
|
2024-02-26 17:37:15 +01:00
|
|
|
class TimelineTableViewCell: VibrantTableViewCell {
|
2019-04-15 22:03:05 +02:00
|
|
|
|
2024-02-26 17:37:15 +01:00
|
|
|
private let titleView = TimelineTableViewCell.multiLineUILabel()
|
|
|
|
private let summaryView = TimelineTableViewCell.multiLineUILabel()
|
|
|
|
private let unreadIndicatorView = UnreadIndicatorView(frame: CGRect.zero)
|
|
|
|
private let dateView = TimelineTableViewCell.singleLineUILabel()
|
|
|
|
private let feedNameView = TimelineTableViewCell.singleLineUILabel()
|
2019-04-15 22:03:05 +02:00
|
|
|
|
2019-11-06 01:05:57 +01:00
|
|
|
private lazy var iconView = IconView()
|
2019-04-15 22:03:05 +02:00
|
|
|
|
|
|
|
private lazy var starView = {
|
2019-04-26 13:30:00 +02:00
|
|
|
return NonIntrinsicImageView(image: AppAssets.timelineStarImage)
|
2019-04-15 22:03:05 +02:00
|
|
|
}()
|
|
|
|
|
2020-06-19 00:37:29 +02:00
|
|
|
private var unreadIndicatorPropertyAnimator: UIViewPropertyAnimator?
|
|
|
|
private var starViewPropertyAnimator: UIViewPropertyAnimator?
|
|
|
|
|
2024-02-26 17:37:15 +01:00
|
|
|
var cellData: TimelineCellData! {
|
2019-04-15 22:03:05 +02:00
|
|
|
didSet {
|
|
|
|
updateSubviews()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
super.init(coder: coder)
|
|
|
|
commonInit()
|
|
|
|
}
|
|
|
|
|
2020-01-28 06:26:49 +01:00
|
|
|
override func prepareForReuse() {
|
2020-06-19 00:37:29 +02:00
|
|
|
unreadIndicatorPropertyAnimator?.stopAnimation(true)
|
|
|
|
unreadIndicatorPropertyAnimator = nil
|
2020-01-28 06:26:49 +01:00
|
|
|
unreadIndicatorView.isHidden = true
|
2020-06-19 00:37:29 +02:00
|
|
|
|
|
|
|
starViewPropertyAnimator?.stopAnimation(true)
|
|
|
|
starViewPropertyAnimator = nil
|
2020-01-28 06:26:49 +01:00
|
|
|
starView.isHidden = true
|
|
|
|
}
|
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
override var frame: CGRect {
|
|
|
|
didSet {
|
|
|
|
setNeedsLayout()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-03 01:57:01 +01:00
|
|
|
override func updateVibrancy(animated: Bool) {
|
2019-11-04 18:47:44 +01:00
|
|
|
updateLabelVibrancy(titleView, color: labelColor, animated: animated)
|
|
|
|
updateLabelVibrancy(summaryView, color: labelColor, animated: animated)
|
|
|
|
updateLabelVibrancy(dateView, color: secondaryLabelColor, animated: animated)
|
|
|
|
updateLabelVibrancy(feedNameView, color: secondaryLabelColor, animated: animated)
|
2019-11-03 01:57:01 +01:00
|
|
|
|
2020-10-23 01:56:33 +02:00
|
|
|
if animated {
|
|
|
|
UIView.animate(withDuration: Self.duration) {
|
|
|
|
if self.isHighlighted || self.isSelected {
|
|
|
|
self.unreadIndicatorView.backgroundColor = AppAssets.vibrantTextColor
|
|
|
|
} else {
|
|
|
|
self.unreadIndicatorView.backgroundColor = AppAssets.secondaryAccentColor
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-11-03 02:15:48 +01:00
|
|
|
if self.isHighlighted || self.isSelected {
|
|
|
|
self.unreadIndicatorView.backgroundColor = AppAssets.vibrantTextColor
|
|
|
|
} else {
|
|
|
|
self.unreadIndicatorView.backgroundColor = AppAssets.secondaryAccentColor
|
|
|
|
}
|
2019-11-03 01:57:01 +01:00
|
|
|
}
|
2019-08-03 23:25:35 +02:00
|
|
|
}
|
2019-11-03 01:57:01 +01:00
|
|
|
|
2019-04-29 21:40:14 +02:00
|
|
|
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
2019-05-31 23:59:02 +02:00
|
|
|
let layout = updatedLayout(width: size.width)
|
|
|
|
return CGSize(width: size.width, height: layout.height)
|
2019-04-29 21:40:14 +02:00
|
|
|
}
|
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
override func layoutSubviews() {
|
|
|
|
|
|
|
|
super.layoutSubviews()
|
|
|
|
|
2019-05-31 23:59:02 +02:00
|
|
|
let layout = updatedLayout(width: bounds.width)
|
2019-04-30 00:45:12 +02:00
|
|
|
|
2019-05-31 23:59:02 +02:00
|
|
|
unreadIndicatorView.setFrameIfNotEqual(layout.unreadIndicatorRect)
|
|
|
|
starView.setFrameIfNotEqual(layout.starRect)
|
2019-11-06 01:05:57 +01:00
|
|
|
iconView.setFrameIfNotEqual(layout.iconImageRect)
|
2019-05-31 23:59:02 +02:00
|
|
|
setFrame(for: titleView, rect: layout.titleRect)
|
|
|
|
setFrame(for: summaryView, rect: layout.summaryRect)
|
|
|
|
feedNameView.setFrameIfNotEqual(layout.feedNameRect)
|
|
|
|
dateView.setFrameIfNotEqual(layout.dateRect)
|
2019-04-29 21:40:14 +02:00
|
|
|
|
2020-03-20 12:58:11 +01:00
|
|
|
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
2019-11-06 01:05:57 +01:00
|
|
|
func setIconImage(_ image: IconImage) {
|
|
|
|
iconView.iconImage = image
|
2019-08-26 22:37:15 +02:00
|
|
|
}
|
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Private
|
|
|
|
|
2024-02-26 17:37:15 +01:00
|
|
|
private extension TimelineTableViewCell {
|
2019-04-15 22:03:05 +02:00
|
|
|
|
|
|
|
static func singleLineUILabel() -> UILabel {
|
2019-04-22 18:49:22 +02:00
|
|
|
let label = NonIntrinsicLabel()
|
2019-04-15 22:03:05 +02:00
|
|
|
label.lineBreakMode = .byTruncatingTail
|
|
|
|
label.allowsDefaultTighteningForTruncation = false
|
2019-04-29 21:40:14 +02:00
|
|
|
label.adjustsFontForContentSizeCategory = true
|
2019-04-15 22:03:05 +02:00
|
|
|
return label
|
|
|
|
}
|
|
|
|
|
|
|
|
static func multiLineUILabel() -> UILabel {
|
2019-04-22 18:49:22 +02:00
|
|
|
let label = NonIntrinsicLabel()
|
2019-04-15 22:03:05 +02:00
|
|
|
label.numberOfLines = 0
|
2019-09-29 21:07:33 +02:00
|
|
|
label.lineBreakMode = .byTruncatingTail
|
2019-04-15 22:03:05 +02:00
|
|
|
label.allowsDefaultTighteningForTruncation = false
|
2019-04-29 21:40:14 +02:00
|
|
|
label.adjustsFontForContentSizeCategory = true
|
2019-04-15 22:03:05 +02:00
|
|
|
return label
|
|
|
|
}
|
|
|
|
|
|
|
|
func setFrame(for label: UILabel, rect: CGRect) {
|
|
|
|
|
|
|
|
if Int(floor(rect.height)) == 0 || Int(floor(rect.width)) == 0 {
|
|
|
|
hideView(label)
|
|
|
|
} else {
|
|
|
|
showView(label)
|
2019-04-20 16:50:44 +02:00
|
|
|
label.setFrameIfNotEqual(rect)
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func addSubviewAtInit(_ view: UIView, hidden: Bool) {
|
|
|
|
addSubview(view)
|
|
|
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
view.isHidden = hidden
|
|
|
|
}
|
|
|
|
|
|
|
|
func commonInit() {
|
|
|
|
|
|
|
|
addSubviewAtInit(titleView, hidden: false)
|
|
|
|
addSubviewAtInit(summaryView, hidden: true)
|
|
|
|
addSubviewAtInit(unreadIndicatorView, hidden: true)
|
|
|
|
addSubviewAtInit(dateView, hidden: false)
|
|
|
|
addSubviewAtInit(feedNameView, hidden: true)
|
2019-11-06 01:05:57 +01:00
|
|
|
addSubviewAtInit(iconView, hidden: true)
|
2019-04-15 22:03:05 +02:00
|
|
|
addSubviewAtInit(starView, hidden: true)
|
|
|
|
}
|
2019-04-21 00:12:39 +02:00
|
|
|
|
2024-02-26 17:37:15 +01:00
|
|
|
func updatedLayout(width: CGFloat) -> TimelineCellLayout {
|
2019-04-30 00:19:08 +02:00
|
|
|
if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory {
|
2024-02-26 17:37:15 +01:00
|
|
|
return TimelineAccessibilityCellLayout(width: width, insets: safeAreaInsets, cellData: cellData)
|
2019-04-30 00:19:08 +02:00
|
|
|
} else {
|
2024-02-26 17:37:15 +01:00
|
|
|
return TimelineDefaultCellLayout(width: width, insets: safeAreaInsets, cellData: cellData)
|
2019-04-30 00:19:08 +02:00
|
|
|
}
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func updateTitleView() {
|
2024-02-26 17:37:15 +01:00
|
|
|
titleView.font = TimelineDefaultCellLayout.titleFont
|
2019-11-03 23:34:57 +01:00
|
|
|
titleView.textColor = labelColor
|
2020-04-30 18:53:10 +02:00
|
|
|
updateTextFieldAttributedText(titleView, cellData?.attributedTitle)
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func updateSummaryView() {
|
2024-02-26 17:37:15 +01:00
|
|
|
summaryView.font = TimelineDefaultCellLayout.summaryFont
|
2019-11-03 23:34:57 +01:00
|
|
|
summaryView.textColor = labelColor
|
2019-04-29 21:40:14 +02:00
|
|
|
updateTextFieldText(summaryView, cellData?.summary)
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func updateDateView() {
|
2024-02-26 17:37:15 +01:00
|
|
|
dateView.font = TimelineDefaultCellLayout.dateFont
|
2019-11-03 23:34:57 +01:00
|
|
|
dateView.textColor = secondaryLabelColor
|
2019-04-15 22:03:05 +02:00
|
|
|
updateTextFieldText(dateView, cellData.dateString)
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateTextFieldText(_ label: UILabel, _ text: String?) {
|
|
|
|
let s = text ?? ""
|
|
|
|
if label.text != s {
|
|
|
|
label.text = s
|
|
|
|
setNeedsLayout()
|
|
|
|
}
|
|
|
|
}
|
2020-04-30 18:53:10 +02:00
|
|
|
|
|
|
|
func updateTextFieldAttributedText(_ label: UILabel, _ text: NSAttributedString?) {
|
|
|
|
var s = text ?? NSAttributedString(string: "")
|
|
|
|
|
2020-05-01 08:58:22 +02:00
|
|
|
if let fieldFont = label.font {
|
|
|
|
s = s.adding(font: fieldFont)
|
2020-04-30 18:53:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if label.attributedText != s {
|
|
|
|
label.attributedText = s
|
|
|
|
setNeedsLayout()
|
|
|
|
}
|
|
|
|
}
|
2019-04-15 22:03:05 +02:00
|
|
|
|
|
|
|
func updateFeedNameView() {
|
2020-04-18 14:53:56 +02:00
|
|
|
switch cellData.showFeedName {
|
|
|
|
case .feed:
|
2019-04-15 22:03:05 +02:00
|
|
|
showView(feedNameView)
|
2024-02-26 17:37:15 +01:00
|
|
|
feedNameView.font = TimelineDefaultCellLayout.feedNameFont
|
2019-11-03 23:34:57 +01:00
|
|
|
feedNameView.textColor = secondaryLabelColor
|
2019-04-15 22:03:05 +02:00
|
|
|
updateTextFieldText(feedNameView, cellData.feedName)
|
2020-04-18 14:53:56 +02:00
|
|
|
case .byline:
|
|
|
|
showView(feedNameView)
|
2024-02-26 17:37:15 +01:00
|
|
|
feedNameView.font = TimelineDefaultCellLayout.feedNameFont
|
2020-04-18 14:53:56 +02:00
|
|
|
feedNameView.textColor = secondaryLabelColor
|
|
|
|
updateTextFieldText(feedNameView, cellData.byline)
|
|
|
|
case .none:
|
2019-04-15 22:03:05 +02:00
|
|
|
hideView(feedNameView)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateUnreadIndicator() {
|
2020-01-21 19:41:03 +01:00
|
|
|
if !unreadIndicatorView.isHidden && cellData.read && !cellData.starred {
|
2020-06-19 00:37:29 +02:00
|
|
|
unreadIndicatorPropertyAnimator = UIViewPropertyAnimator(duration: 0.66, curve: .easeInOut) { [weak self] in
|
|
|
|
self?.unreadIndicatorView.alpha = 0
|
2020-01-21 19:41:03 +01:00
|
|
|
}
|
2020-06-19 00:37:29 +02:00
|
|
|
unreadIndicatorPropertyAnimator?.addCompletion { [weak self] _ in
|
|
|
|
self?.unreadIndicatorView.isHidden = true
|
|
|
|
self?.unreadIndicatorView.alpha = 1
|
|
|
|
self?.unreadIndicatorPropertyAnimator = nil
|
|
|
|
}
|
|
|
|
unreadIndicatorPropertyAnimator?.startAnimation()
|
2020-01-21 19:41:03 +01:00
|
|
|
} else {
|
2021-06-23 10:24:23 +02:00
|
|
|
unreadIndicatorView.alpha = 1
|
2020-01-21 19:41:03 +01:00
|
|
|
showOrHideView(unreadIndicatorView, cellData.read || cellData.starred)
|
|
|
|
}
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func updateStarView() {
|
2021-06-23 10:24:23 +02:00
|
|
|
if !starView.isHidden && cellData.read && !cellData.starred {
|
2020-06-19 00:37:29 +02:00
|
|
|
starViewPropertyAnimator = UIViewPropertyAnimator(duration: 0.66, curve: .easeInOut) { [weak self] in
|
|
|
|
self?.starView.alpha = 0
|
|
|
|
}
|
|
|
|
starViewPropertyAnimator?.addCompletion { [weak self] _ in
|
|
|
|
self?.starView.isHidden = true
|
|
|
|
self?.starView.alpha = 1
|
|
|
|
self?.starViewPropertyAnimator = nil
|
2020-01-21 19:41:03 +01:00
|
|
|
}
|
2020-06-19 00:37:29 +02:00
|
|
|
starViewPropertyAnimator?.startAnimation()
|
2020-01-21 19:41:03 +01:00
|
|
|
} else {
|
2021-06-23 10:24:23 +02:00
|
|
|
starView.alpha = 1
|
2020-01-21 19:41:03 +01:00
|
|
|
showOrHideView(starView, !cellData.starred)
|
|
|
|
}
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
2019-11-06 01:05:57 +01:00
|
|
|
func updateIconImage() {
|
|
|
|
guard let image = cellData.iconImage, cellData.showIcon else {
|
|
|
|
makeIconEmpty()
|
2019-04-15 22:03:05 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-11-06 01:05:57 +01:00
|
|
|
showView(iconView)
|
2019-04-15 22:03:05 +02:00
|
|
|
|
2019-11-06 01:05:57 +01:00
|
|
|
if iconView.iconImage !== cellData.iconImage {
|
|
|
|
iconView.iconImage = image
|
2019-04-15 22:03:05 +02:00
|
|
|
setNeedsLayout()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 01:54:13 +01:00
|
|
|
func updateAccessiblityLabel() {
|
2020-07-14 18:34:20 +02:00
|
|
|
let starredStatus = cellData.starred ? "\(NSLocalizedString("Starred", comment: "Starred article for accessibility")), " : ""
|
2020-07-14 14:38:07 +02:00
|
|
|
let unreadStatus = cellData.read ? "" : "\(NSLocalizedString("Unread", comment: "Unread")), "
|
|
|
|
let label = starredStatus + unreadStatus + "\(cellData.feedName), \(cellData.title), \(cellData.summary), \(cellData.dateString)"
|
2020-06-20 18:05:54 +02:00
|
|
|
accessibilityLabel = label
|
2020-01-10 01:54:13 +01:00
|
|
|
}
|
|
|
|
|
2019-11-06 01:05:57 +01:00
|
|
|
func makeIconEmpty() {
|
|
|
|
if iconView.iconImage != nil {
|
|
|
|
iconView.iconImage = nil
|
2019-04-15 22:03:05 +02:00
|
|
|
setNeedsLayout()
|
|
|
|
}
|
2019-11-06 01:05:57 +01:00
|
|
|
hideView(iconView)
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func hideView(_ view: UIView) {
|
|
|
|
if !view.isHidden {
|
|
|
|
view.isHidden = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func showView(_ view: UIView) {
|
|
|
|
if view.isHidden {
|
|
|
|
view.isHidden = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-23 18:08:34 +02:00
|
|
|
func showOrHideView(_ view: UIView, _ shouldHide: Bool) {
|
|
|
|
shouldHide ? hideView(view) : showView(view)
|
|
|
|
}
|
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
func updateSubviews() {
|
|
|
|
updateTitleView()
|
|
|
|
updateSummaryView()
|
|
|
|
updateDateView()
|
|
|
|
updateFeedNameView()
|
|
|
|
updateUnreadIndicator()
|
|
|
|
updateStarView()
|
2019-11-06 01:05:57 +01:00
|
|
|
updateIconImage()
|
2020-01-10 01:54:13 +01:00
|
|
|
updateAccessiblityLabel()
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|