Fix profile page switching on iOS 18

This commit is contained in:
Shadowfacts 2024-07-11 22:29:34 -07:00
parent 703f6f695b
commit 348dcc558c
3 changed files with 33 additions and 24 deletions

View File

@ -38,10 +38,12 @@ class ProfileHeaderCollectionViewCell: UICollectionViewCell {
header.translatesAutoresizingMaskIntoConstraints = false header.translatesAutoresizingMaskIntoConstraints = false
contentView.embedSubview(header) contentView.embedSubview(header)
self.state = .view(header) self.state = .view(header)
case .view(_): case .view(let existing):
if existing !== header {
fatalError("profile header collection view cell already has view") fatalError("profile header collection view cell already has view")
} }
} }
}
func addConstraint(height: CGFloat) -> ProfileHeaderView? { func addConstraint(height: CGFloat) -> ProfileHeaderView? {
switch state { switch state {

View File

@ -17,7 +17,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
let filterer: Filterer let filterer: Filterer
private(set) var accountID: String! private(set) var accountID: String!
let kind: Kind let kind: Kind
var initialHeaderMode: HeaderMode? var headerViewMode: HeaderMode?
weak var profileHeaderDelegate: ProfileHeaderViewDelegate? weak var profileHeaderDelegate: ProfileHeaderViewDelegate?
private(set) var controller: TimelineLikeController<TimelineItem>! private(set) var controller: TimelineLikeController<TimelineItem>!
@ -30,7 +30,9 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
view as? UICollectionView view as? UICollectionView
} }
private(set) var dataSource: UICollectionViewDiffableDataSource<Section, Item>! private(set) var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
private(set) var headerCell: ProfileHeaderCollectionViewCell? var headerCell: ProfileHeaderCollectionViewCell? {
collectionView.cellForItem(at: IndexPath(item: 0, section: 0)) as? ProfileHeaderCollectionViewCell
}
var reconfigureVisibleItemsOnEndDecelerating: Bool = false var reconfigureVisibleItemsOnEndDecelerating: Bool = false
@ -173,29 +175,29 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
switch itemIdentifier { switch itemIdentifier {
case .header(let id): 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 let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "headerCell", for: indexPath) as! ProfileHeaderCollectionViewCell
switch self.initialHeaderMode { switch self.headerViewMode {
case nil: case nil:
fatalError("missing initialHeaderMode") fatalError("missing headerViewMode")
case .createView: case .createViewIfNeeded:
if let view = cell.view {
view.updateUI(for: id)
self.headerViewMode = .useExistingView(view)
} else {
let view = ProfileHeaderView.create() let view = ProfileHeaderView.create()
view.delegate = self.profileHeaderDelegate view.delegate = self.profileHeaderDelegate
view.updateUI(for: id) view.updateUI(for: id)
view.pagesSegmentedControl.setSelectedOption(self.owner!.currentPage, animated: false) view.pagesSegmentedControl.setSelectedOption(self.owner!.currentPage, animated: false)
cell.addHeader(view) cell.addHeader(view)
self.headerViewMode = .useExistingView(view)
}
case .useExistingView(let view): case .useExistingView(let view):
view.updateUI(for: id) view.updateUI(for: id)
cell.addHeader(view) cell.addHeader(view)
case .placeholder(height: let height): case .placeholder(height: let height):
_ = cell.addConstraint(height: height) _ = cell.addConstraint(height: height)
} }
self.headerCell = cell
return cell return cell
}
case .status(id: let id, collapseState: let collapseState, filterState: let filterState, pinned: let pinned): case .status(id: let id, collapseState: let collapseState, filterState: let filterState, pinned: let pinned):
let (result, precomputedContent) = filterResult(state: filterState, statusID: id) let (result, precomputedContent) = filterResult(state: filterState, statusID: id)
switch result { switch result {
@ -411,7 +413,9 @@ extension ProfileStatusesViewController {
case statuses, withReplies, onlyMedia case statuses, withReplies, onlyMedia
} }
enum HeaderMode { enum HeaderMode {
case createView, useExistingView(ProfileHeaderView), placeholder(height: CGFloat) case createViewIfNeeded
case useExistingView(ProfileHeaderView)
case placeholder(height: CGFloat)
} }
} }

View File

@ -178,7 +178,7 @@ class ProfileViewController: UIViewController, StateRestorableViewController {
guard let currentIndex else { guard let currentIndex else {
assert(!animated) assert(!animated)
// if old doesn't exist, we're selecting the initial view controller, so moving the header around isn't necessary // 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 new.view.translatesAutoresizingMaskIntoConstraints = false
addChild(new) addChild(new)
view.addSubview(new.view) view.addSubview(new.view)
@ -213,11 +213,14 @@ class ProfileViewController: UIViewController, StateRestorableViewController {
// old header cell must have the header view // old header cell must have the header view
let headerView = oldHeaderCell.addConstraint(height: oldHeaderCell.bounds.height)! 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 { if let newHeaderCell = new.headerCell {
_ = newHeaderCell.addConstraint(height: oldHeaderCell.bounds.height) _ = newHeaderCell.addConstraint(height: oldHeaderCell.bounds.height)
} else { } 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 // 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 { if let newHeaderCell = new.headerCell {
newHeaderCell.addHeader(headerView) newHeaderCell.addHeader(headerView)
} else { } else {
new.initialHeaderMode = .useExistingView(headerView) new.headerViewMode = .useExistingView(headerView)
} }
self.state = .idle self.state = .idle