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,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")
}
}
}

View File

@ -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<TimelineItem>!
@ -30,7 +30,9 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
view as? UICollectionView
}
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
@ -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)
}
}

View File

@ -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