Uicollection view dynamic height but fixed width - ios

I am working with UICollectionView I have 4 different kind of cells. Every cell is designed in xib file . when i load them in collection view it goes out of screen. i don't know why it is happening. Some cells have fixed height while some have dynamic height (depends on data coming from API). So is there any possible way to solve this issue.
i have tried Estimate size automatic to dynamic
override func awakeFromNib() {
super.awakeFromNib()
self.contentView.translatesAutoresizingMaskIntoConstraints = false
let screenwidth = UIScreen.main.bounds.size.width
widthAnchor.constraint(equalToConstant: screenwidth-20)
}
func setSize () {
let layout = mainCollection.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)}
}

try this!
let screenWidth = self.CollecionView.frame.width

Related

UICollectionView Scroll Jumpy Initially

I am using a UICollectionView for chat and using scroll to bottom on load. It's all working good except the first time I scroll manually it bounces up about 50-100 pt. If I continue scrolling it's fine and goes into the safe area and below the screen.
What is causing the initial bounce? It seems that it bounces off the top of the bottom safe area first, and then goes below the safe area fine if you continue scrolling.
What I'm trying to get is a nice smooth scroll initially without the weird bounce.
It seems to be caused by the fact that I am dynamically sizing image heights after they load with invalidateLayout.
I was able to solve this by luck with the following
AutoSizing cells: cell width equal to the CollectionView
// UICollectionViewController
if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
layout.estimatedItemSize = CGSize(width: view.frame.width, height: 100)
}
// UICollectionViewCell
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
// Specify you want _full width_
let targetSize = CGSize(width: layoutAttributes.frame.width, height: 200)
// Calculate the size (height) using Auto Layout
let autoLayoutSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.defaultLow)
let autoLayoutFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: autoLayoutSize)
// Assign the new size to the layout attributes
autoLayoutAttributes.frame = autoLayoutFrame
return autoLayoutAttributes
}

Is it possible to change the height of collection view with its dynamic cell height? which delegate method should we use if possible?

I have to change the height of collection view with its dynamic cell height. cell height depends on its content and that cell height should update the height of collection view.
If you're using UICollectionViewFlowLayout then you can use self sizing cells for UICollectionView and UITableView feature available from iOS 8 and above. This feature helps in dynamically setting the height of the cell based on the content and constraints set in the cell.
There are 2 step setup you need to do for self sizing cells to work.
1. Set estimatedItemSize on UICollectionViewFlowLayout:
//for storyboard:
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width, height: 100)
}
//using code:
let flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView.init(frame: .zero, collectionViewLayout: flowLayout)
flowLayout.estimatedItemSize = CGSize.init(width: UIScreen.main.bounds.width, height: 100)
2. Use Autolayout to configure custom UICollectionView cell or implement
preferredLayoutAttributesFittingAttributes in your custom cell
var isHeightCalculated: Bool = false
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
//Exhibit A - We need to cache our calculation to prevent a crash.
if !isHeightCalculated {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var newFrame = layoutAttributes.frame
newFrame.size.width = CGFloat(ceilf(Float(size.width)))
layoutAttributes.frame = newFrame
isHeightCalculated = true
}
return layoutAttributes
}
read stackoverflow and blog for more info on how to use autolayout for self sizing cells.

Equal horizontal spacing UICollectionView, including insets - UICollectionViewFlowLayout?

I'm trying to create equal horizontal spacing between two UICollectionViewCell and between a UICollectionViewCell an the screen border. It should look like this.
I think it is possible to use UICollectionViewFlowLayout to adjust the width of every UICollectionViewCell, so they have equal spacings regardless of the phone's size. There should always only be 3 cells on each row.
First of all you should add constants for all parameters of UICollectionView to the controller:
let itemsPerRow = 3
let spaceBetweenItems: CGFloat = 20.0
let spaceBetweenLines: CGFloat = 40.0
let itemHeight: CGFloat = 50.0
You mentioned that item's width should be adjustable. You can calculate it with this function:
var itemWidth: CGFloat {
return (self.collectionView.frame.width - CGFloat(self.itemsPerRow + 1) * self.spaceBetweenItems) / CGFloat(self.itemsPerRow)
}
Of course you need to customise layout of your UICollectionView instance:
var collectionViewLayout: UICollectionViewFlowLayout {
let result = UICollectionViewFlowLayout()
result.itemSize = CGSize(width: self.itemWidth, height: self.itemHeight)
result.minimumInteritemSpacing = self.spaceBetweenItems
result.minimumLineSpacing = self.spaceBetweenLines
result.sectionInset = UIEdgeInsets(top: 0.0, left: self.spaceBetweenItems, bottom: 0.0, right: self.spaceBetweenItems)
result.scrollDirection = .vertical
return result
}
And in the function viewDidLoad or your controller you should call setupCollectionView(). Here is a code of this function:
func setupCollectionView() {
self.collectionView.frame = self.view.frame
self.collectionView.collectionViewLayout = self.collectionViewLayout
}
You will see something like this:

UICollectionView horizontal paging with space between pages

I'm looking for a way to replace the native UIPageViewController horizontal paging with a UICollectionView.
so far i did the following:
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = collectionView.frame.size
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 10
collectionView.setCollectionViewLayout(layout, animated: false)
collectionView.isPagingEnabled = true
collectionView.alwaysBounceVertical = false
this works fine and i get an horizontal paging effect.
Now i want to add horizontal space between the pages (like u will do with UIPageViewControllerOptionInterPageSpacingKey on UIPageViewController)
so far i couldn't fine a way to add the spaces without damaging the paging effect.
im looking for the same behavior as with the UIPageViewController: the cell should fill the entire screen width, and the space between the cells should only be visible when switching a page.
Solution one:
collectionView.isPagingEnabled = false
add a minimumLineSpacing for the distance between pages
implement targetContentOffsetForProposedContentOffset:withScrollingVelocity: to move the contentOffset to the closest page. You can calculate the page with simple math based on your itemSize and minimumLineSpacing, but it can take a little work to get it right.
Solution Two:
collectionView.isPagingEnabled = true
add a minimumLineSpacing for the distance between pages
the paging size is based on the bounds of the collectionView. So make the collectionView larger then then screenSize. For example, if you have a minimumLineSpacing of 10 then set the frame of the collectionView to be {0,-5, width+10, height}
set a contentInset equal to the minimumLineSpacing to make the first and last item appear correctly.
By default, UICollectionViewFlowLayout's minimumLineSpacing is 10.0, when collectionView's item is horizontally filled (itemSize.width = collectionView.bounds.width) and collectionView is paging enabled, greater than zero 'minimumLineSpacing' will cause an unintended performance: start from second page, every page has a gap which will be accumulated by page number.
It seems that we can expand collectionView's width by 'minimumLineSpacing' to fix this problem. But pratice negates this solution: When there are tow pages or more, collectionView will not give the last one a 'lineSpacing', so it's 'contentSize' is not enough to show this page's content completely, which means if the 'minimumLineSpacing' were 10.0, the last page's end would overstep the collectionView's contentSize by 10.0.
Finally, I use the following simple solution to insert a margin between every item (like UIScrollView's preformance): Expand every collectionViewItem's width by a fixed value 'pageSpacing' (such as 10.0), and expand the collectionView's width by the same value, too. And don't forget that, when layout collevtionViewCell's subviews, there's an additional spacing which is not for diplaying the real content.
Heres the code written in Swift 3.1, I'm sure it's easily understanding for you:
let pageSpacing: CGFloat = 10
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: view.frame.width + pageSpacing, height: view.frame.height)
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
let frame = CGRect(x: 0, y: 0, width: view.frame.width + pageSpacing, height: view.frame.height)
let collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
view.addSubview(collectionView)
}
}
class MyCell: UICollectionViewCell {
var fullScreenImageView = UIImageView()
override func layoutSubviews() {
super.layoutSubviews()
fullScreenImageView.frame = CGRect(x: 0, y: 0, width: frame.width - pageSpacing, height: frame.height)
}
}
Hope that can help you.

UICollectionView layout change when frame size changes

I'm working with a UICollectionView but I have a weird behaviour.
I want a horizontal scroll, small cell height ( smaller than the UICollectionView height).
Tapping a button will increase even more the height of the collection.
The problem is that the layout of my cells is changing also.
I want the collection view height to increase with the cells staying in the same position.
Here attached 2 screen captures (before/after button touched).
Here is my code:
var smallLayout:UICollectionViewFlowLayout = UICollectionViewFlowLayout();
smallLayout.scrollDirection = .Horizontal
var itemHeight = 100
smallLayout.itemSize = CGSizeMake(150, itemHeight);
var bottomDist:CGFloat = 1
var topDist = collHeight - itemHeight - bottomDist
smallLayout.sectionInset = UIEdgeInsets(top: topDist, left: 2, bottom: bottomDist, right: 2)
smallLayout.minimumInteritemSpacing = 15
smallLayout.minimumLineSpacing = 10
So, when touching the button I have the following code:
let viewHeight = CGRectGetHeight(self.view.frame)
let viewWidth = CGRectGetWidth(self.view.frame)
var collHeight:CGFloat = 600
var collYPosition = viewHeight-CGFloat(collHeight)-20
var rect = CGRectMake(0, CGFloat(collYPosition), CGFloat(viewWidth), CGFloat(collHeight))
dishCollectionView.frame = rect
So, I cannot understand why my layout is changing.
I want to have my cell at the bottom.
Perhaps if you reset the top of your sectionInset to accommodate for the additional height, i.e. an updated topDist calculation. Try adding something like this to you on touch code:
var bottomDist:CGFloat = 1
var topDist = collHeight - itemHeight - bottomDist
dishCollectionView.collectionViewLayout.sectionInset = UIEdgeInsets(top: topDist, left: 2, bottom: bottomDist, right: 2)
dishCollectionView.collectionViewLayout.invalidateLayout()

Resources