I want the navigation bar to overlap all the content in the app and I use this code to trying to do this:
code 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()
}
}
}
code 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
}
without navigation bar:
using navigation bar:
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?