// // ItemsViewController.swift // Reader // // Created by Shadowfacts on 1/9/22. // import UIKit import CoreData class ItemsViewController: UIViewController { let fervorController: FervorController let fetchRequest: NSFetchRequest private var collectionView: UICollectionView! private var dataSource: UICollectionViewDiffableDataSource! private var resultsController: NSFetchedResultsController! init(fetchRequest: NSFetchRequest, fervorController: FervorController) { self.fervorController = fervorController self.fetchRequest = fetchRequest super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() var configuration = UICollectionLayoutListConfiguration(appearance: .plain) 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) dataSource = createDataSource() var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.items]) dataSource.apply(snapshot, animatingDifferences: false) fetchRequest.sortDescriptors = [NSSortDescriptor(key: "published", ascending: false)] resultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: fervorController.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil) resultsController.delegate = self try! resultsController.performFetch() } private func createDataSource() -> UICollectionViewDiffableDataSource { 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) } return dataSource } } extension ItemsViewController { enum Section: Hashable { case items } } extension ItemsViewController: NSFetchedResultsControllerDelegate { func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) { var snapshot = self.dataSource.snapshot() snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .items)) // use resultsController here instead of controller so we don't have to cast snapshot.appendItems(resultsController.fetchedObjects!, toSection: .items) self.dataSource.apply(snapshot, animatingDifferences: false) } } extension ItemsViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { guard let item = dataSource.itemIdentifier(for: indexPath) else { return nil } return UIContextMenuConfiguration(identifier: nil, previewProvider: { ReadViewController(item: item, fervorController: self.fervorController) }, actionProvider: nil) } func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { guard let vc = animator.previewViewController else { return } animator.preferredCommitStyle = .pop animator.addCompletion { self.show(vc, sender: nil) } } } extension ItemsViewController: ItemCollectionViewCellDelegate { func itemCellSelected(cell: ItemCollectionViewCell, item: Item) { cell.setRead(true, animated: true) show(ReadViewController(item: item, fervorController: fervorController), sender: nil) } }