frenzy-ios/Reader/Screens/Items/ItemCollectionViewCell.swift

145 lines
4.9 KiB
Swift

//
// ItemCollectionViewCell.swift
// Reader
//
// Created by Shadowfacts on 1/9/22.
//
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)
backgroundConfiguration = .clear()
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .title3).withSymbolicTraits(.traitBold)!.withDesign(.serif)!
titleLabel.font = UIFont(descriptor: descriptor, size: 0)
titleLabel.numberOfLines = 0
feedTitleLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .subheadline), size: 0)
contentLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).withDesign(.serif)!, size: 0)
contentLabel.numberOfLines = 0
let stack = UIStackView(arrangedSubviews: [
titleLabel,
feedTitleLabel,
contentLabel,
])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.spacing = 8
stack.axis = .vertical
addSubview(stack)
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
stack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
stack.topAnchor.constraint(equalTo: topAnchor, constant: 8),
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) {
fatalError("init(coder:) has not been implemented")
}
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 {
let doc = try! SwiftSoup.parse(content)
contentLabel.text = try! doc.select("p").first()?.text()
} else {
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<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()
}
}