I want the navigation bar to overlap all the content in the app and I use this code to trying to do this:
SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = (scene as? UIWindowScene) {
let window = UIWindow(windowScene: windowScene)
let nav = UINavigationController()
let mainView = StoreViewController()
nav.viewControllers = [mainView]
nav.navigationBar.backgroundColor = .green.withAlphaComponent(0.5)
window.rootViewController = nav
self.window = window
window.makeKeyAndVisible()
}
}
}
StoreViewController
class StoreViewController: UIViewController {
let backgroundView = BackgroundView()
var collectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<Section, Item>?
let sections = Bundle.main.decode([Section].self, from: "section.json")
override func viewDidLoad() {
super.viewDidLoad()
createCollectionView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
}
func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
switch self.sections[indexPath.section].identifier {
case "featuredCell": return self.configure(FeaturedCell.self, with: item, for: indexPath)
default: return self.configure(CarouselCell.self, with: item, for: indexPath)
}
}
dataSource?.supplementaryViewProvider = { [weak self] collectionView, kind, indexPath in
guard let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: SectionHeader.reuseIdentifier, for: indexPath) as? SectionHeader
else { return nil }
guard let firstApp = self?.dataSource?.itemIdentifier(for: indexPath) else { return nil }
guard let section = self?.dataSource?.snapshot().sectionIdentifier(containingItem: firstApp)
else { return nil }
if section.title.isEmpty { return nil }
sectionHeader.title.text = section.title
sectionHeader.subtitle.text = section.subtitle
return sectionHeader
}
}
func configure<T: SelfConfiguringCell>(_ cellType: T.Type, with item: Item, for indexPath: IndexPath) -> T {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: cellType.reuseIdentifier,
for: indexPath) as? T else {
fatalError("Unable to dequeue \(cellType)")
}
cell.configure(with: item)
return cell
}
func reloadData() {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(sections)
for section in sections { snapshot.appendItems(section.item, toSection: section) }
dataSource?.apply(snapshot)
}
func createCollectionView() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createCompositionalLayout())
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.backgroundView = backgroundView
collectionView.isScrollEnabled = false
view.addSubview(collectionView)
collectionView.register(SectionHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: SectionHeader.reuseIdentifier)
collectionView.register(CarouselCell.self, forCellWithReuseIdentifier: CarouselCell.reuseIdentifier)
createDataSource()
reloadData()
}
func createCompositionalLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in
let section = self.sections[sectionIndex]
switch section.identifier {
case "featuredCell": return self.featuredSection(using: section)
default: return self.carouselSection(using: section)
}
}
return layout
}
func sectionHeader() -> NSCollectionLayoutBoundarySupplementaryItem {
let layoutSectionHeaderSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.93), heightDimension: .estimated(80))
let layoutSectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: layoutSectionHeaderSize, elementKind: UICollectionElementKindSectionHeader, alignment: .top)
return layoutSectionHeader
}
func carouselSection(using section: Section) -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let layoutItem = NSCollectionLayoutItem(layoutSize: itemSize)
var layoutGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(1), heightDimension: .absolute(1))
var layoutGroup = NSCollectionLayoutGroup.horizontal(layoutSize: layoutGroupSize, subitems: [layoutItem])
switch UIDevice.current.userInterfaceIdiom {
case .phone:
let a:CGFloat = UIScreen.main.bounds.size.height/8
let b:CGFloat = UIScreen.main.bounds.size.height
layoutGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(b - (a) ), heightDimension: .absolute(b))
layoutGroup = NSCollectionLayoutGroup.horizontal(layoutSize: layoutGroupSize, subitems: [layoutItem])
layoutGroup.contentInsets = NSDirectionalEdgeInsets(top: a, leading: a/2, bottom: a, trailing: a/2)
case .pad:
let a:CGFloat = UIScreen.main.bounds.size.height*0.25
let h:CGFloat = a * 1.8
let b:CGFloat = UIScreen.main.bounds.size.height
layoutGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(b - (h)), heightDimension: .absolute(b - (a)))
layoutGroup = NSCollectionLayoutGroup.horizontal(layoutSize: layoutGroupSize, subitems: [layoutItem])
layoutGroup.contentInsets = NSDirectionalEdgeInsets(top: a, leading: a/10, bottom: 0, trailing: a/10)
default:
print(UIScreen.main.bounds.size.height)
}
let layoutSection = NSCollectionLayoutSection(group: layoutGroup)
layoutSection.orthogonalScrollingBehavior = .groupPagingCentered
layoutSection.visibleItemsInvalidationHandler = { (items, offset, environment) in
items.forEach { item in
let distanceFromCenter = abs((item.frame.midX - offset.x) - environment.container.contentSize.width / 2.0)
let minScale: CGFloat = 0.7
let maxScale: CGFloat = 1.1
let scale = max(maxScale - (distanceFromCenter / environment.container.contentSize.width), minScale)
item.transform = CGAffineTransform(scaleX: scale, y: scale)
}
// no apparent built-in property of "scrollable content size"
// so let's jump through some hoops to calculate it
// we could probably make these class properties, and only update them
// on frame change, but that's for another time...
let n: Int = self.dataSource!.snapshot().numberOfItems
let cvw: CGFloat = self.collectionView.frame.width
let cw: CGFloat = environment.container.contentSize.width
let groupWidth: CGFloat = layoutGroupSize.widthDimension.dimension
// because layoutSection.orthogonalScrollingBehavior is set to .groupPagingCentered
// the actual scrollable width is center of item 0 to center of last item
let scrollableWidth: CGFloat = groupWidth * CGFloat(n - 1)
// get the group midX
let groupMidX = CGRect(origin: .zero, size: environment.container.contentSize).midX
// calculate x-offset for first centered group
let baseOffsetX: CGFloat = (groupMidX - (groupWidth * 0.5)) - ((cvw - cw) * 0.5)
// percent we are currently scrolled, of total scrollable width
let pctScrolled: CGFloat = (offset.x + baseOffsetX) / scrollableWidth
// tell the background view to update its image view x-offset
self.backgroundView.xPercent = pctScrolled
}
return layoutSection
}
In this case, the navigation bar covers the background and the collection view, but the collection view cells are moved down. How to fix it?
Set the collection view's contentInsetAdjustmentBehavior to .never
.