How to configure spacing in a collection view cell that is the same as the leading edge with the navigation bar title? - uicollectionview

I have a UICollectionViewController where I want to display collection view cells where the left edge is aligned with the title of the navigation bar. I certainly don't want to hardcode the spacing of CGFloat 20 due to various device screen sizes. Given from my code below, the implementation looks fine on an iPhone 8+ and iPhone 11 Pro Max, but not the iPhone 11 (the collection view cell left edge is more to the right than the edge of the navigation bar title, as shown from the image below my code).
Does anyone know of a method to programmatically (or through Storyboards) align the leading edge of both the navigation bar title and collection view cell?
private let reuseIdentifier = "Cell"
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
private let spacing: CGFloat = 20 // CGFloat of 20 works well for the iPhone 8+ and iPhone 11 Pro Max, but not the iPhone 11 Pro
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
// Setup layout
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
layout.minimumLineSpacing = spacing
layout.minimumInteritemSpacing = spacing
self.collectionView?.collectionViewLayout = layout
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 16
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = .systemFill
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberOfItemsPerRow: CGFloat = 2
let spacingBetweenCells: CGFloat = spacing
let totalSpacing = (2 * self.spacing) + ((numberOfItemsPerRow - 1) * spacingBetweenCells) // Amount of total spacing in a row
if let collection = self.collectionView {
let width = (collection.bounds.width - totalSpacing) / numberOfItemsPerRow
return CGSize(width: width, height: width)
} else {
return CGSize(width: 0, height: 0)
}
}
}

I thought of a neat solution. Maybe a workaround but I am satisfied.
// Change private let spacing: CGFloat = 20 to
private var spacing = CGFloat()
// Upon loading, spacing will be initialized depending on screen width
override func viewDidLoad() {
super.viewDidLoad()
// Added code
if UIScreen.main.bounds.width <= 375 {
spacing = 16 // For the iPhone 7, 8, 11 Pro
} else {
spacing = 20 // For the iPhone 7+, 8+ and 11 Pro Max
}
}

Related

Collection view cell not appear after resize the CollectionView height

I am facing the UICollectionView cell size issue. I am using two collection_view and one table_view 1:- Main CollectionView ,which is use for swipe and only display one cell at one time 2:- Inside Main Collection_View, i use table_view and now further inside table_view I am using Collection view which display 6 cell at one time.
Now the whole process is I am using a layout which is swipable and display 6 item at one page and so on.
The problem is when I set main cell height 200 then inside collectionview cell not appearing as expected.
Outer CollectionView Class
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var collectionMain: UICollectionView!
#IBOutlet weak var tableView: UITableView!
let itemsPerRow: CGFloat = 1
let sectionInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController:UICollectionViewDataSource,UICollectionViewDelegate {
//1
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
//2
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return 3
}
//3
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:cellCollectionCollectionViewCell! = collectionView.dequeueReusableCell(withReuseIdentifier: "cellSwipe",
for: indexPath) as! cellCollectionCollectionViewCell
switch indexPath.item {
case 0:
cell.backgroundColor = UIColor.black
cell.setupCollectionTableCell(indexPath)
break
case 1: cell.backgroundColor = UIColor.red
cell.setupCollectionTableCell(indexPath)
break
default:
cell.backgroundColor = UIColor.yellow
cell.setupCollectionTableCell(indexPath)
break
}
// Configure the cell
return cell
}
}
extension ViewController : UICollectionViewDelegateFlowLayout {
//1
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
//2
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = self.collectionMain.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: widthPerItem)
}
//3
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets
}
// 4
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return sectionInsets.left
}
}
Model Constant class
import UIKit
class Constant {
static let totalItem: CGFloat = 9
static let column: CGFloat = 3
static let minLineSpacing: CGFloat = 1.0
static let minItemSpacing: CGFloat = 1.0
static let offset: CGFloat = 1.0 // TODO: for each side, define its offset
static func getItemWidth(boundWidth: CGFloat) -> CGFloat {
let totalWidth = boundWidth - (offset + offset) - ((column - 1) * minItemSpacing)
return totalWidth / column
}
}
Table Cell Class
import UIKit
class CollectionCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.sectionInset = UIEdgeInsetsMake(
Constant.offset, // top
Constant.offset, // left
Constant.offset, // bottom
Constant.offset // right
)
layout.minimumInteritemSpacing = Constant.minItemSpacing
layout.minimumLineSpacing = Constant.minLineSpacing
}
collectionView.isScrollEnabled = false
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension CollectionCell: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Int(Constant.totalItem)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BoxCell", for: indexPath)
return cell
}
}
extension CollectionCell: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemWidth = Constant.getItemWidth(boundWidth: collectionView.bounds.size.width)
return CGSize(width: itemWidth, height: 50)
}
}
Main CollectionCell class
import UIKit
class cellCollectionCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var tblView: UITableView!
var indexPath:IndexPath?
func setupCollectionTableCell(_ row:IndexPath){
self.indexPath = row
self.tblView.delegate = self
self.tblView.dataSource = self
// self.tblView.estimatedRowHeight = 300
self.tblView.reloadData()
}
}
extension cellCollectionCollectionViewCell:UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0:
return 50
case 1:
let itemHeight = Constant.getItemWidth(boundWidth: tableView.bounds.size.width)
let totalRow = ceil(Constant.totalItem / Constant.column)
let totalTopBottomOffset = Constant.offset + Constant.offset
let totalSpacing = CGFloat(totalRow - 1) * Constant.minLineSpacing
let totalHeight = ((itemHeight * CGFloat(totalRow)) + totalTopBottomOffset + totalSpacing)
print("total height \(totalHeight)")
return totalHeight
default:
return UITableViewAutomaticDimension
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// switch indexPath.row {
// case 0:
// let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath)
// return cell
// default:
// let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionCell", for: indexPath) as! CollectionCell
// return cell
// }
let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionCell", for: indexPath) as! CollectionCell
return cell
}
When I set height of Main Collection view 200
Bellow are the link of images
When I set height 300, working fine

Swift iOS- How to Add Button to the outside a CollectionView Cell

I want to get a deleteButton that is a delete image to sit on the outside of a collectionViewCell (it can be partially inside).
I looked at other SO answers and they said just play with the button's frame's Rect's x & y values which I did. When I set the x & y values to 0,0 I get:
class CustomCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
var deleteButton: UIButton!
var deleteButtonImg: UIImage!
override func awakeFromNib() {
super.awakeFromNib()
deleteButton = UIButton(frame: CGRect(x: 0, y: 0, width: frame.size.width/4, height: frame.size.width/4))
deleteButtonImg = UIImage(named: "delete-icon")!.withRenderingMode(.alwaysTemplate)
deleteButton.setImage(deleteButtonImg, for: .normal)
deleteButton.tintColor = UIColor.red
contentView.addSubview(deleteButton)
}
When I try to set the the Rect's x & y to -10,-10 the deleteButton gets clipped inside the cell's imageView. I don't have clipsToBounds set.
deleteButton = UIButton(frame: CGRect(x: -10, y: -10, width: frame.size.width/4, height: frame.size.width/4))
I even tried setting clipToBounds to false but I get the same effect as pic # 3.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.imageView.image = myImageArray[indexPath.row]
cell.imageView.clipsToBounds = false
return cell
}
Where am I going wrong?
The problem was inside storyboard I had a:
CollectionView
-CustomCell (type collectionViewCell)
-imageView
Under the Attributes Inspector, in the Drawing section, all 3 of these have the property clipsToBounds.
I overlooked the fact that I had clipsToBounds set to false (unchecked) on the imageView but set to true (checked) on the CustomCell and the CollectionView.
If anyone else has this problem make sure you uncheck clipsToBounds on the collectionView, collectionViewCell, and the imageView.
okay u can do like below, first create a xib file and replace default view with collection view cell and set up like below,
in the above image set the class name to CustomCell (In my case) and place image view (green ) and button (blue).
and in View controller,
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//register the nib to collectionview
self.aColectionView.register(UINib(nibName: "CustomCell", bundle: nil), forCellWithReuseIdentifier: "CUSTOM_CELL")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 40
}
//cell creation as u are doing
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:CustomCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CUSTOM_CELL", for: indexPath) as! CustomCell
//set the image for custom cell
return cell
}
//return the required size
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 200, height: 150)
}
and output like below,
and in custom cell class, u add any other features like action for a button and other views related code
class CustomCell:UICollectionViewCell {
#IBOutlet weak var deleteButton: UIButton!
#IBOutlet weak var imageView: UIImageView!
//other view related code
}

CollectionView does not work when embedded in Navigation Controller

I have a simple collection view test (based on an online tutorial) which works fine stand alone. But when I embed it in a navigation controller it stops working. I built the screen in code by (1) creating a headerView (64 pixel high) and added it to the view at the top. (2) I built a collection view and added it to the headerView.
Here is the code:
import UIKit
class ViewController: UIViewController,
UICollectionViewDelegate, UICollectionViewDataSource,
UINavigationControllerDelegate
{
var collectionView : UICollectionView!
var topView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
var frame = CGRect(x:0,y:128, width:view.frame.width, height:64)
topView = UIView(frame:frame)
self.view.addSubview(topView)
// CollectionView
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
layout.itemSize = CGSize(width: 50, height: 50)
frame = CGRect(x: 0, y: 0, width: Int(self.topView.frame.width), height: Int(self.topView.frame.height))
collectionView = UICollectionView (frame: frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionCell")
collectionView.backgroundColor = UIColor.green
self.topView.addSubview(collectionView)
}
//MARK: - CollectionView
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath as IndexPath)
for v in cell.subviews {
v.removeFromSuperview()
}
cell.backgroundColor = UIColor.orange
let label = UILabel(frame: CGRect(x:0 , y:0 , width:50 , height:50))
label.text = "\(indexPath.item)"
label.textAlignment = .center
label.textColor = UIColor.white
cell.addSubview(label)
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I couldn't make Kaushal's suggestion work as stated. It did give me a clue that my positioning of the view in which I had embedded the collection view was being misplaced in viewDidLoad for reasons that I have not understood. However by putting the collection view configuration in viewDidAppear (rather than viewDidLoad) it worked well. I offset the y position by 64 to clear the navbar, and I reduced the row height to 64. I also, put code to execute the code only once so that navigating from pages does not add multiple views of top of each other. BTW, my original objective was to have horizontally scrolling cells. In my program I have tableview with corresponding sections and the idea is to use the row with horizontally scrolling cells to move to a corresponding section.
The code is shown below:
//
// CustomViewController.swift
// DSM Tracker
//
// Created by Syed Tariq on 1/7/17.
// Copyright © 2017 com.syedtariq. All rights reserved.
//
import UIKit
class ViewController: UIViewController,
UICollectionViewDelegate,
UICollectionViewDataSource,
UINavigationControllerDelegate
{
var executeOnce = true
var cellDimensions = [String:Int]()
var cellHeight = 50
var cellWidth = 120
var collectionContainerView: UICollectionView!
var navBar: UINavigationBar = UINavigationBar()
// view constants
var viewY = CGFloat()
var viewX = CGFloat()
var viewWidth = CGFloat()
var viewHeight = CGFloat()
// gaps from view edge
let leftGap = CGFloat(20)
let rightGap = CGFloat(20)
// navbar constants
let navBarHeight = CGFloat(64)
var headerLabels = ["Cell 01","Cell 02","Cell 03","Cell 04","Cell 05","Cell 06","Cell 07","Cell 08","Cell 09","Cell 10","Cell 11","Cell 12","Cell 13","Cell 14","Cell 15","Cell 16"]
override func viewDidLoad() {
super.viewDidLoad()
navBar.backgroundColor = UIColor.green
executeOnce = true
viewY = view.frame.origin.y
viewX = view.frame.origin.x
viewWidth = view.frame.width
viewHeight = view.frame.height
}
func configureCollectionView () {
if executeOnce {
executeOnce = false
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
layout.itemSize = CGSize(width: cellWidth, height: cellHeight)
let colWidth = viewWidth - leftGap - rightGap
let colX = viewX + leftGap
let colY = viewY + navBarHeight
let colHeight = CGFloat(64)
let frame = CGRect(x:colX, y:colY, width: colWidth, height: colHeight)
//let frame = CGRect.zero
collectionContainerView = UICollectionView (frame: frame, collectionViewLayout: layout)
collectionContainerView.dataSource = self
collectionContainerView.delegate = self
collectionContainerView.autoresizingMask = [.flexibleLeftMargin,.flexibleLeftMargin,.flexibleBottomMargin,.flexibleRightMargin, .flexibleHeight, .flexibleWidth]
collectionContainerView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionCell")
collectionContainerView.backgroundColor = UIColor.blue
collectionContainerView.allowsSelection = true
collectionContainerView.isScrollEnabled = true
collectionContainerView.setNeedsDisplay()
print("collectionContainerView.frame \(collectionContainerView.frame)")
view.addSubview(collectionContainerView)
}
}
override func viewDidAppear(_ animated: Bool) {
configureCollectionView()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("headerLabels.count \(headerLabels.count)")
return headerLabels.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath as IndexPath)
for v in cell.subviews {
v.removeFromSuperview()
}
let cellTitle = headerLabels[indexPath.row]
let cellTitleLines = cellTitle.components(separatedBy: " ")
let nLabels = cellTitleLines.count
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
let labelHeight = cellHeight / cellTitleLines.count
for i in (0 ..< nLabels) {
let frame = CGRect(x: 0, y: labelHeight * i, width: cellWidth, height: labelHeight)
let label1 = UILabel(frame: frame)
cell.backgroundColor = UIColor.lightGray
label1.numberOfLines = 1
label1.text = headerLabels[indexPath.row]
label1.textAlignment = .center
label1.textColor = UIColor.black
label1.clipsToBounds = true
label1.adjustsFontSizeToFitWidth = true
label1.text = cellTitleLines[i]
cell.addSubview(label1)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
collectionContainerView.scrollToItem(at:IndexPath(item: indexPath.item, section: 0), at: .centeredHorizontally, animated: false)
return true
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionContainerView.cellForItem(at: indexPath)
print("cell = \(cell)")
collectionContainerView.scrollToItem(at:IndexPath(item: indexPath.item, section: 0), at: .centeredHorizontally, animated: false)
}
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
}

the behavior of the UICollectionViewFlowLayout is not defined, because the cell width is greater than collectionView width

2015-08-18 16:07:51.523 Example[16070:269647] the behavior of the
UICollectionViewFlowLayout is not defined because: 2015-08-18
16:07:51.523
Example[16070:269647] the item width must be less than
the width of the UICollectionView minus the section insets left and
right values, minus the content insets left and right values.
2015-08-18 16:07:51.524 Example[16070:269647] The relevant
UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout:
0x7f24d670>, and it is attached to <UICollectionView: 0x7b377200;
frame = (0 0; 768 1024); clipsToBounds = YES; autoresize = W+H;
gestureRecognizers = <NSArray: 0x7f24ce30>; animations = {
position=<CABasicAnimation: 0x7f279d60>;
bounds.origin=<CABasicAnimation: 0x7f279430>;
bounds.size=<CABasicAnimation: 0x7f279480>; }; layer = <CALayer:
0x7aec9cf0>; contentOffset: {0, 0}; contentSize: {1024, 770}>
collection view layout: <UICollectionViewFlowLayout: 0x7f24d670>.
2015-08-18 16:07:51.524 Example[16070:269647] Make a symbolic
breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch
this in the debugger.
That is what I get and what I do
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(self.collectionView!.frame.size.width - 20, 66)
}
I rotate from landscape to portrait then the console shows this error message only in iOS 9 if anyone knows what happens and if there is a fix for this?
select collectionview in storyboard go to size inspector change estimate size to none
This happens when your collection view resizes to something less wide (go from landscape to portrait mode, for example), and the cell becomes too large to fit.
Why is the cell becoming too large, as the collection view flow layout should be called and return a suitable size ?
collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath)
Update to include Swift 4
#objc override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{ ... }
This is because this function is not called, or at least not straight away.
What happens is that your collection view flow layout subclass does not override the shouldInvalidateLayoutForBoundsChange function, which returns false by default.
When this method returns false, the collection view first tries to go with the current cell size, detects a problem (which logs the warning) and then calls the flow layout to resize the cell.
This means 2 things :
1 - The warning in itself is not harmful
2 - You can get rid of it by simply overriding the shouldInvalidateLayoutForBoundsChange function to return true.
In that case, the flow layout will always be called when the collection view bounds change.
XCode 11
For me, the reason was that the CollectionView's Estimate option size was set to Automatic.
It seems it is the default behavior in XCode 11.
Hence for me which I loaded the images in the cells, because of the size of the images and the automatic option (which wants to adapt the size of the cell with the size of the image) the cell's width became greater than the CollectionView's width.
By setting this option to None the problem solved.
I've also seen this occur when we set the estimatedItemSize to automaticSize and the computed size of the cells is less than 50x50 (Note: This answer was valid at the time of iOS 12. Maybe later versions have it fixed).
i.e. if you are declaring support for self-sizing cells by setting the estimated item size to the automaticSize constant:
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
and your cells are actually smaller than 50x50 points, then UIKit prints out these warnings. FWIW, the cells are eventually sized appropriately and the warnings seem to be harmless.
One fix workaround is to replace the constant with a 1x1 estimated item size:
flowLayout.estimatedItemSize = CGSize(width: 1, height: 1)
This does not impact the self-sizing support, because as the documentation for estimatedItemSize mentions:
Setting it to any other value causes the collection view to query each cell for its actual size using the cell’s preferredLayoutAttributesFitting(_:) method.
However, it might/might-not have performance implications.
I've solve this problem by using safeAreaLayoutGuide.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (view.safeAreaLayoutGuide.layoutFrame.width), height: 80);
}
and you also have to override this function to support portrait and landscape mode correctly.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionView?.collectionViewLayout.invalidateLayout();
}
I am using a subclass of UICollectionViewFlowLayout and using its itemSize property to specify the cell size (instead of the collectionView:sizeForItemAtIndexPath: delegate method). Every time I rotate the screen to a shorter width one (e.g. landscape -> portrait) I get this huge warning in question.
I was able to fix it by doing 2 steps.
Step 1: In UICollectionViewFlowLayout subclass's prepareLayout() function, move super.prepareLayout() to after where self.itemSize is set. I think this makes the super class to use the correct itemSize value.
import UIKit
extension UICollectionViewFlowLayout {
var collectionViewWidthWithoutInsets: CGFloat {
get {
guard let collectionView = self.collectionView else { return 0 }
let collectionViewSize = collectionView.bounds.size
let widthWithoutInsets = collectionViewSize.width
- self.sectionInset.left - self.sectionInset.right
- collectionView.contentInset.left - collectionView.contentInset.right
return widthWithoutInsets
}
}
}
class StickyHeaderCollectionViewLayout: UICollectionViewFlowLayout {
// MARK: - Variables
let cellAspectRatio: CGFloat = 3/1
// MARK: - Layout
override func prepareLayout() {
self.scrollDirection = .Vertical
self.minimumInteritemSpacing = 1
self.minimumLineSpacing = 1
self.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: self.minimumLineSpacing, right: 0)
let collectionViewWidth = self.collectionView?.bounds.size.width ?? 0
self.headerReferenceSize = CGSize(width: collectionViewWidth, height: 40)
// cell size
let itemWidth = collectionViewWidthWithoutInsets
self.itemSize = CGSize(width: itemWidth, height: itemWidth/cellAspectRatio)
// Note: call super last if we set itemSize
super.prepareLayout()
}
// ...
}
Note that the above change will somehow make the layout size change when screen rotates stops working. This is where step 2 comes in.
Step 2: Put this in the view controller that holds the collection view.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
collectionView.collectionViewLayout.invalidateLayout()
}
Now the warning is gone :)
Some Notes:
Make sure you are adding constraints to the collectionView and not using collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
Make sure you are not calling invalidateLayout in viewWillTransitionToSize() because the width of an edge-to-edge cell in landscape is larger than the collection view’s frame width in portrait. See below references.
References
Making Adaptive Forms using UICollectionView
Collection view with self-sizing cells
For Xcode 11, none of the above methods worked for me.
It turned out one need to set the Estimate Size to None in the collection view size panel.
see: https://forums.developer.apple.com/thread/123625
Its happens because your collection view cell's width is bigger than collection view width after rotation.suppose that you have a 1024x768 screen and your collection view fills the screen.When your device is landscape,your cell's width will be self.collectionView.frame.size.width - 20 =1004 and its greater than your collection view's width in portrait = 768.The debugger says that "the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values".
This works for me:
Invalidate the layout on rotation:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}
Have a separate function for calculating the available width:
Note: taking in to account both section inset and content inset.
// MARK: - Helpers
func calculateCellWidth(for collectionView: UICollectionView, section: Int) -> CGFloat {
var width = collectionView.frame.width
let contentInset = collectionView.contentInset
width = width - contentInset.left - contentInset.right
// Uncomment the following two lines if you're adjusting section insets
// let sectionInset = self.collectionView(collectionView, layout: collectionView.collectionViewLayout, insetForSectionAt: section)
// width = width - sectionInset.left - sectionInset.right
// Uncomment if you are using the sectionInset property on flow layouts
// let sectionInset = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.sectionInset ?? UIEdgeInsets.zero
// width = width - sectionInset.left - sectionInset.right
return width
}
And then of course finally returning the item size:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: calculateCellWidth(for: collectionView, section: indexPath.section), height: 60)
}
I think it is important to note that this works because invalidating a layout wont trigger a recalculation of the cell size immediately, but during the next layout update cycle. This means that once the item size callback is eventually called, the collection view has the correct frame, thus allowing accurate sizing.
Voila!
Turn your collection view's estimate size 'None' value from automatic.
You can do this by the storyboard or programatically.
But you have to calculate it manually with custom layout class or 'UICollectionViewDelegateFlowLayout' protocol APIs
After doing some experimenting, this seems to also be tied to how you layout your collectionView.
The tl;dr is: Use AutoLayout, not autoresizingMask.
So for the core of the problem the best solutions I've found to handle orientation change use the following code, which all makes sense:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
self.collectionView.collectionViewLayout.invalidateLayout()
}, completion: nil)
}
However, there are still situations where you can get the item size warning. For me it's if I am in landscape in one tab, switch to another tab, rotate to portrait, then return to the previous tab. I tried invalidating layout in willAppear, willLayout, all the usual suspects, but no luck. In fact, even if you call invalidateLayout before super.willAppear() you still get the warning.
And ultimately, this problem is tied to the size of the collectionView bounds updating before the delegate is asked for the itemSize.
So with that in mind I tried using AutoLayout instead of collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight], and with that, problem solved! (I use SnapKit to do AutoLayout so that I don't pull my hair out constantly). You also just need to invalidateLayout in viewWillTransition (without the coordinator.animate, so just as you have in your example), and also invalidateLayout at the bottom of viewWillAppear(:). That seems to cover all situations.
I dont know if you are using autoresizing or not - it would be interesting to know if my theory/solution works for everyone.
You can check in debugger if collectionView.contentOffset is changed to negative in my case it changes from (0,0) to (0,-40). You can solve this issue by using this method
if ([self respondsToSelector:#selector(setAutomaticallyAdjustsScrollViewInsets:)]) {
self.automaticallyAdjustsScrollViewInsets = NO;
}
This solution definitely works.. Try this code
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
collectionView.collectionViewLayout.invalidateLayout()
super.viewWillTransition(to: size, with: coordinator)
}
I had a similar issue.
In my case, I had a collection view and when you tapped on one of the cells, a popover with a UITextField opened, to edit the item. After that popover disappeared, the self.collectionView.contentInset.bottom was set to 55 (originally 0).
To fix my issue, after the popover view disappears, I’m manually setting contentInset to UIEdgeInsetsZero.
The original issue seems to be related to the contextual prediction bar that shows up on top of the keyboard. When the keyboard is hidden, the bar disappears, but the contentInset.bottom value is not restored to the original value.
Since your issue seems to be related to the width and not to the height of the cell, check if any of the contentInset or layout.sectionInset values are the same as the one set by you.
I had the same issue. I fixed this by clearing my collection view of it's constraints, and resetting them in Storyboard.
I could solve it by putting
super.prepare()
at end of
override func prepare() { }
I've been struggling with this problem for some hours, too.
My UICollectionViewController is embedded in a container view, when rotating the iPad from landscape to portrait it showed the error. The cells did resize though.
I solved the error by calling the following in the UICollectionViewController:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
self.collectionView.collectionViewLayout.invalidateLayout()
super.viewWillTransition(to: size, with: coordinator)
}
The "super" has to be called after invalidating the layout.
Additionally I needed to call the following in the view where the container view is embedded in:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
DispatchQueue.main.async {
self.collectionViewController.collectionView.reloadData()
}
}
This way, the cells are updated after the screen rotation is done.
It is my solution:
Remove all UICollectionViewFlowDelegate methods from your view controller.
Create your own flowLayout class as a subclass UICollectionViewFlowLayout.
class ImageCollectionViewFlowLayout: UICollectionViewFlowLayout {}
Configure all sizes and other parameters into prepare() method. See my example below.
private let sectionInsets = UIEdgeInsets(top: 50.0, left: 20.0, bottom: 50.0, right: 20.0)
override func prepare() {
super.prepare()
guard let collectionView = collectionView else { return }
self.sectionInset = sectionInsets
self.minimumLineSpacing = sectionInsets.bottom
self.minimumInteritemSpacing = sectionInsets.bottom
let width = collectionView.bounds
.inset(by: collectionView.layoutMargins)
.inset(by: self.sectionInset)
.width
self.estimatedItemSize = CGSize(width: width, height: width + 16)
//self.itemSize - I do not set itemsize because my cell has dynamical height
}
Set your new class for collection view on your storyboard(or xib).
That's all. Keep your code clean.
if you're a using Dynamic Types for fonts, just reload a collectionView when font size is changing:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
collectionView?.reloadData()
}
I found that this worked quite well for UICollectionViewController and animated correctly:
- (void)viewWillTransitionToSize:(CGSize)size
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
// Ensure the layout is within the allowed size before animating below, or
// `UICollectionViewFlowLayoutBreakForInvalidSizes` breakpoint will trigger
// and the logging will occur.
if ([self.collectionView.collectionViewLayout isKindOfClass:[YourLayoutSubclass class]]) {
[(YourLayoutSubclass *)self.collectionView.collectionViewLayout updateForWidth:size.width];
}
// Then, animate alongside the transition, as you would:
[coordinator animateAlongsideTransition:^
(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[self.collectionView.collectionViewLayout invalidateLayout];
}
completion:nil];
[super viewWillTransitionToSize:size
withTransitionCoordinator:coordinator];
}
///////////////
#implementation YourLayoutSubclass
- (void)prepareLayout {
[super prepareLayout];
[self updateForWidth:self.collectionView.bounds.size.width];
}
- (void)updateForWidth:(CGFloat)width {
// Update layout as you wish...
}
#end
... See inline code comments above for explanation. 👍🏻
Any Changes in UIView must be done from main thread. Put your code of collectionView addition to views hierarchy into:
DispatchQueue.main.async {
...
}
In my case, for a vertical UICollectionView I had:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width, height: padding(scale: 37))
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: padding(scale: 10), left: padding(scale: 4), bottom: padding(scale: 3), right: padding(scale: 4))
}
I fixed it by resting the horizontal insets to the size:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (collectionView.bounds.width - (2 * padding(scale: 4))), height: padding(scale: 37))
}
In case you're setting the itemSize of collectionViewLayout in code, then make sure that the size is valid. Set a breakpoint on the code line where you set the itemSize, and check if the size complies to the instruction of the error warning:
The item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values.
This is what you need:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
//Get frame width
let width = self.view.frame.width
//I want a width of 418 and height of 274 (Aspect ratio 209:137) with a margin of 24 on each side of the cell (See insetForSectionAt 24 + 24 = 48). So, check if a have that much screen real estate.
if width > (418 + 48) {
//If I do return the size I want
return CGSize(width: 418, height: 274)
}else{
//Get new width. Frame width minus the margins I want to maintain
let newWidth = (width - 48)
//If not calculate the new height that maintains the aspect ratio I want. NewHeight = (originalHeight / originalWidth) * newWidth
let height = (274 / 418) * newWidth
//Return the new size that is Aspect ratio 209:137
return CGSize(width: newWidth, height: height)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 33
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 33
}

hidesBarsOnSwipe never shows navbar again when scrolling up

So I want to hide the navbar when scrolling down and bring it back when scrolling up. Hiding it works perfectly with
self.navigationController?.hidesBarsOnSwipe = true
But I expect it to be shown again when scrolling up. I made a test project where the view controller just has a single UICollectionView that covers the whole screen. Then showing the navbar is shown again as expected until I add this line to the viewDidLoad (adding cells to the collection view):
self.collectionView.delegate = self
And this is what the whole view controller looks like
class ViewController: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Test")
self.navigationController?.hidesBarsOnSwipe = true
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCellWithReuseIdentifier("Test", forIndexPath: indexPath) as UICollectionViewCell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(300, 300)
}
}
So why does showing the navbar stop working when I add cells to my collection view?
I had the same problem but with a web view.
The problem was that the top constraint of the web view was "Top Layout Guide.Top" , after changing the top constraint to "Superview.Top" the problem was solved.
To expand on Oleg's answer...
If you are using Interface Builder to set a constraint to a view controller's primary view, Xcode defaults to showing options to set the vertical constraint against the top layout guide. However, if you press 'Option', you will see an alternate set of constraints. The constraint for 'Top Space to Container' is what you're looking for.
I had same issue. When I added the code for hiding status bar along with navigation bar, it worked.
- (BOOL)prefersStatusBarHidden {
return self.navigationController.isNavigationBarHidden;
}
I tried setting hidesBarsOnSwipe property to true in my ViewController class in ViewDidLoad function as given below, but it didn't work in handling hiding the navigation bar on swipe-up and unhiding the navigation bar on swipe-down.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.hidesBarsOnSwipe = true
}
}
Setting hidesBarsOnSwipe to true will have effect only if we are using the UITableViewController or UICollectionViewController as main screens, hidesBarsOnSwipe will not work if we have added a UITableView to the UIViewController for displaying the list of data.
Solution
class TestTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.hidesBarsOnSwipe = true
}
}
Hope this answer might help...!
I filed a bug report with Apple and ended up using AMScrollingNavbar instead which works really well and is easy to setup.
As per previous comments - this seems like a bug as of ios 10.3
as you are using a uicollectionview - I draw your attention to some code I re-wrote from APDynamicHeaderTableViewController
https://github.com/aaronpang/APDynamicHeaderTableViewController/issues/4
It's using snapkit https://github.com/SnapKit/SnapKit
(Apologies to all the IB + NSLayout Constraint lovers.)
class APDynamicHeaderTableViewController : UIViewController {
var largeWideSize = CGSize(width: UIScreen.main.bounds.width , height: 285 )
let headerView = APDynamicHeaderView () // Change your header view here
let cellLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
var feedCV:UICollectionView!
fileprivate var headerViewHeight:CGFloat = 80 // this will be updated by scrolling
fileprivate var headerBeganCollapsed = false
fileprivate var collapsedHeaderViewHeight : CGFloat = UIApplication.shared.statusBarFrame.height
fileprivate var expandedHeaderViewHeight : CGFloat = 100
fileprivate var headerExpandDelay : CGFloat = 100
fileprivate var tableViewScrollOffsetBeginDraggingY : CGFloat = 0.0
init(collapsedHeaderViewHeight : CGFloat, expandedHeaderViewHeight : CGFloat, headerExpandDelay :CGFloat) {
self.collapsedHeaderViewHeight = collapsedHeaderViewHeight
self.expandedHeaderViewHeight = expandedHeaderViewHeight
self.headerExpandDelay = headerExpandDelay
super.init(nibName: nil, bundle: nil)
}
init () {
super.init(nibName: nil, bundle: nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
self.view.backgroundColor = .green
// Cell Layout Sizes
cellLayout.scrollDirection = .vertical
cellLayout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
cellLayout.itemSize = CGSize(width: UIScreen.main.bounds.width, height: 185 + 80)
// Header view
self.view.addSubview(headerView)
headerView.snp.remakeConstraints { (make) -> Void in
make.top.left.equalToSuperview()
make.width.equalToSuperview()
make.height.equalTo(headerViewHeight)
}
// CollectionView
feedCV = UICollectionView(frame: .zero, collectionViewLayout: cellLayout)
self.view.addSubview(feedCV)
self.feedCV.snp.remakeConstraints { (make) -> Void in
make.top.equalTo(headerView.snp.bottom) // this is pegged to the header view which is going to grow in height
make.left.equalToSuperview()
make.width.equalToSuperview()
make.bottom.equalToSuperview()
}
feedCV.backgroundColor = .red
feedCV.showsVerticalScrollIndicator = true
feedCV.isScrollEnabled = true
feedCV.bounces = true
feedCV.delegate = self
feedCV.dataSource = self
// YOUR COLLECTIONVIEW CELL HERE!!!!!
feedCV.register(VideoCollectionViewCell.self, forCellWithReuseIdentifier: VideoCollectionViewCell.ID)
}
// Animate the header view to collapsed or expanded if it is dragged only partially
func animateHeaderViewHeight () -> Void {
Logger.verbose("animateHeaderViewHeight")
var headerViewHeightDestinationConstant : CGFloat = 0.0
if (headerViewHeight < ((expandedHeaderViewHeight - collapsedHeaderViewHeight) / 2.0 + collapsedHeaderViewHeight)) {
headerViewHeightDestinationConstant = collapsedHeaderViewHeight
} else {
headerViewHeightDestinationConstant = expandedHeaderViewHeight
}
if (headerViewHeight != expandedHeaderViewHeight && headerViewHeight != collapsedHeaderViewHeight) {
let animationDuration = 0.25
UIView.animate(withDuration: animationDuration, animations: { () -> Void in
self.headerViewHeight = headerViewHeightDestinationConstant
let progress = (self.headerViewHeight - self.collapsedHeaderViewHeight) / (self.expandedHeaderViewHeight - self.collapsedHeaderViewHeight)
self.headerView.expandToProgress(progress)
self.view.layoutIfNeeded()
})
}
}
}
extension APDynamicHeaderTableViewController : UICollectionViewDelegate {
}
extension APDynamicHeaderTableViewController : UIScrollViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
// Clamp the beginning point to 0 and the max content offset to prevent unintentional resizing when dragging during rubber banding
tableViewScrollOffsetBeginDraggingY = min(max(scrollView.contentOffset.y, 0), scrollView.contentSize.height - scrollView.frame.size.height)
// Keep track of whether or not the header was collapsed to determine if we can add the delay of expansion
headerBeganCollapsed = (headerViewHeight == collapsedHeaderViewHeight)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Do nothing if the table view is not scrollable
if feedCV.contentSize.height < feedCV.bounds.height {
return
}
var contentOffsetY = feedCV.contentOffset.y - tableViewScrollOffsetBeginDraggingY
// Add a delay to expanding the header only if the user began scrolling below the allotted amount of space to actually expand the header with no delay (e.g. If it takes 30 pixels to scroll up the scrollview to expand the header then don't add the delay of the user started scrolling at 10 pixels)
if tableViewScrollOffsetBeginDraggingY > ((expandedHeaderViewHeight - collapsedHeaderViewHeight) + headerExpandDelay) && contentOffsetY < 0 && headerBeganCollapsed {
contentOffsetY = contentOffsetY + headerExpandDelay
}
// Calculate how much the header height will change so we can readjust the table view's content offset so it doesn't scroll while we change the height of the header
let changeInHeaderViewHeight = headerViewHeight - min(max(headerViewHeight - contentOffsetY, collapsedHeaderViewHeight), expandedHeaderViewHeight)
headerViewHeight = min(max(headerViewHeight - contentOffsetY, collapsedHeaderViewHeight), expandedHeaderViewHeight)
let progress = (headerViewHeight - collapsedHeaderViewHeight) / (expandedHeaderViewHeight - collapsedHeaderViewHeight)
// Logger.verbose("headerViewHeight:",headerViewHeight)
headerView.expandToProgress(progress)
headerView.snp.updateConstraints { (make) -> Void in
make.height.equalTo(headerViewHeight)
}
// When the header view height is changing, freeze the content in the table view
if headerViewHeight != collapsedHeaderViewHeight && headerViewHeight != expandedHeaderViewHeight {
feedCV.contentOffset = CGPoint(x: 0, y: feedCV.contentOffset.y - changeInHeaderViewHeight)
}
}
// Animate the header view when the user ends dragging or flicks the scroll view
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
animateHeaderViewHeight()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
animateHeaderViewHeight()
}
}
extension APDynamicHeaderTableViewController : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: VideoCollectionViewCell.ID, for: indexPath) as! VideoCollectionViewCell
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return largeWideSize
}
}
To make hidesBarsOnSwipe working properly, your view controller's view must contain only UITableView instance and nothing else.