I am attempting to resize the collectionView cell inside the masterViewController of a SplitViewController but the resizing did not work. I have implemented the necessary viewWillTransition and invalidateLayout and it turns out fine if it isn't housed inside a splitViewController. In my case, I have full screen cell size.
Here's my implementation:
//This CollectionController is housed inside a TabBarController.
//The TabBarController is the masterViewController
class CollectionController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = .gray
cv.dataSource = self
cv.delegate = self
layout.scrollDirection = .horizontal
cv.isPagingEnabled = true
cv.showsHorizontalScrollIndicator = false
cv.showsVerticalScrollIndicator = false
cv.contentInsetAdjustmentBehavior = .always
cv.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
return cv
}()
var spacing: CGFloat = 16
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 16
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 16
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let layoutFrame = view.safeAreaLayoutGuide.layoutFrame
let size = CGSize(width: layoutFrame.width, height: layoutFrame.height)
print("SizeForItem", size)
return setUpCellSize(size: size)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
print("Transit1", size)
layout.itemSize = setUpCellSize(size: size) //With or without this makes no difference
layout.invalidateLayout()
print("Transit2", size)
}
}
func setUpCellSize(size: CGSize) -> CGSize {
return size
}
}
This is the result if implemented purely in the TabBarController, ie not inside a splitViewController. Noticed that cells (in blue) are positioned well beneath the status bar. Result is as what is expected and desired.
This is the result if implemented inside a splitViewController. Noticed that on first rotation to landscape, the cells went above the status bar. And when rotated back to portrait, the cell did not resize back to full screen.
Is there something else that needs to be implemented if the collectionView is placed inside a masterViewController?
EDIT
I add print statements inside sizeForItem and viewWillTransition. It turns out that on second rotation, the new size in viewWillTransition were not passed to the sizeForItem.
Even explicit calls to setupCellSize didn't update correctly. It also give the The behavior of the UICollectionViewFlowLayout is not defined warning. See gif (Gif starts when iPad is in landscape mode).
Also notice that when iPad rotates from landscape -> portrait, blue cell anchors to bottom of statusBar and top of tabBar. This is desired. Then when rotated back from portrait -> landscape, blue cell exceeds statusBar and went underneath tabBar. This is not what I want.
You need to embed the CollectionController (the MASTER CONTROLLER) in a UINavigationController. If you really don't want a navigationBar, just hide it.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.isHidden = true
}
Ok, I have managed to solve this issue. #Glenn's solution helped partially. The portion that helped resolved this entirely is from this post.
First, implement #Glenn's solution on embedding CollectionController in a UINavigationController and hiding the navBar (since I do not want the navBar).
Second, add invalidateLayout in viewWillLayoutSubviews.
Full Code:
class CollectionController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = .gray
cv.dataSource = self
cv.delegate = self
cv.isPagingEnabled = true
cv.alwaysBounceHorizontal = true
cv.showsHorizontalScrollIndicator = false
cv.showsVerticalScrollIndicator = false
cv.contentInsetAdjustmentBehavior = .always
cv.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
return cv
}()
var spacing: CGFloat = 16
//FIRST
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.isHidden = true
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let layoutFrame = view.safeAreaLayoutGuide.layoutFrame
let size = CGSize(width: layoutFrame.width, height: layoutFrame.height)
return setUpCellSize(size: size)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
collectionView.collectionViewLayout.invalidateLayout()
}
//SECOND
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}
func setUpCellSize(size: CGSize) -> CGSize {
return size
}
}
Result like so, blue cell anchored nicely to the bottom of statusBar and top of tabBar.
A side note, I attempted some of the solution in this thread and these didn't work for me:
returning shouldInvalidateLayoutForBoundsChange to true as suggested here
subclassing UICollectionViewFlowLayout as suggested here
If anyone out there decides to attempt the solution for the same case in a splitViewController can take note.
Related
I am currently working on a horizontal gallery scroll view. I have decided to use UICollectionView with horizontal paging enabled. It almost works great... expect on rotation the collection view cells are not centred (see screenshot attached), where red is the first cell and yellow is the second one.
It usually works fine on the first rotation but then just messes up :D
This is the code I have for setting up the class:
class GalleryScrollView: UIView {
private var collectionView: UICollectionView!
private var layout: UICollectionViewFlowLayout!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override func layoutSubviews() {
layout.invalidateLayout()
}
private func setupView() {
layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: self.frame,
collectionViewLayout: layout)
collectionView.register(UINib(nibName: "PhotoGalleryCell", bundle: nil), forCellWithReuseIdentifier: "photoGalleryCell")
collectionView.isPagingEnabled = true
collectionView.contentInsetAdjustmentBehavior = .never
collectionView.dataSource = self
collectionView.delegate = self
collectionView.autoresizingMask = .flexibleWidth
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .green
self.addSubview(collectionView)
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: self.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
collectionView.reloadData()
}
}
Followed by the collection view setup:
extension GalleryScrollView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoGalleryCell", for: indexPath) as? PhotoGalleryCell else {
return UICollectionViewCell()
}
if indexPath.item == 0 {
cell.backgroundColor = .red
} else {
cell.backgroundColor = .yellow
}
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
2
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.frame.size
}
I tried playing around with func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint { but for some reason the func was never called.
Did anyone else run into this problem? I saw a lot of topics in objective c threads, not much in swift. I am also somewhat struggling to understand why this is happening, so just understanding the problem would be very helpful. Thank you!
You don't seem to have handled rotations. You need to reloadData when a rotation occurs. You'll get the event in UIViewController from there you can call reloadData on the instance of your UICollectionView.
class GalleryViewController: UIViewController {
//...
var galleryScrollView: GalleryScrollView
//...
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
galleryScrollView.collectionViewLayout.invalidateLayout()
}
//...
}
Then in GalleryScrollView:
class GalleryScrollView: UIView {
var currentVisibleIndexPath = IndexPath(row: 0, section: 0)
//....
}
extension GalleryScrollView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
//...
func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
let attributes = collectionView.layoutAttributesForItem(at: currentVisibleIndexPath)
let newOriginForOldIndex = attributes?.frame.origin
return newOriginForOldIndex ?? proposedContentOffset
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let center = CGPoint(x: scrollView.contentOffset.x + (scrollView.frame.width / 2), y: (scrollView.frame.height / 2))
if let indexPath = self.screenView.indexPathForItem(at: center) {
currentVisibleIndexPath = indexPath
}
}
}
I have a UIViewController containing a UICollectionView for vertical paging. The UICollectionView (blue area in the picture) is pinned to the top of the superview, but UICollectionViewCell (yellow) is not. I tried to explicitly pin the top constraint of the UICollectionViewCell to the top of UICollectionView but the program crashes.
class HomeViewController: UIViewController, UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.register(FeedCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView.backgroundColor = .blue
collectionView.showsVerticalScrollIndicator = false
collectionView.isPagingEnabled = true
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor),
view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor)
])
collectionView.dataSource = self
collectionView.delegate = self
}
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
// MARK: UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: collectionView.bounds.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
extension HomeViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = indexPath.item % 2 == 0 ? .yellow : .orange
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
Just pin your collection view to the top of View, not to the safeArea
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true;
And use to just your collection view to safe area insets:
collectionView.contentInsetAdjustmentBehavior = .never;
Instead of pinning to all safe area edges using pinEdgesToSafeArea(to:) you should pin the top constraint of the collection view to the top constraint of view. I don't know where pinEdgesToSafeArea(to:) comes from so can't comment on a replacement for that but you can use this to pin your collection view to the top of the screen:
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
Edit:
based on the clarifications you've posted, try setting automaticallyAdjustsScrollIndicatorInsets to false:
collectionView.automaticallyAdjustsScrollIndicatorInsets = false
docs for more info
Situlation
I made a horizontal scrolling collection view with UICollectionViewFlowLayout.automaticSize.
The collection view has height of 40
The cell has height of 40 which I set by autolayou on init.
Problem
The problem is that ONLY cells displayed on first displaying the collection view
has size of 50x50 which overridden autolayout height of 40 I manually set by code.
I checked the size on willDisplayCell delegate method.
When I scroll the collection view to display other cells, each newly displayed
cells have proper size with heigh of 40.
Although the cells with 50x50 size(as logged) are being displayed correctly(with height 40 it seems) on the screen.
From this, I get error messaged(logged) when I run the app on simulator and
the app crashed on if I run on my real device(iPhone8 iOS 11).
Code
ViewController
import UIKit
class HorizontalTabNavigationViewController: UIViewController {
// MARK: - Collection View
private lazy var navigationCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = .white
cv.dataSource = self
cv.delegate = self
return cv
}()
private typealias Cell = HorizontalTabNavigationCell
private let cellId: String = "cell"
// MARK: - Property
var items: [HorizontalTabNavigationItem] = []
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.view.translatesAutoresizingMaskIntoConstraints = false
navigationCollectionView.register(Cell.self, forCellWithReuseIdentifier: cellId)
navigationCollectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
setupSubviews()
}
private func setupSubviews() {
view.addSubview( navigationCollectionView )
[
navigationCollectionView.topAnchor .constraint(equalTo: view.topAnchor),
navigationCollectionView.leftAnchor .constraint(equalTo: view.leftAnchor),
navigationCollectionView.rightAnchor .constraint(equalTo: view.rightAnchor),
navigationCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
].forEach { $0.isActive = true }
}
}
extension HorizontalTabNavigationViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! Cell
//cell.setProperties(item: items[indexPath.row])
//print("Cell for item: \(items[indexPath.row].title)")
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print("Cell Size on willDispaly cell")
print(cell.bounds.size)
print("\n")
}
}
extension HorizontalTabNavigationViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5 // Defines horizontal spacing between cells
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return .zero
}
}
Cell
class HorizontalTabNavigationCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupSelfHeight()
self.backgroundColor = .orange
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupSelfHeight() {
self.heightAnchor.constraint(equalToConstant: 40).isActive = true
self.widthAnchor.constraint(equalToConstant: 120).isActive = true
}
}
I solved the problem by setting custom size to flowlayout.
Inspired by Manav's anwer on this question below.
the behavior of the UICollectionViewFlowLayout is not defined, because the cell width is greater than collectionView width
private lazy var navigationCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
// [Here is the answer]
// Set Height equal or less than the height of the collection view.
// This is applied on first display of cells and
// will also not affect cells auto sizing.
layout.estimatedItemSize = CGSize(width: 200, height: 40) // UICollectionViewFlowLayoutAutomaticSize
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.showsHorizontalScrollIndicator = false
cv.backgroundColor = .white
cv.dataSource = self
cv.delegate = self
return cv
}()
I'm developing an application without storyboards/xibs, which is proving to be a challenge. I have a UICollectionView (of ads) where users can tap on a cell to navigate to a new view controller to show details for that ad. Below is a gif of the error I'm having.
As you can see, the UICollectionView kind of sticks around after I tap the cell. I have a adDetailsViewController property in my AdsViewController subclass, and when I tap the cell this code executes:
adDetailsViewController.adTitle = adTitles[indexPath.row]
navigationController?.pushViewController(adDetailsViewController, animated: true)
Below are the life cycle methods (currently just viewDidLoad) of adDetailsViewController:
class AdDetailsViewController: UIViewController {
var adTitle: String?
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: view.bounds)
label.textAlignment = .center
label.center = view.center
label.text = adTitle
view.addSubview(label)
}
}
For completeness, here's the rest of AdsViewController:
class AdsViewController: UIViewController {
lazy var collectionView: AdsCollectionView = {
let collectionView = AdsCollectionView(frame: view.frame, collectionViewLayout: TiledFlowLayout())
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(AdCollectionViewCell.self, forCellWithReuseIdentifier: "AdCell")
view.addSubview(collectionView)
return collectionView
}()
let adDetailsViewController = AdDetailsViewController()
let adTitles = ["1", "2", "3", "4", "5", "6"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
view.backgroundColor = .white
}
}
extension AdsViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return adTitles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AdCell", for: indexPath) as! AdCollectionViewCell
cell.textLabel.text = adTitles[indexPath.row]
return cell
}
}
extension AdsViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
adDetailsViewController.adTitle = adTitles[indexPath.row]
navigationController?.pushViewController(adDetailsViewController, animated: true)
}
}
extension AdsViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (self.view.frame.size.width / 2) - 10, height: self.view.frame.size.height / 4)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
}
}
Ok, I fixed it. It seems that if I set the backgroundColor of AdDetailsViewController to UIColor.white, then it transitions as expected.
You have to share more of your code. Are there any special settings f.e.? Like a transparent background? Anything you do in viewDidLoad? Did you overwrite viewWillAppear, viewDidAppear, etc. and did not call the subclass functions (like super.viewWillAppear(animated))? That's a very common mistake...
My requirement is to have a slider collection view on top of my view in ViewController. I've tried many work around for my requirement (You can see that in my collection view setup code). But, time I receive the warning about The behavior of the UICollectionViewFlowLayout is not defined. The view controller is being called from controllers array inside of a UITabBarController which in turn is embedded in a UINavigtionController. The view controller contains a collection view of height 200. I want each cell of the collection view to expand its full width and height.
Warning:
2017-09-06 12:05:19.139 collectionViewSample[3686:79491] The behavior of the UICollectionViewFlowLayout is not defined because:
2017-09-06 12:05:19.139 collectionViewSample[3686:79491] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.
2017-09-06 12:05:19.140 collectionViewSample[3686:79491] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7ff4e4c00020>, and it is attached to <UICollectionView: 0x7ff4e5032600; frame = (0 64; 414 200); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600000047290>; layer = <CALayer: 0x60000002d240>; contentOffset: {0, -64}; contentSize: {0, 0}> collection view layout: <UICollectionViewFlowLayout: 0x7ff4e4c00020>.
2017-09-06 12:05:19.140 collectionViewSample[3686:79491] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
AppDelegate:
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let mainController = UITabBarController()
let navController = UINavigationController(rootViewController: ViewController())
mainController.viewControllers = [navController]
window?.rootViewController = mainController
ViewController:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupCollectionView()
}
func setupCollectionView() {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.scrollIndicatorInsets = .zero
collectionView.contentInset = .zero
collectionView.preservesSuperviewLayoutMargins = false
collectionView.layoutMargins = .zero
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
collectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
collectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
collectionView.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
cell.backgroundColor = .green
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 0, 0, 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 200)
}
}
When the cells are populated with image that fills the entire frame of the collection view it leaves a small gap on top from navigation bar. Collection view frame does extend to my requirements but the cells are not. How would I fix this the proper way?