NetNewsWire/iOS/Timeline/Cell/TimelineTableViewCell.swift

312 lines
8.6 KiB
Swift
Raw Normal View History

//
2024-02-26 17:37:15 +01:00
// TimelineTableViewCell.swift
// NetNewsWire
//
// Created by Brent Simmons on 8/31/15.
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
//
import UIKit
import UIKitExtras
import Images
2024-02-26 17:37:15 +01:00
class TimelineTableViewCell: VibrantTableViewCell {
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()
private lazy var iconView = IconView()
private lazy var starView = {
2019-04-26 13:30:00 +02:00
return NonIntrinsicImageView(image: AppAssets.timelineStarImage)
}()
private var unreadIndicatorPropertyAnimator: UIViewPropertyAnimator?
private var starViewPropertyAnimator: UIViewPropertyAnimator?
2024-02-26 17:37:15 +01:00
var cellData: TimelineCellData! {
didSet {
updateSubviews()
}
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func prepareForReuse() {
unreadIndicatorPropertyAnimator?.stopAnimation(true)
unreadIndicatorPropertyAnimator = nil
unreadIndicatorView.isHidden = true
starViewPropertyAnimator?.stopAnimation(true)
starViewPropertyAnimator = nil
starView.isHidden = true
}
override var frame: CGRect {
didSet {
setNeedsLayout()
}
}
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)
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-04-29 21:40:14 +02:00
override func sizeThatFits(_ size: CGSize) -> CGSize {
let layout = updatedLayout(width: size.width)
return CGSize(width: size.width, height: layout.height)
2019-04-29 21:40:14 +02:00
}
override func layoutSubviews() {
super.layoutSubviews()
let layout = updatedLayout(width: bounds.width)
unreadIndicatorView.setFrameIfNotEqual(layout.unreadIndicatorRect)
starView.setFrameIfNotEqual(layout.starRect)
iconView.setFrameIfNotEqual(layout.iconImageRect)
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
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func setIconImage(_ image: IconImage) {
iconView.iconImage = image
}
}
// MARK: - Private
2024-02-26 17:37:15 +01:00
private extension TimelineTableViewCell {
static func singleLineUILabel() -> UILabel {
let label = NonIntrinsicLabel()
label.lineBreakMode = .byTruncatingTail
label.allowsDefaultTighteningForTruncation = false
2019-04-29 21:40:14 +02:00
label.adjustsFontForContentSizeCategory = true
return label
}
static func multiLineUILabel() -> UILabel {
let label = NonIntrinsicLabel()
label.numberOfLines = 0
label.lineBreakMode = .byTruncatingTail
label.allowsDefaultTighteningForTruncation = false
2019-04-29 21:40:14 +02:00
label.adjustsFontForContentSizeCategory = true
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)
label.setFrameIfNotEqual(rect)
}
}
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)
addSubviewAtInit(iconView, hidden: true)
addSubviewAtInit(starView, hidden: true)
}
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
}
}
func updateTitleView() {
2024-02-26 17:37:15 +01:00
titleView.font = TimelineDefaultCellLayout.titleFont
titleView.textColor = labelColor
2020-04-30 18:53:10 +02:00
updateTextFieldAttributedText(titleView, cellData?.attributedTitle)
}
func updateSummaryView() {
2024-02-26 17:37:15 +01:00
summaryView.font = TimelineDefaultCellLayout.summaryFont
summaryView.textColor = labelColor
2019-04-29 21:40:14 +02:00
updateTextFieldText(summaryView, cellData?.summary)
}
func updateDateView() {
2024-02-26 17:37:15 +01:00
dateView.font = TimelineDefaultCellLayout.dateFont
dateView.textColor = secondaryLabelColor
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()
}
}
func updateFeedNameView() {
switch cellData.showFeedName {
case .feed:
showView(feedNameView)
2024-02-26 17:37:15 +01:00
feedNameView.font = TimelineDefaultCellLayout.feedNameFont
feedNameView.textColor = secondaryLabelColor
updateTextFieldText(feedNameView, cellData.feedName)
case .byline:
showView(feedNameView)
2024-02-26 17:37:15 +01:00
feedNameView.font = TimelineDefaultCellLayout.feedNameFont
feedNameView.textColor = secondaryLabelColor
updateTextFieldText(feedNameView, cellData.byline)
case .none:
hideView(feedNameView)
}
}
func updateUnreadIndicator() {
if !unreadIndicatorView.isHidden && cellData.read && !cellData.starred {
unreadIndicatorPropertyAnimator = UIViewPropertyAnimator(duration: 0.66, curve: .easeInOut) { [weak self] in
self?.unreadIndicatorView.alpha = 0
}
unreadIndicatorPropertyAnimator?.addCompletion { [weak self] _ in
self?.unreadIndicatorView.isHidden = true
self?.unreadIndicatorView.alpha = 1
self?.unreadIndicatorPropertyAnimator = nil
}
unreadIndicatorPropertyAnimator?.startAnimation()
} else {
unreadIndicatorView.alpha = 1
showOrHideView(unreadIndicatorView, cellData.read || cellData.starred)
}
}
func updateStarView() {
if !starView.isHidden && cellData.read && !cellData.starred {
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
}
starViewPropertyAnimator?.startAnimation()
} else {
starView.alpha = 1
showOrHideView(starView, !cellData.starred)
}
}
func updateIconImage() {
guard let image = cellData.iconImage, cellData.showIcon else {
makeIconEmpty()
return
}
showView(iconView)
if iconView.iconImage !== cellData.iconImage {
iconView.iconImage = image
setNeedsLayout()
}
}
func updateAccessiblityLabel() {
2020-07-14 18:34:20 +02:00
let starredStatus = cellData.starred ? "\(NSLocalizedString("Starred", comment: "Starred article for accessibility")), " : ""
let unreadStatus = cellData.read ? "" : "\(NSLocalizedString("Unread", comment: "Unread")), "
let label = starredStatus + unreadStatus + "\(cellData.feedName), \(cellData.title), \(cellData.summary), \(cellData.dateString)"
accessibilityLabel = label
}
func makeIconEmpty() {
if iconView.iconImage != nil {
iconView.iconImage = nil
setNeedsLayout()
}
hideView(iconView)
}
func hideView(_ view: UIView) {
if !view.isHidden {
view.isHidden = true
}
}
func showView(_ view: UIView) {
if view.isHidden {
view.isHidden = false
}
}
func showOrHideView(_ view: UIView, _ shouldHide: Bool) {
shouldHide ? hideView(view) : showView(view)
}
func updateSubviews() {
updateTitleView()
updateSummaryView()
updateDateView()
updateFeedNameView()
updateUnreadIndicator()
updateStarView()
updateIconImage()
updateAccessiblityLabel()
}
}