From 348dcc558cef54e9912c61b88f0e00cd71736998 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Thu, 11 Jul 2024 22:29:34 -0700 Subject: [PATCH] Fix profile page switching on iOS 18 --- .../ProfileHeaderCollectionViewCell.swift | 6 ++- .../ProfileStatusesViewController.swift | 42 ++++++++++--------- .../Profile/ProfileViewController.swift | 9 ++-- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/Tusker/Screens/Profile/ProfileHeaderCollectionViewCell.swift b/Tusker/Screens/Profile/ProfileHeaderCollectionViewCell.swift index bc7303d078..e74141b640 100644 --- a/Tusker/Screens/Profile/ProfileHeaderCollectionViewCell.swift +++ b/Tusker/Screens/Profile/ProfileHeaderCollectionViewCell.swift @@ -38,8 +38,10 @@ class ProfileHeaderCollectionViewCell: UICollectionViewCell { header.translatesAutoresizingMaskIntoConstraints = false contentView.embedSubview(header) self.state = .view(header) - case .view(_): - fatalError("profile header collection view cell already has view") + case .view(let existing): + if existing !== header { + fatalError("profile header collection view cell already has view") + } } } diff --git a/Tusker/Screens/Profile/ProfileStatusesViewController.swift b/Tusker/Screens/Profile/ProfileStatusesViewController.swift index c7764ed9b0..d1f6d3c920 100644 --- a/Tusker/Screens/Profile/ProfileStatusesViewController.swift +++ b/Tusker/Screens/Profile/ProfileStatusesViewController.swift @@ -17,7 +17,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie let filterer: Filterer private(set) var accountID: String! let kind: Kind - var initialHeaderMode: HeaderMode? + var headerViewMode: HeaderMode? weak var profileHeaderDelegate: ProfileHeaderViewDelegate? private(set) var controller: TimelineLikeController! @@ -30,7 +30,9 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie view as? UICollectionView } private(set) var dataSource: UICollectionViewDiffableDataSource! - private(set) var headerCell: ProfileHeaderCollectionViewCell? + var headerCell: ProfileHeaderCollectionViewCell? { + collectionView.cellForItem(at: IndexPath(item: 0, section: 0)) as? ProfileHeaderCollectionViewCell + } var reconfigureVisibleItemsOnEndDecelerating: Bool = false @@ -173,29 +175,29 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in switch itemIdentifier { case .header(let id): - if let headerCell = self.headerCell { - headerCell.view?.updateUI(for: id) - return headerCell - } else { - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "headerCell", for: indexPath) as! ProfileHeaderCollectionViewCell - switch self.initialHeaderMode { - case nil: - fatalError("missing initialHeaderMode") - case .createView: + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "headerCell", for: indexPath) as! ProfileHeaderCollectionViewCell + switch self.headerViewMode { + case nil: + fatalError("missing headerViewMode") + case .createViewIfNeeded: + if let view = cell.view { + view.updateUI(for: id) + self.headerViewMode = .useExistingView(view) + } else { let view = ProfileHeaderView.create() view.delegate = self.profileHeaderDelegate view.updateUI(for: id) view.pagesSegmentedControl.setSelectedOption(self.owner!.currentPage, animated: false) cell.addHeader(view) - case .useExistingView(let view): - view.updateUI(for: id) - cell.addHeader(view) - case .placeholder(height: let height): - _ = cell.addConstraint(height: height) + self.headerViewMode = .useExistingView(view) } - self.headerCell = cell - return cell + case .useExistingView(let view): + view.updateUI(for: id) + cell.addHeader(view) + case .placeholder(height: let height): + _ = cell.addConstraint(height: height) } + return cell case .status(id: let id, collapseState: let collapseState, filterState: let filterState, pinned: let pinned): let (result, precomputedContent) = filterResult(state: filterState, statusID: id) switch result { @@ -411,7 +413,9 @@ extension ProfileStatusesViewController { case statuses, withReplies, onlyMedia } enum HeaderMode { - case createView, useExistingView(ProfileHeaderView), placeholder(height: CGFloat) + case createViewIfNeeded + case useExistingView(ProfileHeaderView) + case placeholder(height: CGFloat) } } diff --git a/Tusker/Screens/Profile/ProfileViewController.swift b/Tusker/Screens/Profile/ProfileViewController.swift index f4266c74d6..7711e66d14 100644 --- a/Tusker/Screens/Profile/ProfileViewController.swift +++ b/Tusker/Screens/Profile/ProfileViewController.swift @@ -178,7 +178,7 @@ class ProfileViewController: UIViewController, StateRestorableViewController { guard let currentIndex else { assert(!animated) // if old doesn't exist, we're selecting the initial view controller, so moving the header around isn't necessary - new.initialHeaderMode = .createView + new.headerViewMode = .createViewIfNeeded new.view.translatesAutoresizingMaskIntoConstraints = false addChild(new) view.addSubview(new.view) @@ -213,11 +213,14 @@ class ProfileViewController: UIViewController, StateRestorableViewController { // old header cell must have the header view let headerView = oldHeaderCell.addConstraint(height: oldHeaderCell.bounds.height)! + // Set the outgoing VC's header view mode to placeholder, so that it does steal the header view back + // in case it updates the cell in the background. + old.headerViewMode = .placeholder(height: oldHeaderCell.bounds.height) if let newHeaderCell = new.headerCell { _ = newHeaderCell.addConstraint(height: oldHeaderCell.bounds.height) } else { - new.initialHeaderMode = .placeholder(height: oldHeaderCell.bounds.height) + new.headerViewMode = .placeholder(height: oldHeaderCell.bounds.height) } // disable user interaction during animation, to avoid any potential weird race conditions @@ -285,7 +288,7 @@ class ProfileViewController: UIViewController, StateRestorableViewController { if let newHeaderCell = new.headerCell { newHeaderCell.addHeader(headerView) } else { - new.initialHeaderMode = .useExistingView(headerView) + new.headerViewMode = .useExistingView(headerView) } self.state = .idle