I have a horizontally scrolling UICollectionView using the flow layout that has its delegate and datasource set. It has a height of 50 and is pinned to my view using autolayout. Unfortunately, it never displays and always gives the following error:
2020-10-09 21:30:08.687110+0100 MyApp[8679:1601772] The behavior of the UICollectionViewFlowLayout is not defined because:
2020-10-09 21:30:08.687428+0100 MyApp[8679:1601772] 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.
2020-10-09 21:30:08.687743+0100 MyApp[8679:1601772] Please check the values returned by the delegate.
2020-10-09 21:30:08.688641+0100 MyApp[8679:1601772] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x105dc6350>, and it is attached to <UICollectionView: 0x10611d000; frame = (0 0; 375 90); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x28372ccf0>; layer = <CALayer: 0x2839465c0>; contentOffset: {0, 0}; contentSize: {1081.6666666666667, 90}; adjustedContentInset: {0, 0, 0, 0}> collection view layout: <UICollectionViewFlowLayout: 0x105dc6350>.
2020-10-09 21:30:08.688943+0100 MyApp[8679:1601772] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
I've noticed that it is saying the contentSize is {1081.6666666666667, 90} but it actually only calls the numberOfSections and numberOfItemsInSection delegate methods. The others aren't called, and neither is the UICollectionViewDelegateFlowLayout sizing method. Why is this and how is it getting a contentSize without actually calling the datasource/delegate methods? It makes no difference if I set the autolayout height from 50 to 90.
My collectionView is set up in the storyboard. I have registered my cell and set the estimated flow layout size like so:
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(
UINib(nibName: "VideoClipCell", bundle: nil),
forCellWithReuseIdentifier: "VideoClipCell"
)
if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: 30, height: 50)
}
}
These are the datasource/flow delegate methods:
extension EditingViewController : UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "VideoClipCell", for: indexPath) as? VideoClipCell else {
return UICollectionViewCell()
}
return cell
}
}
extension EditingViewController: UICollectionViewDelegateFlowLayout {
func collectionView(
_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 50)
}
}
Do you have an UIImageView in your cell? That's probably why you're getting 1081.6666666666667 for the content size.
You probably don't want self-sizing cells, so turn off "Estimate Size" in the storyboard. Otherwise, your collection view won't read what's inside sizeForItemAt.
Can you show your UICollectionView`s init func,i guess you did not set the UICollectionView delegate to self,and the layout itemsize height is larger than 90
Related
I'm setting a collection View in order to display content as on instagram. instagram's grid
In order to set the layout I'm using some delegate methods of UIcollectionviewDelegateFlowLayout :
extension ProfileViewController : UICollectionViewDelegateFlowLayout{
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
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (view.frame.width)/3, height: (view.frame.width)/3)
}
}
When I open the project, the collectionView works well.
The problem occurs when you scroll down very fast the collectionView so that you make momentaneously disappear all the cells.
In that exact moment the cell layout I've set before doesn't hold and the images become ultra zoomed and one on top of each other, like this: messed Up collectionView
I don't know how to fix this issue.
Also in the compiler I get this error :
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.
Please check the values returned by the delegate.
The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7fbbe972d1f0>, and it is attached to <UICollectionView: 0x7fbbeb056400; frame = (0 0; 414 896); clipsToBounds = YES; autoresize = RM+BM;
gestureRecognizers = <NSArray: 0x60000203c4e0>; layer = <CALayer: 0x600002ef19c0>; contentOffset: {0, 424}; contentSize: {414, 16048}; adjustedContentInset: {88, 0, 83, 0}; layout: <UICollectionViewFlowLayout: 0x7fbbe972d1f0>; dataSource: <InstagramClone.ProfileViewController: 0x7fbbe9708890>>.
I think your problem could be the same as the problem in this post.
Try fixing your problem by setting the UICollectionView's estimatedSize to none.
This is because in Xcode 11 Cells in a CollectionView can now self-size with Auto Layout constrained views in the canvas.
I have a collectionView and each cell contains a tableView inside it. The gray portion is supposed to be the collectionView.
But when I run the app, I get something like this
As you can clearly see that the table view present doesn't stick to the constraints set. I get the following errors from the console.
The behavior of the UICollectionViewFlowLayout is not defined because:
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.
The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7f80fd28e420>, and it
is attached to <UICollectionView: 0x7f80fc08f800; frame = (0 114; 375 664); clipsToBounds = YES;
autoresize = RM+BM; gestureRecognizers = <NSArray: 0x600000d6c4b0>; layer = <CALayer: 0x6000003beee0>;
contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0};
layout: <UICollectionViewFlowLayout: 0x7f80fd28e420>; dataSource: <BizTiz.EventDetailController: 0x7f80fa0b7800>>.
Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
How do I fix this? Also when I push this collectionView onto the stack, there is a small lag. I am guessing this might be due to the previous error.
Please set layout in the UICollectionViewDelegateFlowLayout.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: <cell_width>, height: <cell_height>)
}
Realized my mistake....
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height - 114)
}
I was setting the height as view.frame.height. I had forgotten to subtract the extra height at top.
I have been working on an app that has a UICollectionView that works like the main screen with the UICollectionViewCells acting as different pages (scrolling horizontally). I have added a text field on each cell to edit, but when I click on the textfield, the cell height is extended when the keyboard appears. When the keyboard is hidden the cell height remains extended. I have been searching for an answer to this problem, but I have not come across a solution that works.
I have tried to invalidate the layout, set the translatesAutoresizingMaskIntoConstraints to false, and directly setting the content offset to zero. None of these options have fixed the issue.
Below is my code:
private let reuseIdentifier = ["Cell1", "Cell2", "Cell3", "Cell4", "Cell5"]
let navi_btn_array: [UIButton] = [navi_home_btn, navi_gavel_btn, navi_orders_btn, navi_profile_btn, navi_lightning_btn]
var collectionView: UICollectionView = {
var layout = UICollectionViewFlowLayout();
layout.sectionInset = UIEdgeInsets.zero
var cv = UICollectionView(frame: .zero, collectionViewLayout: layout);
cv.autoresizesSubviews = false
cv.contentInset = UIEdgeInsets.zero
cv.translatesAutoresizingMaskIntoConstraints = false;
return cv;
}();
class MainCVC: UICollectionViewController,UICollectionViewDelegateFlowLayout, CLLocationManagerDelegate, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.register(SimpleDispensaryPage_Cell.self, forCellWithReuseIdentifier: reuseIdentifier[0])
collectionView?.delegate = self
collectionView?.dataSource = self
collectionView?.isPagingEnabled = true
let nc:NotificationCenter = NotificationCenter.default
nc.addObserver(self, selector: #selector(keyboardDidShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
nc.addObserver(self, selector: #selector(keyboardDidHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}
#objc func keyboardDidShow(notification: Notification){
collectionViewLayout.invalidateLayout()
collectionView?.contentOffset.y = 0
}
#objc func keyboardDidHide(notification: Notification){
collectionViewLayout.invalidateLayout()
collectionView?.contentOffset.y = 0
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier[0], for: indexPath)
cell.backgroundColor = Custom_Colors.color_pine.withAlphaComponent(0.5)
var txt_fld = UITextField()
txt_fld.frame = CGRect(x: 0, y: 50, width: 100, height: 50)
cell_label.text = "Placeholder #"+String(indexPath.item)
cell_label.textAlignment = .center
cell.addSubview(cell_label)
return cell
}
}
}
I also get this error when it runs:
2019-03-14 11:41:20.770799-0700 app[57379:5390785] 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.
2019-03-14 11:41:20.771043-0700 app[57379:5390785] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7fda15c13f40>, and it is attached to <UICollectionView: 0x7fda1706f000; frame = (0 0; 375 730.8); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000447bc0>; layer = <CALayer: 0x600000437720>; contentOffset: {8, -38.333333333333336}; contentSize: {1891, 579}; adjustedContentInset: {0, 0, 151.79999999999995, 0}> collection view layout: <UICollectionViewFlowLayout: 0x7fda15c13f40>.
2019-03-14 11:41:20.771245-0700 app[57379:5390785] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
You have a little bit of strange setup here. I suggest making a couple of changes:
1 - Never add subviews in collectionView(_, cellForItemAt ...). This would result in multiple additions every time the cell is being reused. The subviews should be added by the cell (and preferably at creation time).
2 - Remove cv.autoresizesSubviews = false and cv.translatesAutoresizingMaskIntoConstraints = false.
3 - If you want a fixed size for your cells you can set the layout.delegate = self and implement the following method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: screenWidth, height: screenHeight)
}
4 - If you really just want a couple of pages why don't you use UIPageViewController?
Here's a UICollectionView, and the cell in purple:
Quite simply, I want the cells to be 1/2 of the collection view width. (So TBC, it will be a two rows arrangement of cells in the collection view.)
(The collection view is simply fullscreen, so each cell is half the screen width.)
How do you do this in storyboard?
If I try to control-drag in the normal way, it basically doesn't work.
These are simple totally static cells (not dynamic).
For anyone googling here, to save your time: Here's exactly (2016) the simplest way to make a two-across UICollectionView layout; no gaps between the cells.
// Two - two-across UICollectionView
// use a completely standard UIViewController on the storyboard,
// likely change scroll direction to vertical.
// name the cell identifier "cellTwo" on the storyboard
import UIKit
class Two:UICollectionViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
let w = collectionView!.bounds.width / 2.0
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width:w,height:w)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
collectionView!.collectionViewLayout = layout
// Note!! DO NOT!!! register if using a storyboard cell!!
// do NOT do this:
// self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int
{ return 1 }
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{ return 5 }
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
return collectionView.dequeueReusableCellWithReuseIdentifier("cellTwo", forIndexPath: indexPath)
}
}
You can't do it in the storyboard. The collection view width is not known until runtime, and collection view cells are not under autolayout, so you cannot express the notion "1/2 the width" of anything else. (If you did know the collection view width in advance, you could use the flow layout in the storyboard to set the cell size absolutely, by dividing in your head; but you don't know it, because the width differs depending on the device.)
I have an UITableView and I want to add a UICollectionView with a horizontal flow layout as subview in the backgroundView of the tableView, to do the same effect of the AppStore. Here I have the implementation code:
in the viewDidLoad:
UIView *tableViewBackgroundView = [[UIView alloc] initWithFrame:self.view.bounds];
self.tableView.backgroundView = tableViewBackgroundView;
// HighlightView heightFactor returns the reason which is 9.0/16.0
CGFloat headerViewHeight = CGRectGetWidth(self.view.frame) * [HighlightView heightFactor];
self.tableView.contentInset = UIEdgeInsetsMake(headerViewHeight, 0, 0, 0);
self.headerView = [[HighlightView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), headerViewHeight)];
[tableViewBackgroundView addSubview:self.headerView];
The HighlightView is a view with a collectionView inside.
But I'm having an issue, when the user interacts with the collectionView I start to receive this log:
Please check the values return by the delegate. the behavior of the
UICollectionViewFlowLayout is not defined because: the item height
must be less than the height of the UICollectionView minus the section
insets top and bottom values.
And this becomes a loop that doesn't stop even when the user stops to interact.
HighlightView (CollectionView) code:
override init(frame: CGRect) {
super.init(frame: frame)
self.viewModel.delegate = self
self.configureHighlightsCollectionView()
}
func configureHighlightsCollectionView() {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = UICollectionViewScrollDirection.Horizontal
flowLayout.minimumInteritemSpacing = 0
flowLayout.minimumLineSpacing = 0
if systemVersion > 8.0 {
flowLayout.estimatedItemSize = self.frame.size
}
flowLayout.itemSize = self.frame.size
self.highlightsCollectionView = UICollectionView(frame: self.bounds, collectionViewLayout: flowLayout)
self.highlightsCollectionView.frame = self.bounds
self.highlightsCollectionView.scrollsToTop = false
self.highlightsCollectionView.pagingEnabled = true
self.highlightsCollectionView.registerClass(CachedImageCollectionViewCell.self, forCellWithReuseIdentifier: "ImageCell")
self.addSubview(self.highlightsCollectionView)
self.highlightsCollectionView.invalidateIntrinsicContentSize()
self.highlightsCollectionView.dataSource = self
self.highlightsCollectionView.delegate = self
self.highlightsCollectionView.backgroundColor = UIColor.greenColor()
self.highlightsCollectionView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self)
make.bottom.equalTo(self)
make.left.equalTo(self)
make.right.equalTo(self)
}
}
//Mark: UICollectionViewDataSource
public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.viewModel.highlightsArray.count
}
public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ImageCell", forIndexPath: indexPath) as! CachedImageCollectionViewCell
cell.highlightData = self.viewModel.highlightsArray[indexPath.item]
return cell
}
public func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
println(self.frame.size)
return self.frame.size
}
The viewModel is a class who controls the data flow of the collectionView.
First I don't think it's a good idea to put your view as datasource and delegate of your UICollection inside your UIView subclass. You're not respecting the MVC pattern. Learn more informations about it at Introducing iOS Design Pattern . You should set your controller as it.
The problem is you're setting the itemSize of your UICollectionViewFlowLayout in initFrame: based on the frame of your view. In that method, the frame of your UIView is not correct since you're using AutoLayout. You have to wait until AutoLayout calculates the layouts of your views so when layoutSubviews: is called. Learn about UIView and AutoLayout in the Matt's book. It's for iOS 6 and in Objective - C but still great.