From 057455806586f42680ec416210fba4885e1a9d40 Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:49:09 +0100 Subject: [PATCH 01/12] bumped to ios13+ collectionview diffabledatasource and snapshot These are more modern approaches to managing a collectionView's contents. As before, the collection shows a single section and merely displays all of the filtered goals in order. --- BeeSwift/Gallery/GalleryViewController.swift | 61 +++++++++++++------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 4f8bd3f9..1b018034 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -18,7 +18,7 @@ import CoreData import BeeKit -class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate, UICollectionViewDataSource, UISearchBarDelegate, SFSafariViewControllerDelegate { +class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate, UISearchBarDelegate, SFSafariViewControllerDelegate { let logger = Logger(subsystem: "com.beeminder.beeminder", category: "GalleryViewController") // Dependencies @@ -44,6 +44,8 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou var goals : [Goal] = [] var filteredGoals : [Goal] = [] + var dataSource: UICollectionViewDiffableDataSource! + public enum NotificationName { public static let openGoal = Notification.Name(rawValue: "com.beeminder.openGoal") } @@ -84,11 +86,25 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou make.bottom.equalTo(stackView.keyboardLayoutGuide.snp.top) } + + self.collectionViewLayout = UICollectionViewFlowLayout() self.collectionView = UICollectionView(frame: stackView.frame, collectionViewLayout: self.collectionViewLayout!) self.collectionView?.backgroundColor = .systemBackground self.collectionView?.alwaysBounceVertical = true self.collectionView?.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer") + + self.dataSource = .init(collectionView: collectionView!, cellProvider: { collectionView, indexPath, itemIdentifier in + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellReuseIdentifier, for: indexPath) as! GoalCollectionViewCell + cell.goal = self.filteredGoals[indexPath.row] + + return cell + }) + dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in + let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "footer", for: indexPath) + return footerView + } + self.collectionView?.dataSource = dataSource self.view.backgroundColor = .systemBackground self.title = "Goals" @@ -149,7 +165,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou self.collectionContainer.addSubview(self.collectionView!) self.collectionView!.delegate = self - self.collectionView!.dataSource = self + self.collectionView!.register(GoalCollectionViewCell.self, forCellWithReuseIdentifier: self.cellReuseIdentifier) self.collectionView?.snp.makeConstraints { (make) in make.top.bottom.equalTo(collectionContainer) @@ -217,6 +233,11 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou make.right.equalTo(self.view.safeAreaLayoutGuide.snp.rightMargin) make.bottom.equalTo(self.collectionView!.keyboardLayoutGuide.snp.top) } + + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([0]) + snapshot.appendItems(filteredGoals) + dataSource.apply(snapshot, animatingDifferences: true) } override func viewDidAppear(_ animated: Bool) { @@ -269,7 +290,12 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou @objc func handleSignOut() { self.goals = [] self.filteredGoals = [] - self.collectionView?.reloadData() + + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([0]) + snapshot.appendItems(filteredGoals) + dataSource.apply(snapshot, animatingDifferences: true) + if self.presentedViewController != nil { if type(of: self.presentedViewController!) == SignInViewController.self { return } } @@ -376,7 +402,12 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou self.setupHealthKit() self.collectionView?.refreshControl?.endRefreshing() MBProgressHUD.hide(for: self.view, animated: true) - self.collectionView!.reloadData() + + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([0]) + snapshot.appendItems(filteredGoals) + dataSource.apply(snapshot, animatingDifferences: true) + self.updateDeadbeatVisibility() self.lastUpdated = Date() self.updateLastUpdatedLabel() @@ -395,10 +426,6 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou return versionManager.lastChckedUpdateState() == .UpdateRequired ? 0 : self.filteredGoals.count } - func numberOfSections(in collectionView: UICollectionView) -> Int { - return 1 - } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let minimumWidth: CGFloat = 320 @@ -423,16 +450,6 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou return CGSize(width: targetWidth, height: 120) } - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let cell:GoalCollectionViewCell = self.collectionView!.dequeueReusableCell(withReuseIdentifier: self.cellReuseIdentifier, for: indexPath) as! GoalCollectionViewCell - - let goal:Goal = self.filteredGoals[(indexPath as NSIndexPath).row] - - cell.goal = goal - - return cell - } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { return CGSize(width: 320, height: section == 0 && self.goals.count > 0 ? 5 : 0) } @@ -445,8 +462,12 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { // After a rotation or other size change the optimal width for our cells may have changed. // We instruct the collectionView to reload so widths are recalculated. - coordinator.animate { _ in } completion: { _ in - self.collectionView?.reloadData() + coordinator.animate { _ in } completion: { [weak self] _ in + guard let self else { return } + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([0]) + snapshot.appendItems(filteredGoals) + dataSource.apply(snapshot, animatingDifferences: true) } } From 36b7e8511f710ce1e0db2ce043f370e026708abb Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Tue, 7 Jan 2025 09:49:23 +0100 Subject: [PATCH 02/12] clean code --- BeeSwift/Gallery/GalleryViewController.swift | 71 +++++++++++--------- BeeSwift/GoalCollectionViewCell.swift | 34 ++++++---- 2 files changed, 62 insertions(+), 43 deletions(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 1b018034..4939036b 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -33,7 +33,6 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou var collectionView :UICollectionView? var collectionViewLayout :UICollectionViewFlowLayout? private let freshnessIndicator = FreshnessIndicatorView() - let cellReuseIdentifier = "Cell" var deadbeatView = UIView() var outofdateView = UIView() let noGoalsLabel = BSLabel() @@ -44,7 +43,11 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou var goals : [Goal] = [] var filteredGoals : [Goal] = [] - var dataSource: UICollectionViewDiffableDataSource! + private enum Section: CaseIterable { + case main + } + + private var dataSource: UICollectionViewDiffableDataSource! public enum NotificationName { public static let openGoal = Notification.Name(rawValue: "com.beeminder.openGoal") @@ -85,27 +88,11 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou make.top.left.right.equalToSuperview() make.bottom.equalTo(stackView.keyboardLayoutGuide.snp.top) } - + configureCollectionView() + configureDataSource() - self.collectionViewLayout = UICollectionViewFlowLayout() - self.collectionView = UICollectionView(frame: stackView.frame, collectionViewLayout: self.collectionViewLayout!) - self.collectionView?.backgroundColor = .systemBackground - self.collectionView?.alwaysBounceVertical = true - self.collectionView?.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer") - self.dataSource = .init(collectionView: collectionView!, cellProvider: { collectionView, indexPath, itemIdentifier in - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellReuseIdentifier, for: indexPath) as! GoalCollectionViewCell - cell.goal = self.filteredGoals[indexPath.row] - - return cell - }) - dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in - let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "footer", for: indexPath) - return footerView - } - self.collectionView?.dataSource = dataSource - self.view.backgroundColor = .systemBackground self.title = "Goals" @@ -166,7 +153,6 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou self.collectionContainer.addSubview(self.collectionView!) self.collectionView!.delegate = self - self.collectionView!.register(GoalCollectionViewCell.self, forCellWithReuseIdentifier: self.cellReuseIdentifier) self.collectionView?.snp.makeConstraints { (make) in make.top.bottom.equalTo(collectionContainer) make.left.right.equalTo(collectionContainer.safeAreaLayoutGuide) @@ -234,10 +220,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou make.bottom.equalTo(self.collectionView!.keyboardLayoutGuide.snp.top) } - var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections([0]) - snapshot.appendItems(filteredGoals) - dataSource.apply(snapshot, animatingDifferences: true) + applySnapshot() } override func viewDidAppear(_ animated: Bool) { @@ -397,17 +380,19 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou } } } + + private func applySnapshot() { + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([.main]) + snapshot.appendItems(filteredGoals, toSection: .main) + dataSource.apply(snapshot, animatingDifferences: true) + } @objc func didUpdateGoals() { self.setupHealthKit() self.collectionView?.refreshControl?.endRefreshing() MBProgressHUD.hide(for: self.view, animated: true) - - var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections([0]) - snapshot.appendItems(filteredGoals) - dataSource.apply(snapshot, animatingDifferences: true) - + self.applySnapshot() self.updateDeadbeatVisibility() self.lastUpdated = Date() self.updateLastUpdatedLabel() @@ -496,6 +481,30 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou self.navigationController?.pushViewController(goalViewController, animated: true) } + private func configureCollectionView() { + self.collectionViewLayout = UICollectionViewFlowLayout() + self.collectionView = UICollectionView(frame: stackView.frame, collectionViewLayout: self.collectionViewLayout!) + self.collectionView?.backgroundColor = .systemBackground + self.collectionView?.alwaysBounceVertical = true + self.collectionView?.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer") + } + + private func configureDataSource() { + let cellRegistration = UICollectionView.CellRegistration { cell, indexPath, goal in + cell.configure(with: goal) + } + + self.dataSource = .init(collectionView: collectionView!, cellProvider: { collectionView, indexPath, goal in + collectionView.dequeueConfiguredReusableCell(using: cellRegistration, + for: indexPath, + item: goal) + }) + dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in + collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "footer", for: indexPath) + } + self.collectionView?.dataSource = dataSource + } + // MARK: - SFSafariViewControllerDelegate func safariViewControllerDidFinish(_ controller: SFSafariViewController) { diff --git a/BeeSwift/GoalCollectionViewCell.swift b/BeeSwift/GoalCollectionViewCell.swift index daa65b18..2fa2160d 100644 --- a/BeeSwift/GoalCollectionViewCell.swift +++ b/BeeSwift/GoalCollectionViewCell.swift @@ -18,18 +18,6 @@ class GoalCollectionViewCell: UICollectionViewCell { let safesumLabel :BSLabel = BSLabel() let margin = 8 - var goal: Goal? { - didSet { - self.thumbnailImageView.goal = goal - self.titleLabel.text = goal?.title - self.slugLabel.text = goal?.slug - self.titleLabel.isHidden = goal?.title == goal?.slug - self.todaytaLabel.text = goal?.todayta == true ? "✓" : "" - self.safesumLabel.text = goal?.capitalSafesum() - self.safesumLabel.textColor = goal?.countdownColor ?? UIColor.Beeminder.gray - } - } - override init(frame: CGRect) { super.init(frame: frame) @@ -86,4 +74,26 @@ class GoalCollectionViewCell: UICollectionViewCell { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } + + override func prepareForReuse() { + super.prepareForReuse() + + self.thumbnailImageView.goal = nil + self.titleLabel.text = nil + self.slugLabel.text = nil + self.titleLabel.isHidden = true + self.todaytaLabel.text = nil + self.safesumLabel.text = nil + self.safesumLabel.textColor = UIColor.Beeminder.gray + } + + func configure(with goal: Goal) { + self.thumbnailImageView.goal = goal + self.titleLabel.text = goal.title + self.slugLabel.text = goal.slug + self.titleLabel.isHidden = goal.title == goal.slug + self.todaytaLabel.text = goal.todayta ? "✓" : "" + self.safesumLabel.text = goal.capitalSafesum() + self.safesumLabel.textColor = goal.countdownColor + } } From d91a9b13920221765869176f1eafe16efa99584b Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Wed, 8 Jan 2025 07:55:21 +0100 Subject: [PATCH 03/12] we know we will create a collection view and layout --- BeeSwift/Gallery/GalleryViewController.swift | 58 +++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 4939036b..43e03216 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -30,8 +30,8 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou let stackView = UIStackView() let collectionContainer = UIView() - var collectionView :UICollectionView? - var collectionViewLayout :UICollectionViewFlowLayout? + var collectionView :UICollectionView! + var collectionViewLayout :UICollectionViewFlowLayout! private let freshnessIndicator = FreshnessIndicatorView() var deadbeatView = UIView() var outofdateView = UIView() @@ -150,15 +150,15 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou stackView.addArrangedSubview(self.collectionContainer) - self.collectionContainer.addSubview(self.collectionView!) - self.collectionView!.delegate = self + self.collectionContainer.addSubview(self.collectionView) + self.collectionView.delegate = self - self.collectionView?.snp.makeConstraints { (make) in + self.collectionView.snp.makeConstraints { (make) in make.top.bottom.equalTo(collectionContainer) make.left.right.equalTo(collectionContainer.safeAreaLayoutGuide) } - self.collectionView?.refreshControl = { + self.collectionView.refreshControl = { let refreshControl = UIRefreshControl() refreshControl.addTarget(self, action: #selector(self.fetchGoals), for: UIControl.Event.valueChanged) return refreshControl @@ -195,14 +195,14 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou case .UpdateRequired: self.outofdateView.isHidden = false self.outofdateLabel.text = "This version of the Beeminder app is no longer supported.\n Please update to the newest version in the App Store." - self.collectionView?.isHidden = true + self.collectionView.isHidden = true case .UpdateSuggested: self.outofdateView.isHidden = false self.outofdateLabel.text = "There is a new version of the Beeminder app in the App Store.\nPlease update when you have a moment." - self.collectionView?.isHidden = false + self.collectionView.isHidden = false case .UpToDate: self.outofdateView.isHidden = true - self.collectionView?.isHidden = false + self.collectionView.isHidden = false } } catch let error as VersionError { logger.error("Error checking for current version: \(error)") @@ -213,11 +213,11 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - self.collectionView!.snp.remakeConstraints { make in + self.collectionView.snp.remakeConstraints { make in make.top.equalTo(self.searchBar.snp.bottom) make.left.equalTo(self.view.safeAreaLayoutGuide.snp.leftMargin) make.right.equalTo(self.view.safeAreaLayoutGuide.snp.rightMargin) - make.bottom.equalTo(self.collectionView!.keyboardLayoutGuide.snp.top) + make.bottom.equalTo(self.collectionView.keyboardLayoutGuide.snp.top) } applySnapshot() @@ -273,12 +273,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou @objc func handleSignOut() { self.goals = [] self.filteredGoals = [] - - var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections([0]) - snapshot.appendItems(filteredGoals) - dataSource.apply(snapshot, animatingDifferences: true) - + self.applySnapshot() if self.presentedViewController != nil { if type(of: self.presentedViewController!) == SignInViewController.self { return } } @@ -337,9 +332,9 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true, completion: nil) } - self.collectionView?.refreshControl?.endRefreshing() + self.collectionView.refreshControl?.endRefreshing() MBProgressHUD.hide(for: self.view, animated: true) - self.collectionView!.reloadData() + self.collectionView.reloadData() } } } @@ -390,7 +385,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou @objc func didUpdateGoals() { self.setupHealthKit() - self.collectionView?.refreshControl?.endRefreshing() + self.collectionView.refreshControl?.endRefreshing() MBProgressHUD.hide(for: self.view, animated: true) self.applySnapshot() self.updateDeadbeatVisibility() @@ -414,8 +409,8 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let minimumWidth: CGFloat = 320 - let availableWidth = self.collectionView!.frame.width - self.collectionView!.contentInset.left - self.collectionView!.contentInset.right - let itemSpacing = self.collectionViewLayout!.minimumInteritemSpacing + let availableWidth = self.collectionView.frame.width - self.collectionView.contentInset.left - self.collectionView.contentInset.right + let itemSpacing = self.collectionViewLayout.minimumInteritemSpacing // Calculate how many cells could fit at the minimum width, rounding down (as we can't show a fractional cell) // We need to account for there being margin between cells, so there is 1 fewer margin than cell. We do this by @@ -430,7 +425,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou // Calculate how wide a cell can be. This can be larger than our minimum width because we // may have rounded down the number of cells. E.g. if we could have fit 1.5 minimum width // cells we will only show 1, but can make it 50% wider than minimum - let targetWidth = (availableWidth + itemSpacing) / CGFloat(cellsWhileMaintainingMinimumWidth) - self.collectionViewLayout!.minimumInteritemSpacing + let targetWidth = (availableWidth + itemSpacing) / CGFloat(cellsWhileMaintainingMinimumWidth) - self.collectionViewLayout.minimumInteritemSpacing return CGSize(width: targetWidth, height: 120) } @@ -449,10 +444,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou // We instruct the collectionView to reload so widths are recalculated. coordinator.animate { _ in } completion: { [weak self] _ in guard let self else { return } - var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections([0]) - snapshot.appendItems(filteredGoals) - dataSource.apply(snapshot, animatingDifferences: true) + self.applySnapshot() } } @@ -483,10 +475,10 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou private func configureCollectionView() { self.collectionViewLayout = UICollectionViewFlowLayout() - self.collectionView = UICollectionView(frame: stackView.frame, collectionViewLayout: self.collectionViewLayout!) - self.collectionView?.backgroundColor = .systemBackground - self.collectionView?.alwaysBounceVertical = true - self.collectionView?.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer") + self.collectionView = UICollectionView(frame: stackView.frame, collectionViewLayout: self.collectionViewLayout) + self.collectionView.backgroundColor = .systemBackground + self.collectionView.alwaysBounceVertical = true + self.collectionView.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer") } private func configureDataSource() { @@ -494,7 +486,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou cell.configure(with: goal) } - self.dataSource = .init(collectionView: collectionView!, cellProvider: { collectionView, indexPath, goal in + self.dataSource = .init(collectionView: collectionView, cellProvider: { collectionView, indexPath, goal in collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: goal) @@ -502,7 +494,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "footer", for: indexPath) } - self.collectionView?.dataSource = dataSource + self.collectionView.dataSource = dataSource } // MARK: - SFSafariViewControllerDelegate From d1e78d4f7d507b0e24609bb15b24e22bb6fc9643 Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Wed, 8 Jan 2025 08:14:16 +0100 Subject: [PATCH 04/12] also handles updateRequired state --- BeeSwift/Gallery/GalleryViewController.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 43e03216..3c56069c 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -377,9 +377,11 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou } private func applySnapshot() { + let goalsToShow = versionManager.lastChckedUpdateState() == .UpdateRequired ? [] : filteredGoals + var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) - snapshot.appendItems(filteredGoals, toSection: .main) + snapshot.appendItems(goalsToShow, toSection: .main) dataSource.apply(snapshot, animatingDifferences: true) } @@ -401,10 +403,6 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou let searchItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(self.searchButtonPressed)) self.navigationItem.leftBarButtonItem = searchItem } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return versionManager.lastChckedUpdateState() == .UpdateRequired ? 0 : self.filteredGoals.count - } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let minimumWidth: CGFloat = 320 From 67a2964989f1ab4d40eb20f36460fc400ee7e447 Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:20:43 +0100 Subject: [PATCH 05/12] Update BeeSwift/Gallery/GalleryViewController.swift Co-authored-by: Theo Spears --- BeeSwift/Gallery/GalleryViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 3c56069c..ea06940c 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -382,6 +382,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) snapshot.appendItems(goalsToShow, toSection: .main) + snapshot.reconfigureItems(goalsToShow) dataSource.apply(snapshot, animatingDifferences: true) } From e43fc7fa4bbf52f10cef8f71ecef75747194057b Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:23:07 +0100 Subject: [PATCH 06/12] Update BeeSwift/Gallery/GalleryViewController.swift --- BeeSwift/Gallery/GalleryViewController.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index ea06940c..5d4e1241 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -441,10 +441,9 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { // After a rotation or other size change the optimal width for our cells may have changed. // We instruct the collectionView to reload so widths are recalculated. - coordinator.animate { _ in } completion: { [weak self] _ in - guard let self else { return } - self.applySnapshot() - } + coordinator.animate(alongsideTransition: { _ in }, completion: { _ in + self.collectionViewLayout?.invalidateLayout() + }) } @objc func openGoalFromNotification(_ notification: Notification) { From 6f2f61d589b5a637ec7bf8aa8cbe1c809c275a89 Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:23:40 +0100 Subject: [PATCH 07/12] Update BeeSwift/Gallery/GalleryViewController.swift --- BeeSwift/Gallery/GalleryViewController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 5d4e1241..60f567c3 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -440,7 +440,6 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { // After a rotation or other size change the optimal width for our cells may have changed. - // We instruct the collectionView to reload so widths are recalculated. coordinator.animate(alongsideTransition: { _ in }, completion: { _ in self.collectionViewLayout?.invalidateLayout() }) From fac4f14a173ecbd57c7d158c0e88f4ce1e3f03e2 Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:24:11 +0100 Subject: [PATCH 08/12] Update BeeSwift/Gallery/GalleryViewController.swift --- BeeSwift/Gallery/GalleryViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 60f567c3..f7e56daf 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -334,7 +334,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou } self.collectionView.refreshControl?.endRefreshing() MBProgressHUD.hide(for: self.view, animated: true) - self.collectionView.reloadData() + self.applySnapshot() } } } From 0e5ce0c0902448246f305311333200fd8aeb71b2 Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:26:06 +0100 Subject: [PATCH 09/12] Update BeeSwift/Gallery/GalleryViewController.swift --- BeeSwift/Gallery/GalleryViewController.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index f7e56daf..48abfbb4 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -377,7 +377,10 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou } private func applySnapshot() { - let goalsToShow = versionManager.lastChckedUpdateState() == .UpdateRequired ? [] : filteredGoals + var goalsToShow: [Goal] { + guard versionManager.lastChckedUpdateState() != .UpdateRequired else { return [] } + return filteredGoals + } var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) From ad05c8e78a2a2212bcb49093c4e6bd565fddc811 Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:31:33 +0100 Subject: [PATCH 10/12] for a smaller diff --- BeeSwift/Gallery/GalleryViewController.swift | 44 ++++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/BeeSwift/Gallery/GalleryViewController.swift b/BeeSwift/Gallery/GalleryViewController.swift index 48abfbb4..e5ef794f 100644 --- a/BeeSwift/Gallery/GalleryViewController.swift +++ b/BeeSwift/Gallery/GalleryViewController.swift @@ -30,8 +30,8 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou let stackView = UIStackView() let collectionContainer = UIView() - var collectionView :UICollectionView! - var collectionViewLayout :UICollectionViewFlowLayout! + var collectionView :UICollectionView? + var collectionViewLayout :UICollectionViewFlowLayout? private let freshnessIndicator = FreshnessIndicatorView() var deadbeatView = UIView() var outofdateView = UIView() @@ -150,15 +150,15 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou stackView.addArrangedSubview(self.collectionContainer) - self.collectionContainer.addSubview(self.collectionView) - self.collectionView.delegate = self + self.collectionContainer.addSubview(self.collectionView!) + self.collectionView!.delegate = self - self.collectionView.snp.makeConstraints { (make) in + self.collectionView?.snp.makeConstraints { (make) in make.top.bottom.equalTo(collectionContainer) make.left.right.equalTo(collectionContainer.safeAreaLayoutGuide) } - self.collectionView.refreshControl = { + self.collectionView?.refreshControl = { let refreshControl = UIRefreshControl() refreshControl.addTarget(self, action: #selector(self.fetchGoals), for: UIControl.Event.valueChanged) return refreshControl @@ -195,14 +195,14 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou case .UpdateRequired: self.outofdateView.isHidden = false self.outofdateLabel.text = "This version of the Beeminder app is no longer supported.\n Please update to the newest version in the App Store." - self.collectionView.isHidden = true + self.collectionView?.isHidden = true case .UpdateSuggested: self.outofdateView.isHidden = false self.outofdateLabel.text = "There is a new version of the Beeminder app in the App Store.\nPlease update when you have a moment." - self.collectionView.isHidden = false + self.collectionView?.isHidden = false case .UpToDate: self.outofdateView.isHidden = true - self.collectionView.isHidden = false + self.collectionView?.isHidden = false } } catch let error as VersionError { logger.error("Error checking for current version: \(error)") @@ -213,11 +213,11 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - self.collectionView.snp.remakeConstraints { make in + self.collectionView!.snp.remakeConstraints { make in make.top.equalTo(self.searchBar.snp.bottom) make.left.equalTo(self.view.safeAreaLayoutGuide.snp.leftMargin) make.right.equalTo(self.view.safeAreaLayoutGuide.snp.rightMargin) - make.bottom.equalTo(self.collectionView.keyboardLayoutGuide.snp.top) + make.bottom.equalTo(self.collectionView!.keyboardLayoutGuide.snp.top) } applySnapshot() @@ -332,7 +332,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true, completion: nil) } - self.collectionView.refreshControl?.endRefreshing() + self.collectionView?.refreshControl?.endRefreshing() MBProgressHUD.hide(for: self.view, animated: true) self.applySnapshot() } @@ -391,7 +391,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou @objc func didUpdateGoals() { self.setupHealthKit() - self.collectionView.refreshControl?.endRefreshing() + self.collectionView?.refreshControl?.endRefreshing() MBProgressHUD.hide(for: self.view, animated: true) self.applySnapshot() self.updateDeadbeatVisibility() @@ -411,8 +411,8 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let minimumWidth: CGFloat = 320 - let availableWidth = self.collectionView.frame.width - self.collectionView.contentInset.left - self.collectionView.contentInset.right - let itemSpacing = self.collectionViewLayout.minimumInteritemSpacing + let availableWidth = self.collectionView!.frame.width - self.collectionView!.contentInset.left - self.collectionView!.contentInset.right + let itemSpacing = self.collectionViewLayout!.minimumInteritemSpacing // Calculate how many cells could fit at the minimum width, rounding down (as we can't show a fractional cell) // We need to account for there being margin between cells, so there is 1 fewer margin than cell. We do this by @@ -427,7 +427,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou // Calculate how wide a cell can be. This can be larger than our minimum width because we // may have rounded down the number of cells. E.g. if we could have fit 1.5 minimum width // cells we will only show 1, but can make it 50% wider than minimum - let targetWidth = (availableWidth + itemSpacing) / CGFloat(cellsWhileMaintainingMinimumWidth) - self.collectionViewLayout.minimumInteritemSpacing + let targetWidth = (availableWidth + itemSpacing) / CGFloat(cellsWhileMaintainingMinimumWidth) - self.collectionViewLayout!.minimumInteritemSpacing return CGSize(width: targetWidth, height: 120) } @@ -475,10 +475,10 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou private func configureCollectionView() { self.collectionViewLayout = UICollectionViewFlowLayout() - self.collectionView = UICollectionView(frame: stackView.frame, collectionViewLayout: self.collectionViewLayout) - self.collectionView.backgroundColor = .systemBackground - self.collectionView.alwaysBounceVertical = true - self.collectionView.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer") + self.collectionView = UICollectionView(frame: stackView.frame, collectionViewLayout: self.collectionViewLayout!) + self.collectionView?.backgroundColor = .systemBackground + self.collectionView?.alwaysBounceVertical = true + self.collectionView?.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "footer") } private func configureDataSource() { @@ -486,7 +486,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou cell.configure(with: goal) } - self.dataSource = .init(collectionView: collectionView, cellProvider: { collectionView, indexPath, goal in + self.dataSource = .init(collectionView: collectionView!, cellProvider: { collectionView, indexPath, goal in collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: goal) @@ -494,7 +494,7 @@ class GalleryViewController: UIViewController, UICollectionViewDelegateFlowLayou dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "footer", for: indexPath) } - self.collectionView.dataSource = dataSource + self.collectionView?.dataSource = dataSource } // MARK: - SFSafariViewControllerDelegate From a92d38bddb1630f6d16c635d73daafd7b4fb3654 Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:18:33 +0100 Subject: [PATCH 11/12] Update BeeSwift/GoalCollectionViewCell.swift --- BeeSwift/GoalCollectionViewCell.swift | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/BeeSwift/GoalCollectionViewCell.swift b/BeeSwift/GoalCollectionViewCell.swift index 2fa2160d..4d7f3982 100644 --- a/BeeSwift/GoalCollectionViewCell.swift +++ b/BeeSwift/GoalCollectionViewCell.swift @@ -78,22 +78,16 @@ class GoalCollectionViewCell: UICollectionViewCell { override func prepareForReuse() { super.prepareForReuse() - self.thumbnailImageView.goal = nil - self.titleLabel.text = nil - self.slugLabel.text = nil - self.titleLabel.isHidden = true - self.todaytaLabel.text = nil - self.safesumLabel.text = nil - self.safesumLabel.textColor = UIColor.Beeminder.gray + configure(with: nil) } - func configure(with goal: Goal) { + func configure(with goal: Goal?) { self.thumbnailImageView.goal = goal - self.titleLabel.text = goal.title - self.slugLabel.text = goal.slug - self.titleLabel.isHidden = goal.title == goal.slug - self.todaytaLabel.text = goal.todayta ? "✓" : "" - self.safesumLabel.text = goal.capitalSafesum() - self.safesumLabel.textColor = goal.countdownColor + self.titleLabel.text = goal?.title + self.slugLabel.text = goal?.slug + self.titleLabel.isHidden = goal?.title == goal?.slug + self.todaytaLabel.text = goal?.todayta == true ? "✓" : "" + self.safesumLabel.text = goal?.capitalSafesum() + self.safesumLabel.textColor = goal?.countdownColor } } From 7125d4e045b705fd0c756eab4d2a1753ae140edc Mon Sep 17 00:00:00 2001 From: krugerk <4656811+krugerk@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:19:12 +0100 Subject: [PATCH 12/12] Update BeeSwift/GoalCollectionViewCell.swift --- BeeSwift/GoalCollectionViewCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BeeSwift/GoalCollectionViewCell.swift b/BeeSwift/GoalCollectionViewCell.swift index 4d7f3982..1b770db7 100644 --- a/BeeSwift/GoalCollectionViewCell.swift +++ b/BeeSwift/GoalCollectionViewCell.swift @@ -88,6 +88,6 @@ class GoalCollectionViewCell: UICollectionViewCell { self.titleLabel.isHidden = goal?.title == goal?.slug self.todaytaLabel.text = goal?.todayta == true ? "✓" : "" self.safesumLabel.text = goal?.capitalSafesum() - self.safesumLabel.textColor = goal?.countdownColor + self.safesumLabel.textColor = goal?.countdownColor ?? UIColor.Beeminder.gray } }