Mark items as read
This commit is contained in:
parent
96255b2a1f
commit
736e8283e1
|
@ -11,10 +11,11 @@ class HomeCollectionViewCell: UICollectionViewListCell {
|
||||||
|
|
||||||
override func updateConfiguration(using state: UICellConfigurationState) {
|
override func updateConfiguration(using state: UICellConfigurationState) {
|
||||||
var backgroundConfig = UIBackgroundConfiguration.listGroupedCell().updated(for: state)
|
var backgroundConfig = UIBackgroundConfiguration.listGroupedCell().updated(for: state)
|
||||||
if !state.isHighlighted && !state.isSelected {
|
if state.isHighlighted || state.isSelected {
|
||||||
|
backgroundConfig.backgroundColor = .appCellHighlightBackground
|
||||||
|
} else {
|
||||||
backgroundConfig.backgroundColor = .appBackground
|
backgroundConfig.backgroundColor = .appBackground
|
||||||
}
|
}
|
||||||
// todo: this breaks the deselection animation
|
|
||||||
self.backgroundConfiguration = backgroundConfig
|
self.backgroundConfiguration = backgroundConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,20 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import SwiftSoup
|
import SwiftSoup
|
||||||
|
|
||||||
|
protocol ItemCollectionViewCellDelegate: AnyObject {
|
||||||
|
func itemCellSelected(item: Item)
|
||||||
|
}
|
||||||
|
|
||||||
class ItemCollectionViewCell: UICollectionViewListCell {
|
class ItemCollectionViewCell: UICollectionViewListCell {
|
||||||
|
|
||||||
|
weak var delegate: ItemCollectionViewCellDelegate?
|
||||||
|
|
||||||
private let titleLabel = UILabel()
|
private let titleLabel = UILabel()
|
||||||
private let feedTitleLabel = UILabel()
|
private let feedTitleLabel = UILabel()
|
||||||
private let contentLabel = UILabel()
|
private let contentLabel = UILabel()
|
||||||
|
private var shouldHighlight = true
|
||||||
|
|
||||||
|
private var item: Item!
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
@ -24,10 +33,8 @@ class ItemCollectionViewCell: UICollectionViewListCell {
|
||||||
titleLabel.numberOfLines = 0
|
titleLabel.numberOfLines = 0
|
||||||
|
|
||||||
feedTitleLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .subheadline), size: 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.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).withDesign(.serif)!, size: 0)
|
||||||
contentLabel.textColor = .appContentPreviewLabel
|
|
||||||
contentLabel.numberOfLines = 0
|
contentLabel.numberOfLines = 0
|
||||||
|
|
||||||
let stack = UIStackView(arrangedSubviews: [
|
let stack = UIStackView(arrangedSubviews: [
|
||||||
|
@ -46,6 +53,14 @@ class ItemCollectionViewCell: UICollectionViewListCell {
|
||||||
stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
|
stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
|
||||||
separatorLayoutGuide.leadingAnchor.constraint(equalTo: stack.leadingAnchor),
|
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) {
|
required init?(coder: NSCoder) {
|
||||||
|
@ -53,6 +68,8 @@ class ItemCollectionViewCell: UICollectionViewListCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI(item: Item) {
|
func updateUI(item: Item) {
|
||||||
|
self.item = item
|
||||||
|
|
||||||
titleLabel.text = item.title
|
titleLabel.text = item.title
|
||||||
feedTitleLabel.text = item.feed!.title ?? item.feed!.url?.host
|
feedTitleLabel.text = item.feed!.title ?? item.feed!.url?.host
|
||||||
if let content = item.content {
|
if let content = item.content {
|
||||||
|
@ -62,6 +79,66 @@ class ItemCollectionViewCell: UICollectionViewListCell {
|
||||||
contentLabel.text = ""
|
contentLabel.text = ""
|
||||||
}
|
}
|
||||||
contentLabel.isHidden = contentLabel.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true
|
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<UITouch>, 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<UITouch>, with event: UIEvent) {
|
||||||
|
super.touchesBegan(touches, with: event)
|
||||||
|
onTouchesBegan()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ class ItemsViewController: UIViewController {
|
||||||
configuration.backgroundColor = .appBackground
|
configuration.backgroundColor = .appBackground
|
||||||
let layout = UICollectionViewCompositionalLayout.list(using: configuration)
|
let layout = UICollectionViewCompositionalLayout.list(using: configuration)
|
||||||
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
|
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
|
||||||
collectionView.delegate = self
|
|
||||||
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
collectionView.register(ItemCollectionViewCell.self, forCellWithReuseIdentifier: "itemCell")
|
collectionView.register(ItemCollectionViewCell.self, forCellWithReuseIdentifier: "itemCell")
|
||||||
view.addSubview(collectionView)
|
view.addSubview(collectionView)
|
||||||
|
@ -53,8 +52,9 @@ class ItemsViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||||
let itemCell = UICollectionView.CellRegistration<ItemCollectionViewCell, Item> { cell, indexPath, item in
|
let itemCell = UICollectionView.CellRegistration<ItemCollectionViewCell, Item> { [unowned self] cell, indexPath, item in
|
||||||
cell.updateUI(item: item)
|
cell.updateUI(item: item)
|
||||||
|
cell.delegate = self
|
||||||
}
|
}
|
||||||
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
|
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
|
||||||
return collectionView.dequeueConfiguredReusableCell(using: itemCell, for: indexPath, item: item)
|
return collectionView.dequeueConfiguredReusableCell(using: itemCell, for: indexPath, item: item)
|
||||||
|
@ -80,12 +80,8 @@ extension ItemsViewController: NSFetchedResultsControllerDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ItemsViewController: UICollectionViewDelegate {
|
extension ItemsViewController: ItemCollectionViewCellDelegate {
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
func itemCellSelected(item: Item) {
|
||||||
guard let item = dataSource.itemIdentifier(for: indexPath) else {
|
show(ReadViewController(item: item, fervorController: fervorController), sender: nil)
|
||||||
return
|
|
||||||
}
|
|
||||||
let vc = ReadViewController(item: item, fervorController: fervorController)
|
|
||||||
show(vc, sender: nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
static let appContentPreviewLabel = UIColor { traitCollection in
|
||||||
switch traitCollection.userInterfaceStyle {
|
switch traitCollection.userInterfaceStyle {
|
||||||
case .dark:
|
case .dark:
|
||||||
|
|
Loading…
Reference in New Issue