When debugging custom UICollectionView via View Debug it's width is 829, but when it's width is accessed via code it's width is 820. What am I missing?
Visual debug:
Code:
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize
{
let widthForCulculation = self.frame.size.width // 820.0
...
return CGSize(width: segmentWidth, height: segmentHeight)
}
Pretty hacky way, but try to invalidate layout when layouting subviews
var width : CGFloat = 0
override func layoutSubviews() {
super.layoutSubviews()
if (self.width != self.frame.size.width)
{
self.width = self.frame.size.width
self.collectionViewLayout.invalidateLayout()
}
}
Related
What is the way to set the equal size height for all cells in collection view?
I have this method:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return getCellSize()
}
func getCellSize() -> CGSize {
let screenWidth: CGFloat = UIScreen.main.bounds.width
let spaceBetweenItems: CGFloat = CGFloat(2) * 10
let width: CGFloat = (screenWidth - 10 - spaceBetweenItems) / CGFloat(3.0)
return CGSize(width: width, height: CGFloat(140.0))
}
And these are my customs cell constraints:
This is my actual output:
What is wrong in my code or is missing one constraint ?
create Constraint CollectionViewHeight.
#IBOutlet weak var collectionViewHeight: NSLayoutConstraint!
and then, try this Code.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let height = collectionView.collectionViewLayout.collectionViewContentSize.height
collectionViewHeight.constant = height
self.view.layoutIfNeeded()
}
In storyboard with collection view: select your collection view -> go to size inspector -> change field "Estimate size" from "automatic" to "none"
like here.
But then if you text will be too large it will be clipped with dots like "Some large text..", so you can change field "Autoshrink" of your UILabel in attribute inspector to "Minimum Font Scale".
I've a fixed grid of collection view cells (UICollectionView) but the cells in the bottom row always appears with a slightly smaller width on screen. The frame size (or bounds) and calculated width used within collectionViewLayout: UICollectionViewLayout sizeForItemAt are the same for all rows.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
let settings = currentContents[indexPath.item]
let height = CGFloat(30)
// https://stackoverflow.com/questions/54915227/uicollectionview-remove-space-between-cells-with-7-items-per-row
var cellWidth = CGFloat()
let availableWidth = collectionView.bounds.size.width
print("available width", availableWidth)
let minimumWidth = floor(availableWidth / collectionContents.cellsPerRow5)
print("minmum width", minimumWidth)
cellWidth = minimumWidth * settings.portion - 1
print("cell width", cellWidth)
return CGSize(width: cellWidth, height: height)
}
I'd like to get the bottom row to line up with the other rows, but can't imagine what is happening that is changing the widths after returning the value in the layout delegate method (or how to fix).
UIKit does not like values with more than 2 decimals, round them or it will do it for you.
Here, UICollectionViewFlowLayout rounds your cell size and starts to fill the lines with an "interitemspacing" at least equal to the minimumInteritemSpacing you specified. On the last line, it used exactly the minimumInteritemSpacing value and doesn't fill entirely the line.
Fix it using better rounded values, giving the illusion that all is perfectly aligned.
I usually use those extensions:
extension CGFloat {
func xx_rounded(_ rule: FloatingPointRoundingRule = .down, toDecimals decimals: Int = 2) -> CGFloat {
let multiplier = CGFloat(pow(10.0, CGFloat(decimals)))
return (self * multiplier).rounded(.down) / multiplier
}
}
extension CGSize {
func xx_rounded(rule: FloatingPointRoundingRule = .down, toDecimals: Int = 2) -> CGSize {
return CGSize(
width: width.xx_rounded(rule, toDecimals: toDecimals),
height: height.xx_rounded(rule, toDecimals: toDecimals)
)
}
}
Change:
return CGSize(width: cellWidth, height: height)
to
return CGSize(width: cellWidth, height: height).xx_rounded()
Just replace
cellWidth = (minimumWidth * settings.portion) - 0.5
WITH
cellWidth = (minimumWidth * settings.portion)
Specified minimumInteritemSpacingForSectionAt to 0.5 instead of 0
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.5
}
CVTVCell.swift
Just reload your UICollectionView in awakeFromNib method after your data set into currentContents. Please refer below sample code.
override func awakeFromNib(){
super.awakeFromNib()
currentContents = collectionContents.allEndings[0]
self.cView.reloadData()
}
Download code from here
The best way I found was to capture the remaining space (minus 1 to avoid rounding up) after calculating the initial column width, and then use this remaining space for additional columns.
For a two column example:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
if indexPath.item == 0 || indexPath.item == 1
{ rowHeight = 40 }
else { rowHeight = 30 }
var cellWidth = CGFloat()
let minimumWidth = floor(availableWidth / numberOfColumns)
var columnPortion = CGFloat()
let columnNumber = indexPath.item % Int(numberOfColumns)
switch columnNumber
{
case 0:
columnPortion = columnPortion1
cellWidth = minimumWidth * columnPortion - 10
remainder = availableWidth - cellWidth - 1
case 1:
columnPortion = columnPortionFinal
cellWidth = remainder
default:
break
}
return CGSize(width: cellWidth, height: rowHeight)
}
I read
How to vertically center UICollectionView content
But when I used the codes here
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let navBarHeight = self.navigationController?.navigationBar.frame.height
let collectionViewHeight = (self.collectionView?.frame.height)! - navBarHeight!
let itemsHeight = self.collectionView?.contentSize.height
let topInset = ( collectionViewHeight - itemsHeight! ) / 4
return UIEdgeInsetsMake(topInset ,0, 0 , 0)
}
But when scrolling collection view this will show incorrect form
so here is my codes because my collection view cells are square and equal to screen width
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
showImagesCV.contentInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
if let flowLayout = showImagesCV.collectionViewLayout as? UICollectionViewFlowLayout{
cellWidth = UIScreen.main.bounds.width
cellHeight = cellWidth //yourCellHeight = cellWidth if u want square cell
flowLayout.minimumLineSpacing = spacing
flowLayout.minimumInteritemSpacing = spacing
UIView.performWithoutAnimation {
}
}
}
}
extension showImagesViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: cellWidth, height: cellHeight)
}
}
I want the spacing between cells be 0 and each cell that is showing (current page (because the collection view is in paging mode)) be the center of the screen
Regarding the SO Question you mentioned and using, that is to centre the whole section not individual cells. Some of your code might be invalid(needs to be redone).
First and most important, set the constraints of your collectionView for full screen. Remember to take safeAre/TopGuide into account, this will make sure that collection view is below the navigation bar if there is one.
This will make sure that your collectionView is up to date
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myCollectionView.collectionViewLayout.invalidateLayout()
}
Dump/comment below code and set insets to (0,0,0,0) in inspector of collectionView in story board. In same inspector change Min Spacing to 0 for cells and for line, both.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let navBarHeight = self.navigationController?.navigationBar.frame.height
let collectionViewHeight = (self.collectionView?.frame.height)! - navBarHeight!
let itemsHeight = self.collectionView?.contentSize.height
let topInset = ( collectionViewHeight - itemsHeight! ) / 4
return UIEdgeInsetsMake(topInset ,0, 0 , 0)
}
Also remove/comment below code
showImagesCV.contentInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
if let flowLayout = showImagesCV.collectionViewLayout as? UICollectionViewFlowLayout{
cellWidth = UIScreen.main.bounds.width
cellHeight = cellWidth //yourCellHeight = cellWidth if u want square cell
flowLayout.minimumLineSpacing = spacing
flowLayout.minimumInteritemSpacing = spacing
UIView.performWithoutAnimation {
}
}
Now change your sizeForItemAt like this.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return collectionView.frame.size
}
UICollectionViewCell
Now in your collectionView cell, make sure that your image view is 1:1 and in centre. Also handle constraints of your other UI components.
PS : This is easy approach, other would be to write custom flowLayout
I have a collection view that the height of the cells won't change when I will run the application in bigger screen iPhone I used this code But when I will run this code I can't see my collection view in my app
remember that I have horizontal collection view that contains one line
var cellWidth:CGFloat = 0
var cellHeight:CGFloat = 0
var spacing:CGFloat = 12
var numberOfColumn:CGFloat = 9
//Scale Collection View
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
specialCollectionView.contentInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
if let flowLayout = specialCollectionView.collectionViewLayout as? UICollectionViewFlowLayout{
cellHeight = view.frame.size.height / specialCollectionView.frame.height
flowLayout.minimumLineSpacing = spacing
flowLayout.minimumInteritemSpacing = spacing
}
}
and here is the extension code
extension secondTabViewController:UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: cellWidth, height: cellHeight)
}
}
when i implement below code :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
let cellW = (ScreenWidth - 10 - 10 - 30) / 4
return CGSize(width: cellW, height: cellW * 1.5 )
}
by using sizeForItemAt method , i can draw frame what i want , but my custom collectionview cell can not update as wise , where did i miss , plz help me !!
my custom cell
debug view
i fix it by this ref
UICollectionView cell subviews do not resize
thanks guys , at frist still have problem with my Cell.imageview , can not shape-round as i want , and i create a class for imageview ,below:
class CellImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
let radius: CGFloat = self.bounds.size.width / 2.0
self.layer.cornerRadius = radius
self.clipsToBounds = true
}
and anything work perfectly
For me in iOS 13, I have fixed by first estimatedItemSize to .zero and set the itemSize like;
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.estimatedItemSize = .zero
layout.itemSize = CGSize(width: collectionView.frame.size.width, height: 80)
}