I have a custom collection view layout which loads the cells from the bottom up for a messaging app, except I'm having trouble implementing dynamic cell heights. I believe the protocol and delegates I hooked up to retrieve the dynamic cell height are working for each cell (which I deduced from printing the height for each indexPath.item) except when the collectionView is loaded the cell heights are not the heights they are supposed to be. They are all just one static height (not the 80 preset by var cellHeight) and show just one line of text.
protocol InvertedStackProtocol: class {
func collectionView(_ collectionView: UICollectionView, heightForCellAtIndexPath indexPath: IndexPath) -> CGFloat
}
class InvertedStackLayout: UICollectionViewLayout, UICollectionViewDelegateFlowLayout {
var cellHeight: CGFloat = 80
weak var delegate: InvertedStackProtocol!
override func prepare() {
guard let collectionView = self.collectionView else {return}
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
let height = delegate.collectionView(collectionView, heightForCellAtIndexPath: indexPath)
print (height, indexPath.item)
cellHeight = height
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttrs = [UICollectionViewLayoutAttributes]()
if let collectionView = self.collectionView {
for section in 0 ..< collectionView.numberOfSections {
if let numberOfSectionItems = numberOfItemsInSection(section) {
for item in 0 ..< numberOfSectionItems {
let indexPath = IndexPath(item: item, section: section)
let layoutAttr = layoutAttributesForItem(at: indexPath)
if let layoutAttr = layoutAttr, layoutAttr.frame.intersects(rect) {
layoutAttrs.append(layoutAttr)
}
}
}
}
}
return layoutAttrs
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAttr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let contentSize = self.collectionViewContentSize
layoutAttr.frame = CGRect(
x: 0, y: contentSize.height - CGFloat(indexPath.item + 1) * cellHeight,
width: contentSize.width, height: cellHeight)
return layoutAttr
}
func numberOfItemsInSection(_ section: Int) -> Int? {
if let collectionView = self.collectionView,
let numSectionItems = collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: section)
{
return numSectionItems
}
return 0
}
override var collectionViewContentSize: CGSize {
get {
var height: CGFloat = 0.0
var bounds = CGRect.zero
if let collectionView = self.collectionView {
for section in 0 ..< collectionView.numberOfSections {
if let numItems = numberOfItemsInSection(section) {
height += CGFloat(numItems) * cellHeight
}
}
bounds = collectionView.bounds
}
return CGSize(width: bounds.width, height: max(height, bounds.height))
// return CGSize(width: bounds.width, height: height) do this for more exact fit of collection view
}
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
if let oldBounds = self.collectionView?.bounds,
oldBounds.width != newBounds.width || oldBounds.height != newBounds.height
{
return true
}
return false
}
}
Use this method to change the height, UICollectionViewCell.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 125.0, height: 175.0)
}
Related
I am trying to implement the Tags Layout using UICollectionView.
It works fine but only at some places the word is not displayed properly (Cropped) (mostly at the end of the UICollectioView frame). I have attached the screenshot as well.
I followed this Setup a collectionView with "tag"-like cells and tried to implement the same but it didn't work for me.
class UserProfileTagsFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributesForElementsInRect = super.layoutAttributesForElements(in: rect) else { return nil }
guard var newAttributesForElementsInRect = NSArray(array: attributesForElementsInRect, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }
var leftMargin: CGFloat = 0.0;
for attributes in attributesForElementsInRect {
if (attributes.frame.origin.x == self.sectionInset.left) {
leftMargin = self.sectionInset.left
}
else {
var newLeftAlignedFrame = attributes.frame
newLeftAlignedFrame.origin.x = leftMargin
attributes.frame = newLeftAlignedFrame
}
leftMargin += attributes.frame.size.width + 8 // Makes the space between cells
newAttributesForElementsInRect.append(attributes)
}
return newAttributesForElementsInRect
}
}
I am using the below array to populate the data :
data = ["Kishor", "iOS", "Developer", "Software Engineer", "ABCD", "Coding", "Xcode", "I am an iOS Developer","JKLMNO" , "Testing Coding", "Development", "XYZ-XYZ-XYZCD", "Enable the Tag Layout", "Layouts Arrangement"]
But if you see in the screenshot, last word of the first line "ABCD", 'D' is cropped and also few of the words are cropped at the end of the frame.
Below is my UICollectionView (Inside the UITableViewCell)
class TagListCell: UITableViewCell {
#IBOutlet weak var tagListCollection: UICollectionView!
var data: [String] = []
override func awakeFromNib() {
super.awakeFromNib()
}
fileprivate func setCollectioView() {
self.setupCollectionnViewLayout()
self.tagListCollection.register(UINib(nibName: "TagListNameCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "TagListNameCollectionViewCell")
tagListCollection.delegate = self
tagListCollection.dataSource = self
}
private func setupCollectionnViewLayout() {
let layout = UserProfileTagsFlowLayout()
//layout.estimatedItemSize = CGSize.init(width: 50, height: 50)
layout.scrollDirection = .vertical
tagListCollection.collectionViewLayout = layout
}
// MARK: UIView functions
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
self.tagListCollection.layoutIfNeeded()
let size = tagListCollection.collectionViewLayout.collectionViewContentSize
let newSize = CGSize(width: size.width, height: size.height + 1)
debugPrint("New Size : \(newSize)")
return newSize
}
func setData(data: [String]) {
self.setCollectioView()
self.data = data
}
}
//MARK:- UICollectionView Delegate and DataSource
extension TagListCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TagListNameCollectionViewCell", for: indexPath) as? TagListNameCollectionViewCell {
cell.titleLable.text = data[indexPath.row]
return cell
}
else {
return UICollectionViewCell()
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let tag = data[indexPath.row]
let font = UIFont.systemFont(ofSize: 17)//UIFont(name: "Label Test Data", size: 16)!
let size = tag.size(withAttributes: [NSAttributedString.Key.font: font])
let dynamicCellWidth = size.width
return CGSize(width: dynamicCellWidth, height: 50)
}
// Space between rows
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
// Space between cells
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
}
I can't figure out how I can adjust the cell sizes without "breaking" rows count.
Before I use customized UICollectionViewFlowLayout, the scroll function works Fine, but when I set my layout to UICollectionView, it can't scroll anymore, but all contents display well because my layout works.
I want to make PinterestLayout, so I created my layout displayed as the following photo.
I am try to use this lay out into my Instagram-style app with UICollection header and cell
My Code for my layout is the following:
class MyLayout : UICollectionViewFlowLayout{
fileprivate let numberOfColumns = 2
weak var delegate : MyLayoutDelegate!
fileprivate var cache = [UICollectionViewLayoutAttributes]()
fileprivate var contentHeight : CGFloat = 0
fileprivate var contentWidth : CGFloat{
guard let collectionView = collectionView else {return 0.0}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
override var collectionViewContentSize: CGSize{
return CGSize(width: contentWidth, height: contentHeight)
}
override init() {
super.init()
self.minimumLineSpacing = 1
self.minimumInteritemSpacing = 1
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepare() {
guard let collection = collectionView else {return}
let columnWidth = contentWidth / CGFloat(numberOfColumns)
let xOffset: [CGFloat] = [0,columnWidth]
var yOffset = [CGFloat](repeating: 190, count: numberOfColumns)
var columnToPlacePhoto = 0
for item in 0 ..< collection.numberOfItems(inSection: 0){
let indexPath = IndexPath(item: item, section: 0)
let photoHeight : CGFloat = delegate.collectionView(collection, numberOfColumns: numberOfColumns, heightForPhotoAtIndexPath: indexPath)
let frame = CGRect(x: xOffset[columnToPlacePhoto], y: yOffset[columnToPlacePhoto], width: columnWidth, height: photoHeight)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = frame
self.cache.append(attributes)
yOffset[columnToPlacePhoto] = yOffset[columnToPlacePhoto] + photoHeight
columnToPlacePhoto = columnToPlacePhoto < (numberOfColumns - 1) ? (columnToPlacePhoto + 1) : 0
}
}
//
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes = super.layoutAttributesForElements(in: rect)
layoutAttributes?.forEach({ (attribute) in
// I did this to keep attribute of Collection View Header, without this step, my Header doesn't show up
if attribute.representedElementKind == UICollectionView.elementKindSectionHeader{
cache.append(attribute)
}})
return cache
}
}
I assign my layout to Collection View
class ProfileVC: UICollectionViewController, UICollectionViewDelegateFlowLayout, {
override func viewDidLoad() {
super.viewDidLoad()
let layout = MyLayout()
layout.delegate = self
self.collectionView.collectionViewLayout = layout
self.collectionView.isScrollEnabled = true
}
I also set the number of items, number of sections, size of header and so on.
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
// Set size of header
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: view.frame.width, height: 190)
}
// Set cell
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ProfilePostCell
cell.post = posts[indexPath.item]
return cell
}
I am pretty sure my original Collection View works well
enter image description here
But after I add the following, it can not scroll. Please help me out of here, thanks in advance.
let layout = MyLayout()
layout.delegate = self
self.collectionView.collectionViewLayout = layout
self.collectionView.isScrollEnabled = true
I have solved this problem by adding.
contentHeight = max(contentHeight, frame.maxY)
I have a UICollectionView that shows 2 cells on top and 3 at the bottom like given in
This link
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 5
layout.minimumInteritemSpacing = 5
collectionview2.delegate = self
collectionview2.dataSource = self
collectionview2.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "ident")
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch indexPath.item {
case 0,1:
return CGSize(width: (UIScreen.main.bounds.width - 16) / 2, height: (UIScreen.main.bounds.width - 16) / 2)
default:
return CGSize(width: (UIScreen.main.bounds.width - 32) / 3, height: (UIScreen.main.bounds.width) / 3)
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ident", for: indexPath)
cell.backgroundColor = .red
return cell
}
But I want to show one long cell on left and 2 small cells on right like so..
So what changes do I need to make in my answer..?
EDIT 1 This is the latest output...
code in sizeForItemAt..
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch indexPath.item {
case 0,3:
return CGSize(width: (UIScreen.main.bounds.width - 16) / 2, height: (UIScreen.main.bounds.width - 16) / 2)
case 1,2,4:
return CGSize(width: (UIScreen.main.bounds.width - 32) / 3, height: (UIScreen.main.bounds.width) / 3)
default:
return CGSize(width: (UIScreen.main.bounds.width - 32) / 3, height: (UIScreen.main.bounds.width) / 3)
}
}
Output:
Check this may be helps you :)
DynamicHeightForCollectionView
You need to subclass UICollectionViewLayout as below
class PinterestLayout: UICollectionViewLayout {
//1. Pinterest Layout Delegate
weak var delegate: PinterestLayoutDelegate!
//2. Configurable properties
fileprivate var numberOfColumns = 2
fileprivate var cellPadding: CGFloat = 6
//3. Array to keep a cache of attributes.
fileprivate var cache = [UICollectionViewLayoutAttributes]()
//4. Content height and size
fileprivate var contentHeight: CGFloat = 0
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
// 1. Only calculate once
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
// 2. Pre-Calculates the X Offset for every column and adds an array to increment the currently max Y Offset for each column
let columnWidth = contentWidth / CGFloat(numberOfColumns)
var xOffset = [CGFloat]()
for column in 0 ..< numberOfColumns {
xOffset.append(CGFloat(column) * columnWidth)
}
var column = 0
var yOffset = [CGFloat](repeating: 0, count: numberOfColumns)
// 3. Iterates through the list of items in the first section
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
// 4. Asks the delegate for the height of the picture and the annotation and calculates the cell frame.
let photoHeight = delegate.collectionView(collectionView, heightForPhotoAtIndexPath: indexPath)
let height = cellPadding * 2 + photoHeight
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
// 5. Creates an UICollectionViewLayoutItem with the frame and add it to the cache
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
// 6. Updates the collection view content height
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + height
column = column < (numberOfColumns - 1) ? (column + 1) : 0
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}
See this Tutorial
I want to create a UICollectionview like the picture. Here I am having 18 Squares. The width of the Collection view is addition of 6 times square width and three border lines width.Similarly the height is also addition of 3 times the height of square and two border lines.
In collectionView how can i add the borderlines in the front as well as in between the cells as in the picture.
Output
You can get above design layout from UICollectionViewLayout.
Three Files:
1. SquareViewController
2. SquareCollectionViewCell
3. SquareLayout
SquareViewController----
class SquareViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
// CONSTRAINTS: top 20, left 0, right 0, height 100.
#IBOutlet weak var squareCollectionVw: UICollectionView!
// SET squareCollectionVw HEIGHT CONSTRAINT OUTLET
#IBOutlet weak var collectionVwHeightConst: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
collectionVwHeightConst.constant = (UIScreen.main.bounds.width / 6) * 3 + 1
squareCollectionVw.backgroundColor = UIColor.darkGray
// Do any additional setup after loading the view.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 18
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "square", for: indexPath) as! SquareCollectionViewCell
cell.backgroundColor = UIColor.white
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 1.0
return cell
}
}
SquareCollectionViewCell
class SquareCollectionViewCell: UICollectionViewCell {
}
SquareLayout [UICollectionViewLayout]
class Square: UICollectionViewLayout {
var CELL_HEIGHT = 0.0
var CELL_WIDTH = 0.0
var portrait_Ypos : Double = 0.0
var portrait_Xpos : Double = 0.0
var contentSize = CGSize.zero
var cellAttrsDictionary = Dictionary<IndexPath, UICollectionViewLayoutAttributes>()
override var collectionViewContentSize : CGSize {
return self.contentSize
}
override func prepare() {
CELL_WIDTH = (Double(UIScreen.main.bounds.width) / 6) - 0.5
CELL_HEIGHT = CELL_WIDTH
if let sectionCount = collectionView?.numberOfSections, sectionCount > 0 {
for section in 0...sectionCount-1 {
if let rowCount = collectionView?.numberOfItems(inSection: section), rowCount > 0 {
for item in 0...rowCount-1 {
let cellIndex = IndexPath(item: item, section: section)
if item <= 5
{
portrait_Ypos = 1
if item == 0
{
portrait_Xpos = 1
}
else
{
if item == 3
{
portrait_Xpos = portrait_Xpos + CELL_WIDTH + 1
}
else
{
portrait_Xpos = portrait_Xpos + CELL_WIDTH
}
}
}
else
{
if item == 6
{
portrait_Ypos = 1 + CELL_HEIGHT
portrait_Xpos = 1
}
else
{
if item % 6 == 0
{
portrait_Ypos = portrait_Ypos + CELL_HEIGHT
portrait_Xpos = 1
}
else
{
if item % 6 == 3
{
portrait_Xpos = portrait_Xpos + CELL_WIDTH + 1
}
else
{
portrait_Xpos = portrait_Xpos + CELL_WIDTH
}
}
}
}
let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex)
cellAttributes.frame = CGRect(x: portrait_Xpos, y: portrait_Ypos, width: CELL_WIDTH, height: CELL_HEIGHT)
if section == 0 && item == 0 {
cellAttributes.zIndex = 4
} else if section == 0 {
cellAttributes.zIndex = 3
} else if item == 0 {
cellAttributes.zIndex = 2
} else {
cellAttributes.zIndex = 1
}
cellAttrsDictionary[cellIndex] = cellAttributes
}
}
}
}
let contentWidth = UIScreen.main.bounds.width
let contentHeight = CGFloat(portrait_Ypos) + CGFloat(CELL_HEIGHT)
self.contentSize = CGSize(width: contentWidth, height: contentHeight)
print("sPort.contentSize ", self.contentSize)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var attributesInRect = [UICollectionViewLayoutAttributes]()
for cellAttributes in cellAttrsDictionary.values {
if rect.intersects(cellAttributes.frame) {
attributesInRect.append(cellAttributes)
}
}
return attributesInRect
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cellAttrsDictionary[indexPath]!
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
Embedd UICollectionViewLayout in UICollectionView:
Select squareCollectionVw, and Click Attributes Inspector,
Change Layout as Custom from Flow
Class as SquareLayout [ For me, Square ]
You can simply put two UICollectionViewCell, with nine squares inside, in your UICollectionView and in the method cellForItem
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! CustomCell
cell.layer.borderWidth = 2
cell.layer.borderColor = .black
return cell
}
From my opinion, you have to create a 2 identical cells and configure it in UICollectionViewFlowLayoutDelegate that each cell width is screen.width / 2 and put border for those 2 cells. In part of border you might have to play with layer (CALayer) to make it equal for 4 sides
For add space between cell
Suppose there are 2 cell in one row of UICollectionView
For example:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (self.view.frame.size.width - 15) / 2.0 //total view width and 15 space between each cell
let height = CGFloat(70.0) //Height is 70. (you can take same as width also)
return CGSize(width: width, height: height)
}
This will help you to add space between cell.
For add border between cell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionCell
cell.layer.borderWidth = 1.0
cell.layer.borderColor = UIColor.black
}
I have a horizontal collectionView and i would like to manage that when scrolling it, the centre cell becomes larger. So, each time a cell is the center cell it will be larger than the others. Check the image beneath.
Can anyone help me with the best way to do this? Thanks in advance!
My code so far:
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet var horizontalCollectionView: UICollectionView!
#IBOutlet var verticalCollectionView: UICollectionView!
var horizontal = ["1","2","3","4","5","6","7","8","9","10","1","2","3","4","5","6","7","8","9","10"]
var horizontalCellSize = CGSize(width: 50, height: 50)
var horizontalLargeCellSize = CGSize(width: 80, height: 80)
var centerCell = UICollectionViewCell()
var cellIsInCenter = false
var myIndexPath = IndexPath()
override func viewDidLoad() {
super.viewDidLoad()
}
// When the main view's dimensions change this will re-layout the collection view
override func viewWillLayoutSubviews() {
verticalCollectionView.collectionViewLayout.invalidateLayout()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return horizontal.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == horizontalCollectionView {
let horizontalCell = collectionView.dequeueReusableCell(withReuseIdentifier: "HorizontalCell", for: indexPath) as! HorizontalCell
horizontalCell.backgroundColor = UIColor.blue
horizontalCell.textLabel.text = horizontal[indexPath.row]
return horizontalCell
}
else {
let verticalCell = collectionView.dequeueReusableCell(withReuseIdentifier: "VerticalCell", for: indexPath) as! VerticalCell
verticalCell.backgroundColor = UIColor.red
verticalCell.textLabel.text = horizontal[indexPath.row]
return verticalCell
}
}
// MARK: UIScrollViewDelegate
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == self.horizontalCollectionView {
let horizontalScrollPosition = scrollView.contentOffset.x * 35
self.verticalCollectionView.delegate = nil
self.verticalCollectionView.contentOffset = CGPoint(x: 0, y: horizontalScrollPosition)
self.verticalCollectionView.delegate = self
}
if scrollView == self.verticalCollectionView {
let verticalScrollPosition = scrollView.contentOffset.y
self.horizontalCollectionView.delegate = nil
self.horizontalCollectionView.contentOffset = CGPoint(x: verticalScrollPosition, y: 0)
self.horizontalCollectionView.delegate = self
}
}
// MARK: CollectionViewLayout
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if collectionView == verticalCollectionView {
let size = CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height)
return size
}
if indexPath.item == 3 {
return CGSize(width: 70, height: 70)
}
return CGSize(width: 50, height: 50)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.verticalCollectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: true)
print("\(indexPath)")
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var closestCell: UICollectionViewCell = self.horizontalCollectionView.visibleCells[0]
for cell in self.horizontalCollectionView.visibleCells as [UICollectionViewCell] {
let closestCellDelta = abs(closestCell.center.x - self.horizontalCollectionView.bounds.size.width / 2.0 - self.horizontalCollectionView.contentOffset.x)
let cellDelta = abs(cell.center.x - self.horizontalCollectionView.bounds.size.width/2.0 - self.horizontalCollectionView.contentOffset.x)
if (cellDelta < closestCellDelta){
closestCell = cell
self.centerCell = cell
self.cellIsInCenter = true
horizontalCellSize = CGSize(width: 80, height: 50)
}
}
let indexPath = self.horizontalCollectionView.indexPath(for: closestCell)
self.horizontalCollectionView.scrollToItem(at: indexPath!, at: .centeredHorizontally , animated: true)
if closestCell.backgroundColor == UIColor.black {
closestCell.backgroundColor = UIColor.blue
}
horizontalCollectionView.reloadItems(at: [self.horizontalCollectionView.indexPath(for: closestCell)!])
closestCell.backgroundColor = UIColor.black
}
}
Try this out:
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if collectionView == verticalCollectionView {
let size = CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height)
return size
}
let point = CGPoint(x: self.view.frame.size.width/2.0, y:35 )
//or you can try this code also
//let point = CGPoint(x: UIScreen.main.bounds.width/2, y:35 )
print(point)
let centerIndex = self.collectionView.indexPathForItemAtPoint(point)
if indexPath.item == centerIndex {
return CGSize(width: 70, height: 70)
}
return CGSize(width: 50, height: 50)
}
This code gives you the index of the item at the center of the collection View.
Hope this helps