Thanks to help.
As shown in the above,CollectionView ItemSize.height is big than CollectionView.height.
And I just set the itemSize in viewDidLayoutSubviews()
override func viewDidLayoutSubviews() {
resizeCollectionView(size: collectionView.frame.size)
}
private func resizeCollectionView(size: CGSize){
print("collectionViewSize:\(size)")
print("viewSize:\(view.frame.size)")
if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.itemSize = CGSize(width: size.width, height: collectionView.frame.size.height)
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
collectionView?.layoutIfNeeded()
print("itemSize:\(layout.itemSize)")
}
}
The print show that CollectionViewSize is equal ItemSize, But the actual, itemSize always big than CollectioinViewSize.
Use this method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: yourWidth, height: yourCollectionView.bounds.height)
}
Related
Following is my code which is working fine if numberOfItemsInSection is greater than 1 but if numberOfItemsInSection is equal to 1 it should display single cell but it is not happening, for numberOfItemsInSection equals 1 noting is displayed and cellForItemAt is not called
Also initial numberOfItemsInSection is Zero after making api call and receiving data it becomes one and I call following code as well
tabsCollectionView.reloadData()
Other code
tabsCollectionView.delegate = self
tabsCollectionView.dataSource = self
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menuResult?.foodMenuItems?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = tabsCollectionView.dequeueReusableCell(withReuseIdentifier: "foodTabsCollectionViewCellID", for: indexPath) as! FoodTabsCollectionViewCell
cell.foodMenuItem = menuResult?.foodMenuItems?[indexPath.row]
cell.setUI()
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
following code related sizing in side viewDidLoad()
if let flowLayout = tabsCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: 200, height: 70)
flowLayout.itemSize = UICollectionViewFlowLayout.automaticSize
tabsCollectionView.collectionViewLayout = flowLayout
}
sizeForItemAt method
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var height = collectionView.frame.height
var width = collectionView.frame.width
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
width = flowLayout.estimatedItemSize.width
height = flowLayout.estimatedItemSize.height
}
return CGSize(width: width, height: height)
}
Following is code inside FoodTabsCollectionViewCell
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var frame = layoutAttributes.frame
frame.size.width = ceil(size.width)
frame.size.height = ceil(size.height)
layoutAttributes.frame = frame
layoutIfNeeded()
return layoutAttributes
}
I had the same problem when I tried to set a flexible width to a cell containing a UILabel with variable width (actually the size was related to the width of the text of the UILabel)
So initially I was using this approach (THIS was causing the problems):
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize <--- THIS
layout.itemSize.height = 70 <-- THIS
Then I removed the automaticSize (and itemSize.height) as below:
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
And it worked.
To make sure that I had flexible cell width I used this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let dummyCell = MyCell(frame: .init(x: 0, y: 0, width: frame.width, height: 40))
dummyCell.name = users[indexPath.item].name
// 3. Force the view to update its layout
dummyCell.layoutIfNeeded()
// 4. Calculate the estimated size
let estimatedSize = dummyCell.systemLayoutSizeFitting(.init(width: frame.width, height: 40))
return CGSize(width: estimatedSize.width, height: 40)
}
The problem will happen when the collectionView scroll direction is horizontal and the height of collectionView is less than 50. I had the same problem and I increased the collectionView height to 50 then the problem solved.
Pure code (I didn't use the storyboard, just use code) builds the collection but UICollectionViewDelegateFlowLayout is not executed.
class Calendarview : UIView , UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout {
var collectionview : UICollectionView!
override func didAddSubview(_ subview: UIView) {
}
override func didMoveToSuperview() {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
collectionview = UICollectionView(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height) , collectionViewLayout: layout)
collectionview.frame = self.frame
collectionview.register(CalendarCell.self, forCellWithReuseIdentifier: "Cell")
collectionview.dataSource = self
collectionview.delegate = self
self.addSubview(collectionview!)
collectionview.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 42
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: self.bounds.width / 7, height: self.bounds.height / 3)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CalendarCell
cell.lbshow = UILabel(frame: cell.bounds)
cell.lbshow.text = String(indexPath.row)
cell.addSubview(cell.lbshow)
cell.backgroundColor = .blue
return cell
}
}
Set delegate as self.collectionView.delegate = self;. UICollectionViewDelegateFlowLayout inherits from UICollectionViewDelegate. So it will called all methods of UICollectionViewDelegateFlowLayout.
Hence, You need to set self.collectionView.collectionViewLayout to your flow layout.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 10, height: 10)
layout.itemSize = CGSize(width: 200, height: 200)
layout.minimumLineSpacing = 10
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.scrollDirection = .horizontal
collectionview.collectionViewLayout = layout
I'm trying to center the uicollectionview cell while paging enable, but when I slide it, it does not stay centered! I would want it to be just like the image below!
collectionView.dataSource = self
collectionView.delegate = self
collectionView.alwaysBounceHorizontal = true
collectionView.isPagingEnabled = true
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let inset: CGFloat = calculateSectionIntet()
return CGSize(width: view.frame.width - inset * 2, height: view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 16
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let inset: CGFloat = calculateSectionIntet()
return UIEdgeInsets(top: 0, left: inset, bottom: 0 , right: inset)
}
func calculateSectionIntet() -> CGFloat {
return 32
}
Image:
Swift solution(Originally Objective-C) from a 2012 WWDC video.
Subclass UICollectionViewFlowLayout and override this function:
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else { return .zero }
// Add some snapping behaviour so that the zoomed cell is always centered
let targetRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.frame.width, height: collectionView.frame.height)
guard let rectAttributes = super.layoutAttributesForElements(in: targetRect) else { return .zero }
var offsetAdjustment = CGFloat.greatestFiniteMagnitude
let horizontalCenter = proposedContentOffset.x + collectionView.frame.width / 2
for layoutAttributes in rectAttributes {
let itemHorizontalCenter = layoutAttributes.center.x
if (itemHorizontalCenter - horizontalCenter).magnitude < offsetAdjustment.magnitude {
offsetAdjustment = itemHorizontalCenter - horizontalCenter
}
}
return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y)
}
Also for better snapping, add this to the collectionView's viewController:
collectionView.decelerationRate = .fast
I have a horizontal scrolling collectionview.The cell contains a label.I want to adjust the cell width based on the content of the label.How can this be achieved??Please help me with this
instead of calling sizeforitem
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height : 100)
}
You can set layout properties to auto resizing according to label content
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.estimatedItemSize = CGSize(width: 100, height: 30)
Use this func to calculate the label width
func widthOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSFontAttributeName: font]
let size = self.size(attributes: fontAttributes)
return size.width
}
Use UICollectionViewDelegateFlowLayout to set size of collectionViewCell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = //calculated width of label from func
return CGSize(width: width, height:requiredHeight)
}
So I have a working collection view with 2 columns and 3 items in each column. The grid view works great until I get to the iPhone 5s simulator, and then my grid view turns into one single column instead of 2. I've read a bunch of posts about custom layouts but none of them seem to work. Any help is appreciated. Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
layout.itemSize = CGSize(width: (screenWidth - 32)/2, height: 175)
var collview = UICollectionView(frame: CGRectMake(0, 0, screenWidth, screenHeight), collectionViewLayout: layout)
[collview.reloadData];
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! MyCollectionViewCell
let myImage = UIImage(named: pictureArray[indexPath.row])
cell.textView.text = textArray[indexPath.row]
cell.imageView.image = myImage
return cell
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pictureArray.count
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
return CGSize(width: 175, height: 175)
}
Because you set the size for each item is CGSize(width: 175, height: 175), so when you change other phone, it will not work well.
Just change like this: (change UICollectionViewDelegate to UICollectionViewDelegateFlowLayout)
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
return CGSize(width: screenWidth / 3, height: screenWidth / 3)
}