Using Swift-5.0.1, iOS-12.2,
I successfully was able to place a UICollectionView with screen-wide cells with the custom height of 150 pixels.
However, the navigationBar is hiding the first cell.
I saw different statements which state that a contentInset must be applied to the collectionView.
However, none of the posts I found clearly states the amount that has to be used as the offset (especially, considering all the new iPhone-screens and considering the fact that the navBar might be large or small).
I tried:
let offset1 = self.navigationController?.navigationBar.frame.size.height
let offset2 = navigationController?.navigationBar.frame.maxY
self.edgesForExtendedLayout = []
self.collectionView.contentInset = UIEdgeInsets(top: offset2, left: 0, bottom: 0, right: 0)
But none of the values really match the needed inset !
My question: Is there a height-property of the navigationController (or other) that I can rely on in order to set the contentInset on the collectionView properly ??
I realised that for an iPhone XS, the correct inset is approximately 112 pixels. How does these come into place ??
The following images illustrate the NavBar hiding of the first cell of the collectionView.
My NavigationController and UICollectionViewController are instantiated inside AppDelegate.swift like this:
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
let vc = CollectionViewController()
let nc = UINavigationController(rootViewController: vc)
window?.rootViewController = nc
window?.makeKeyAndVisible()
return true
}
Here the CollectionViewController:
import UIKit
class CollectionViewController: BaseListController, UICollectionViewDelegateFlowLayout {
fileprivate let cellId = "cellId"
fileprivate let activityIndicator = UIActivityIndicatorView()
lazy var viewModel: PhotoListViewModel = {
return PhotoListViewModel()
}()
override func viewDidLoad() {
super.viewDidLoad()
// Init the static view
initView()
// init view model
initVM()
}
func initView() {
self.navigationItem.title = "NavBar"
collectionView.register(PhotoListCollectionViewCell.self, forCellWithReuseIdentifier: cellId)
collectionView.backgroundColor = .white
// a number of 112 makes the correct inset but why ???????
collectionView.contentInset = UIEdgeInsets(top: 112, left: 0, bottom: 0, right: 0)
}
func initVM() {
// Naive binding
viewModel.showAlertClosure = { [weak self] () in
DispatchQueue.main.async {
if let message = self?.viewModel.alertMessage {
self?.showAlert( message )
}
}
}
viewModel.updateLoadingStatus = { [weak self] () in
DispatchQueue.main.async {
let isLoading = self?.viewModel.isLoading ?? false
if isLoading {
self?.activityIndicator.startAnimating()
UIView.animate(withDuration: 0.2, animations: {
self?.collectionView.alpha = 0.0
})
}else {
self?.activityIndicator.stopAnimating()
UIView.animate(withDuration: 0.2, animations: {
self?.collectionView.alpha = 1.0
})
}
}
}
viewModel.reloadTableViewClosure = { [weak self] () in
DispatchQueue.main.async {
self?.collectionView.reloadData()
}
}
viewModel.initFetch()
}
func showAlert( _ message: String ) {
let alert = UIAlertController(title: "Alert", message: message, preferredStyle: .alert)
alert.addAction( UIAlertAction(title: "Ok", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return viewModel.numberOfCells
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as? PhotoListCollectionViewCell else {
fatalError("Cell not exists in storyboard")
}
let cellVM = viewModel.getCellViewModel( at: indexPath )
cell.photoListCellViewModel = cellVM
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: view.frame.width, height: 150)
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.viewModel.userPressed(at: indexPath)
if viewModel.isAllowSegue {
let detailVC = PhotoDetailViewController()
if let photo = viewModel.selectedPhoto {
detailVC.imageView = UIImageView()
detailVC.imageView.contentMode = .scaleAspectFill
detailVC.imageView.sd_setImage(with: URL(string: photo.image_url)) { (image, error, type, url) in
}
}
self.navigationController?.pushViewController(detailVC, animated: true)
}
}
}
With its BaseController...
import UIKit
class BaseListController: UICollectionViewController {
init() {
super.init(collectionViewLayout: UICollectionViewFlowLayout())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

