Change UICollectionView cell X position - swift - ios

How can I change the X position of every single cell programmatically? I can change the width and height of the cell as my wish but it is unable to complete the project because of XY position.
My result ,
Expected result is,
and my full view controller code is,
import UIKit
class ReviewVC: UIViewController , UICollectionViewDataSource , UICollectionViewDelegate , UICollectionViewDelegateFlowLayout{
var cellWidthArr : [CGFloat] = [60.0 , 170.0 , 110.0 , 120.0]
var titleArr = [" Polite " , " Showed up on time ", " Quick responses " , " Fair prices "]
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Review your experiance"
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
collectionView!.collectionViewLayout = layout
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ReviewCollectionCell", for: indexPath)as? ReviewCollectionCell
cell?.titleLabel.text = self.titleArr[indexPath.row]
cell?.titleLabel.layer.cornerRadius = (cell?.titleLabel.bounds.size.height)!/2
cell?.titleLabel.clipsToBounds = true
cell?.titleLabel.layer.borderColor = (UIColor.init(red: 240/255, green: 36/255, blue: 70/255, alpha: 1.0)).cgColor
cell?.titleLabel.layer.borderWidth = 1.0
return cell!
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: self.cellWidthArr[indexPath.row], height: 30)
}
}
Anyone please help me to findout the solution.Thanks...

You can use the open source project TagCellLayout written by riteshhgupta in GitHub.
Sample Code to initialize the layout:
import TagCellLayout
let tagCellLayout = TagCellLayout(alignment: .left, delegate: self)
collectionView.collectionViewLayout = tagCellLayout
I know you might write the lots of code for this, but it would be helpful greatly and even you can customize to different layout alignments.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return (ArrayName[indexPath.row] as NSString).size(withAttributes: nil)
}
Rather than giving Cell Fixed Size in an Array there is a way to find label size in sizeForItemAtIndexPath return the size of the text So it will find the size and work

Related

Dynamic item height with equal spacing

What I want to achieve is a layout like this.
Inside to box there will be other content like image, text, button and so on. what I want is make the box sizing dynamic so that it's it takes the height of it's content. The first option came to my mind is use a collection view flow layout.
After some trial and error I end up with this layout.
My Problem is the inter item spacing I want them to be equal, is there any way to achieve this?
Any suggestion I greatly appreciated.
What I have tried so far.
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCollectionViewCell", for: indexPath) as! MyCollectionViewCell
cell.configure(title: data[indexPath.row].title)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let spacing: CGFloat = 10
let numberOfItemsPerRow:CGFloat = 2
let spacingBetweenCells:CGFloat = 10
let totalSpacing = (2 * spacing) + ((numberOfItemsPerRow - 1) * spacingBetweenCells)
let approximateWidthOfContent = view.frame.width - 32
let size = CGSize(width: approximateWidthOfContent, height: 1000)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: data[indexPath.row].title).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
if let collection = self.collectionView{
let width = (collection.bounds.width - totalSpacing)/numberOfItemsPerRow
return CGSize(width: width, height: estimatedFrame.height + 50)
}else{
return CGSize(width: 0, height: 0)
}
}
}
I know there are similar questions but I could not find any which meets my requirement.
After following a tutorial from raywenderlich I finally managed to achieve the layout I wanted, Thanks people who have helped me suggesting with important clues.
What I achieved
The link for the Instructions I have followed is here.

viewForSupplementaryElementOfKind is not being called in iOS 13 device

I try to add footer view in my collection view layout with collection view delegate method viewForSupplementaryElementOfKind but it's not being called while reload collection view.
SeatArrangementViewController : UICollectionViewDelegate , UICollectionViewDataSource , QuiltViewDelegate ,UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
setCollection()
}
func setCollection() {
self.clnSeats.collectionViewLayout.invalidateLayout()
self.clnSeats.register(UINib(nibName: "SeatFooterView",bundle:nil),forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: "SeatFooterView")
let space = UIScreen.main.bounds.size.width - 262
self.clnSeats.contentInset = UIEdgeInsets(top: 0, left: space/2.0, bottom: 90, right: space/2.0)
let layout = self.clnSeats.collectionViewLayout as! QuiltView
layout.scrollDirection = UICollectionViewScrollDirection.vertical
layout.itemBlockSize = CGSize(width: 50, height: 50)
self.clnSeats.reloadData()
}
Collection View Methods
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind{
case UICollectionElementKindSectionFooter: .....
}
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, blockSizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
// get random width and height values for the cells
let width = self.numberWidths[indexPath.row]
let height = self.numberHeights[indexPath.row]
return CGSize(width: width, height: height)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForItemAtIndexPath indexPath: IndexPath) -> UIEdgeInsets {
return UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
}
I also add #objc (collectionView:viewForSupplementaryElementOfKind:atIndexPath:) but that not work for me.
Check if you've registered a supplementary view.
Check if delegate and data source are set.
If you are using custom size for footer or header ensure that it returns a size that greater than zero otherwise delegate will stop trying to get a supplementary view
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize

How to make Collection View cell more than one column?

Here the output of the code but I want to make it into 2 column
I want to make a collection view with more than 1 column but this code just shows one column. Can anyone help me how to fixed it??? Thank You....
import UIKit
class SearchViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
Here the array
var collectionArray : [String] = ["1", "2", "3", "4"]
let labelnamee = [("Long Sleeve Abstract Print Top"), ("Logo Graphic Print Top"), ("Material Study T-Shirt"), ("Mission Statement Print T-Shirt"), ("Printed Logo T-Shirt"), ("Tie-Dye Print T-Shirt")]
let labelpricee = [("RM 1021"), ("RM 860"), ("RM 750"), ("RM 750") , ("RM 670"), ("RM 760")]
let imagee = [UIImage(named: "abstractprint"),
UIImage(named: "longsleeve"),UIImage(named: "studymaterial"),UIImage(named: "mission"), UIImage(named: "white"), UIImage(named: "tiedye")]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return labelnamee.count
}
for the collection cell border
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
let cellIndex = indexPath.item
cell.imageView.image = imagee[cellIndex]
cell.labelname.text = labelnamee[cellIndex]
cell.labelprice.text = labelpricee[cellIndex]
cell.contentView.layer.cornerRadius = 10
cell.contentView.layer.borderWidth = 1.0
cell.contentView.layer.borderColor = UIColor.blue.cgColor
cell.backgroundColor = UIColor.white
cell.layer.shadowColor = UIColor.gray.cgColor
cell.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
cell.layer.shadowRadius = 2.0
cell.layer.shadowOpacity = 1.0
cell.layer.masksToBounds = false
cell.layer.shadowPath = UIBezierPath(roundedRect: cell.bounds, cornerRadius: cell.contentView.layer.cornerRadius).cgPath
return cell
}
}
Here the code for the layout. I think I made mistake in the height or weight..
extension SearchViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let bounds = collectionView.bounds
let heigtVal = self.view.frame.height
let widthVal = self.view.frame.width
let cellsize = (heigtVal < widthVal) ? bounds.height/2: bounds.width/2
return CGSize(width: cellsize - 10, height: cellsize - 10)
}
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 10
}
}
If the width of the cell or the contents of the cells is such that the collectionView cannot accommodate be able to see them within the visible portion of the screen, the collection view automatically places next to them. You have to scroll your collecctionView to see your next column.
If you want to see two columns within the visible screen portion, pls follow the steps:
Firstly place all your views inside a StackView and align them vertically.
Then set the right width & height constraints to your imageView to maintain the aspect ratio for your requirements. I have set like below just to demonstrate:
Along with the above constraints, I have also set the cell size to automatic in Storyboard itself rather than doing programmatically. Removed sizeForItemAt indexPath from code
:
Finally, after following all the above steps the output looked like this

Remove Extra Space in Collection View in Swift

Code:
let reuseIdentifier = "cell"
var items = ["1", "2", "3", "4","5","6","7","8", "9", "10", "11","12","13","14"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.myLabel.text = self.items[indexPath.item]
cell.backgroundColor = UIColor.red
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: CGFloat((collectionView.frame.size.width / 2) - 8), height: CGFloat(250))
}
Do this according to your requirements hope it will help you to get an idea.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 10, left: 1, bottom: 5, right: 0) // according to your requirements
layout.minimumInteritemSpacing = 1 // according to your requirements
layout.minimumLineSpacing = 1// according to your requirements
If you want more help or this not work for you, you can also go through that link which can help you to achieve your goal. Click on this link
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: CGFloat((collectionView.frame.size.width / 2) - 8), height: (collectionView.frame.size. height / 3) - 8))
}

Set collectionView size. (sizeForItemAtIndexPath function is not working) Swift 3

I have a tableView and in every tableViewCell is a collectionView.
I am trying to change the collectionView size with this code:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView.tag == 2{
return CGSize(width: 100, height: 100)
}else if collectionView.tag == 4 {
return CGSize(width: 222, height: 200)
}else{
return CGSize(width: 10, height: 10)
}
}
The problem is that there is no error but cells do not change sizes
If you need to know something more, just write a comment.
Thank you.
First, don't forget to extends from UICollectionViewDelegateFlowLayout delegate.
For single collectionview display logic :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = ((collectionView.frame.width - 15) / 2) // 15 because of paddings
print("cell width : \(width)")
return CGSize(width: width, height: 200)
}
And important point in storyboard don't forget Estimate Size : None
The signature for this method has changed in Swift 3 to:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
// your code here
}
Pay attention to the subtle difference: (_ collectionView: ... instead of (collectionView: ....
This is because in Swift 3, you have to explicitly specify the label of the first parameter. You can override this default behaviour by adding the underscore character _.
Also, make sure that your class adopts both UICollectionViewDelegate and UICollectionViewDelegateFlowLayout protocols
class MyViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
// your code here
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
// your code here
}
}
Refer to this link to learn more about changes in Swift 3.
You need to specify that you implement the protocol UICollectionViewDelegateFlowLayout in your class declaration.
class PhrasesCompactCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout
So This is my code right now and it works:
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.setContentOffset(collectionView.contentOffset, animated:false)
collectionView.reloadData()
collectionView.register(UINib(nibName: "eventsCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "eventsCollectionViewCell")
if row == 2{
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: 120, height: 120)
layout.scrollDirection = .horizontal
collectionView.collectionViewLayout = layout
}else if row == 4 {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: 222, height: 200)
layout.scrollDirection = .horizontal
collectionView.collectionViewLayout = layout
}else{
print("else")
}
Short Version:
Try adding
collectionView.delegate = dataSource
Longer version:
For Me this was a small mistake -
collectionView.dataSource = self
This would call these methods
func numberOfSections(in collectionView: UICollectionView) -> Int
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
However it would NOT call
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
Because this is not part of UICollectionView 's dataSource. It's part of UICollectionView's delegate.
So adding the following solved my issue.
collectionView.delegate = dataSource
I've had the same problem! If you make the size half the width (width/2), then it will still appear like the entire width because the padding is being added to the width/2.
Fix: Make sure to set the section insets in Storyboard to 0. Alternatively, adjust the width/2 to a size that INCLUDES the section insets.
It took me a while to fix that one ;-)
Don't forget to assign UICollectionViewFlowLayout object to your collectionViewLayout since UICollectionView uses UICollectionViewLayout (not UICollectionViewFlowLayout) by default.
let flowLayout = UICollectionViewFlowLayout()
collectionView.collectionViewLayout = flowLayout

Resources