Last active
October 30, 2022 11:30
-
-
Save prasadpamidi/829e636d4697fda025bb0795ee81e355 to your computer and use it in GitHub Desktop.
Issue with large navigation bar and collection view
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
class SegmentedControlSupplementaryView: UICollectionReusableView { | |
let segmentControl = UISegmentedControl(items: ["Item 1", "Items 2", "Items 3"]) | |
static let reuseIdentifier = "segmented-supplementary-reuse-identifier" | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
self.backgroundColor = .white | |
configure() | |
} | |
required init?(coder: NSCoder) { | |
fatalError() | |
} | |
} | |
extension SegmentedControlSupplementaryView { | |
func configure() { | |
addSubview(segmentControl) | |
segmentControl.translatesAutoresizingMaskIntoConstraints = false | |
let inset = CGFloat(10) | |
NSLayoutConstraint.activate([ | |
segmentControl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: inset), | |
segmentControl.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -inset), | |
segmentControl.topAnchor.constraint(equalTo: topAnchor, constant: inset), | |
segmentControl.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -inset) | |
]) | |
self.setNeedsLayout() | |
} | |
} | |
let sectionHeaderElementKind = "sticky-header-element-kind" | |
let itemsPerPage = 15 | |
let section = "Sticky Header" | |
let sectionHeader = "Section Header" | |
class StickyHeaderViewController: UIViewController { | |
var dataSource: UICollectionViewDiffableDataSource<String, Int>? = nil | |
var collectionView: UICollectionView? | |
// initial data | |
var items: [Int] = [] | |
var currentOffset: Int = 0 | |
fileprivate lazy var refreshControl: UIRefreshControl = { | |
let refreshControl = UIRefreshControl() | |
refreshControl.addTarget(self, action: #selector(performRefresh(_:)), for: UIControl.Event.valueChanged) | |
return refreshControl | |
}() | |
/// A background serial queue is used to calculate diffing and apply snapshot to the registered collection view | |
fileprivate let serialQueue = DispatchQueue(label: "assetlist.update.serial.queue") | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
self.view.backgroundColor = .white | |
self.navigationController?.navigationBar.prefersLargeTitles = true | |
self.navigationItem.title = "Sticky Section Headers" | |
self.edgesForExtendedLayout = .all | |
self.extendedLayoutIncludesOpaqueBars = true | |
self.navigationItem.searchController = UISearchController() | |
setUpNavBarAppearance() | |
setUpCollectionView() | |
configureDataSource() | |
} | |
func setUpNavBarAppearance() { | |
let appearance = UINavigationBarAppearance() | |
appearance.configureWithOpaqueBackground() | |
appearance.backgroundColor = .white | |
appearance.shadowColor = .clear | |
self.navigationController?.navigationBar.standardAppearance = appearance | |
self.navigationController?.navigationBar.scrollEdgeAppearance = appearance | |
} | |
func layout() -> UICollectionViewLayout { | |
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), | |
heightDimension: .fractionalHeight(1.0)) | |
let item = NSCollectionLayoutItem(layoutSize: itemSize) | |
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), | |
heightDimension: .absolute(44)) | |
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) | |
let section = NSCollectionLayoutSection(group: group) | |
section.interGroupSpacing = 5 | |
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10) | |
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem( | |
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), | |
heightDimension: .estimated(44)), | |
elementKind: sectionHeaderElementKind, | |
alignment: .top) | |
sectionHeader.pinToVisibleBounds = true | |
sectionHeader.zIndex = 2 | |
section.boundarySupplementaryItems = [sectionHeader] | |
let layout = UICollectionViewCompositionalLayout(section: section) | |
return layout | |
} | |
func setUpCollectionView() { | |
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout()) | |
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] | |
collectionView.backgroundColor = .systemBackground | |
collectionView.delegate = self | |
collectionView.register(ListCell.self, forCellWithReuseIdentifier: ListCell.reuseIdentifier) | |
collectionView.register(SegmentedControlSupplementaryView.self, | |
forSupplementaryViewOfKind: sectionHeaderElementKind, | |
withReuseIdentifier: SegmentedControlSupplementaryView.reuseIdentifier) | |
self.view.addSubview(collectionView) | |
collectionView.refreshControl = refreshControl | |
self.collectionView = collectionView | |
} | |
func configureDataSource() { | |
guard let collectionView = self.collectionView else { | |
return | |
} | |
dataSource = UICollectionViewDiffableDataSource<String, Int>(collectionView: collectionView) { | |
(collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in | |
// Get a cell of the desired kind. | |
guard let cell = collectionView.dequeueReusableCell( | |
withReuseIdentifier: ListCell.reuseIdentifier, | |
for: indexPath) as? ListCell else { fatalError("Cannot create new cell") } | |
// Populate the cell with our item description. | |
cell.label.text = "\(indexPath.section),\(indexPath.item)" | |
return cell | |
} | |
dataSource?.supplementaryViewProvider = { [weak self] | |
(collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? in | |
// Get a supplementary view of the desired kind. | |
guard let header = collectionView.dequeueReusableSupplementaryView( | |
ofKind: kind, | |
withReuseIdentifier: SegmentedControlSupplementaryView.reuseIdentifier, | |
for: indexPath) as? SegmentedControlSupplementaryView else { fatalError("Cannot create new header") } | |
header.segmentControl.addTarget(self, action: #selector(self?.segmentChanged), for: .valueChanged) | |
// Return the view. | |
return header | |
} | |
var snapshot = NSDiffableDataSourceSnapshot<String, Int>() | |
snapshot.appendSections([section]) | |
items = Array(currentOffset..<currentOffset + 2*itemsPerPage) | |
currentOffset += 2*itemsPerPage | |
snapshot.appendItems(items) | |
serialQueue.async { [weak self] in | |
self?.dataSource?.apply(snapshot, animatingDifferences: true) | |
} | |
} | |
@objc func segmentChanged() { | |
self.collectionView?.setContentOffset(.zero, animated: true) | |
} | |
@objc | |
func performRefresh(_ refreshControl: UIRefreshControl?) { | |
getNextPage() | |
// if not nil make sure to call end refreshing. | |
DispatchQueue.main.async { | |
refreshControl?.endRefreshing() | |
} | |
} | |
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { | |
self.navigationController?.pushViewController(StickyHeaderViewController(), animated: true) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment