From 736e8283e1430f8f59d65ea920de8eb09f4da40f Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 10 Jan 2022 22:36:54 -0500 Subject: [PATCH] Mark items as read --- .../Screens/Home/HomeCollectionViewCell.swift | 5 +- .../Items/ItemCollectionViewCell.swift | 81 ++++++++++++++++++- .../Screens/Items/ItemsViewController.swift | 14 ++-- Reader/UIColor+App.swift | 11 +++ 4 files changed, 98 insertions(+), 13 deletions(-) diff --git a/Reader/Screens/Home/HomeCollectionViewCell.swift b/Reader/Screens/Home/HomeCollectionViewCell.swift index 35e65ab..71c022d 100644 --- a/Reader/Screens/Home/HomeCollectionViewCell.swift +++ b/Reader/Screens/Home/HomeCollectionViewCell.swift @@ -11,10 +11,11 @@ class HomeCollectionViewCell: UICollectionViewListCell { override func updateConfiguration(using state: UICellConfigurationState) { var backgroundConfig = UIBackgroundConfiguration.listGroupedCell().updated(for: state) - if !state.isHighlighted && !state.isSelected { + if state.isHighlighted || state.isSelected { + backgroundConfig.backgroundColor = .appCellHighlightBackground + } else { backgroundConfig.backgroundColor = .appBackground } - // todo: this breaks the deselection animation self.backgroundConfiguration = backgroundConfig } diff --git a/Reader/Screens/Items/ItemCollectionViewCell.swift b/Reader/Screens/Items/ItemCollectionViewCell.swift index 38659f6..e6100d3 100644 --- a/Reader/Screens/Items/ItemCollectionViewCell.swift +++ b/Reader/Screens/Items/ItemCollectionViewCell.swift @@ -8,11 +8,20 @@ import UIKit import SwiftSoup +protocol ItemCollectionViewCellDelegate: AnyObject { + func itemCellSelected(item: Item) +} + class ItemCollectionViewCell: UICollectionViewListCell { + weak var delegate: ItemCollectionViewCellDelegate? + private let titleLabel = UILabel() private let feedTitleLabel = UILabel() private let contentLabel = UILabel() + private var shouldHighlight = true + + private var item: Item! override init(frame: CGRect) { super.init(frame: frame) @@ -24,10 +33,8 @@ class ItemCollectionViewCell: UICollectionViewListCell { titleLabel.numberOfLines = 0 feedTitleLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .subheadline), size: 0) - feedTitleLabel.textColor = .tintColor contentLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).withDesign(.serif)!, size: 0) - contentLabel.textColor = .appContentPreviewLabel contentLabel.numberOfLines = 0 let stack = UIStackView(arrangedSubviews: [ @@ -46,6 +53,14 @@ class ItemCollectionViewCell: UICollectionViewListCell { stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8), separatorLayoutGuide.leadingAnchor.constraint(equalTo: stack.leadingAnchor), ]) + + let doubleTap = DoubleTapRecognizer(target: self, action: #selector(cellDoubleTapped)) + doubleTap.onTouchesBegan = highlightCell + addGestureRecognizer(doubleTap) + + let singleTap = UITapGestureRecognizer(target: self, action: #selector(cellSingleTapped)) + singleTap.require(toFail: doubleTap) + addGestureRecognizer(singleTap) } required init?(coder: NSCoder) { @@ -53,6 +68,8 @@ class ItemCollectionViewCell: UICollectionViewListCell { } func updateUI(item: Item) { + self.item = item + titleLabel.text = item.title feedTitleLabel.text = item.feed!.title ?? item.feed!.url?.host if let content = item.content { @@ -62,6 +79,66 @@ class ItemCollectionViewCell: UICollectionViewListCell { contentLabel.text = "" } contentLabel.isHidden = contentLabel.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true + + setColors() + } + + private func setColors() { + if item.read { + titleLabel.textColor = .secondaryLabel + feedTitleLabel.textColor = .secondaryLabel + contentLabel.textColor = .secondaryLabel + } else { + titleLabel.textColor = .label + feedTitleLabel.textColor = .tintColor + contentLabel.textColor = .appContentPreviewLabel + } + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + highlightCell() + } + + @objc private func highlightCell() { + guard shouldHighlight else { return } + shouldHighlight = false + UIView.animateKeyframes(withDuration: 0.4, delay: 0, options: .allowUserInteraction) { + UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) { + self.backgroundColor = .appCellHighlightBackground + } + UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) { + self.backgroundColor = nil + } + } + } + + @objc private func cellDoubleTapped() { + self.item.read = !self.item.read + // i don't know why .transition works but .animate doesn't + UIView.transition(with: self, duration: 0.2, options: .transitionCrossDissolve) { + self.setColors() + } + shouldHighlight = true + } + + @objc private func cellSingleTapped() { + delegate?.itemCellSelected(item: item) + shouldHighlight = true } } + +private class DoubleTapRecognizer: UITapGestureRecognizer { + var onTouchesBegan: (() -> Void)! + + override init(target: Any?, action: Selector?) { + super.init(target: target, action: action) + numberOfTapsRequired = 2 + delaysTouchesBegan = true + } + + override func touchesBegan(_ touches: Set, with event: UIEvent) { + super.touchesBegan(touches, with: event) + onTouchesBegan() + } +} diff --git a/Reader/Screens/Items/ItemsViewController.swift b/Reader/Screens/Items/ItemsViewController.swift index ab1bfef..b70d6d0 100644 --- a/Reader/Screens/Items/ItemsViewController.swift +++ b/Reader/Screens/Items/ItemsViewController.swift @@ -35,7 +35,6 @@ class ItemsViewController: UIViewController { configuration.backgroundColor = .appBackground let layout = UICollectionViewCompositionalLayout.list(using: configuration) collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) - collectionView.delegate = self collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] collectionView.register(ItemCollectionViewCell.self, forCellWithReuseIdentifier: "itemCell") view.addSubview(collectionView) @@ -53,8 +52,9 @@ class ItemsViewController: UIViewController { } private func createDataSource() -> UICollectionViewDiffableDataSource { - let itemCell = UICollectionView.CellRegistration { cell, indexPath, item in + let itemCell = UICollectionView.CellRegistration { [unowned self] cell, indexPath, item in cell.updateUI(item: item) + cell.delegate = self } let dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item in return collectionView.dequeueConfiguredReusableCell(using: itemCell, for: indexPath, item: item) @@ -80,12 +80,8 @@ extension ItemsViewController: NSFetchedResultsControllerDelegate { } } -extension ItemsViewController: UICollectionViewDelegate { - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard let item = dataSource.itemIdentifier(for: indexPath) else { - return - } - let vc = ReadViewController(item: item, fervorController: fervorController) - show(vc, sender: nil) +extension ItemsViewController: ItemCollectionViewCellDelegate { + func itemCellSelected(item: Item) { + show(ReadViewController(item: item, fervorController: fervorController), sender: nil) } } diff --git a/Reader/UIColor+App.swift b/Reader/UIColor+App.swift index b33e8be..8b1c6af 100644 --- a/Reader/UIColor+App.swift +++ b/Reader/UIColor+App.swift @@ -20,6 +20,17 @@ extension UIColor { } } + static let appCellHighlightBackground = UIColor { traitCollection in + switch traitCollection.userInterfaceStyle { + case .dark: + return UIColor(white: 0.15, alpha: 1) + case .unspecified, .light: + fallthrough + @unknown default: + return UIColor(white: 0.9, alpha: 1) + } + } + static let appContentPreviewLabel = UIColor { traitCollection in switch traitCollection.userInterfaceStyle { case .dark: