Aligning CollectionView Cells to Left with equal spacing in between (Cells will be of dynamic width depending on the data received i.e the length of the string that will go in the cell).
My Custom View is TrendingView.swift
//
// TrendingView.swift
//
// Created by Abhishek Chandrakant Gidde on 24/05/23.
//
import UIKit
protocol TrendingViewDelegate: AnyObject {
func didSelectScrip(scrip:ScripModel)
func seeAllScrips()
func seeAllBreakouts()
}
enum DataType {
case EQUITY
case DERIVATIVES
func isEquityData() -> Bool {
switch self {
case .EQUITY:
return true
case .DERIVATIVES:
return false
}
}
}
class TrendingView: UIView {
@IBOutlet var contentViewRoot: UIView!
@IBOutlet weak var lblTrendingTitle: UILabel!
@IBOutlet weak var clvTrendingData: UICollectionView!
var isTrending = true
var dataType: DataType?
var arrayTrendingData = [MarketScripModel]() {
didSet{
self.updateCollectionViewUI()
}
}
var arrayBreakoutData = [SignalListModel]() {
didSet{
self.updateCollectionViewUI()
}
}
// MARK: Properties
weak var delegate:TrendingViewDelegate?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
customInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
customInit()
}
override func awakeFromNib() {
super.awakeFromNib()
customInit()
}
func customInit(){
Bundle.main.loadNibNamed("TrendingView",owner: self, options: nil)
addSubview(self.contentViewRoot)
self.contentViewRoot.frame = self.bounds
self.contentViewRoot.autoresizingMask = [.flexibleWidth,.flexibleHeight]
clvTrendingData.dataSource = self
clvTrendingData.delegate = self
clvTrendingData.register(UINib(nibName: "TrendingViewCell", bundle: .main),forCellWithReuseIdentifier: "trendingViewCell")
clvTrendingData.collectionViewLayout = CustomCollectionViewFlowLayout()
}
func updateCollectionViewUI() {
DispatchQueue.main.async {
if self.isTrending {
if self.dataType == .EQUITY {
self.lblTrendingTitle.text = "Trending Stocks"
} else if self.dataType == .DERIVATIVES {
self.lblTrendingTitle.text = "Trending Strikes"
}
} else {
if self.dataType == .EQUITY {
self.lblTrendingTitle.text = "Breakout Stocks"
} else if self.dataType == .DERIVATIVES {
self.lblTrendingTitle.text = "Breakout Futures"
}
}
//
self.clvTrendingData.reloadData()
}
}
// MARK: Actions
@IBAction func onSellAllTap(_ sender: Any) {
if(isTrending){
self.delegate?.seeAllScrips()
}
else{
self.delegate?.seeAllBreakouts()
}
}
}
// CollectionView FlowLayout
extension TrendingView:UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}
}
extension TrendingView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if self.isTrending {
return self.arrayTrendingData.count
} else {
return self.arrayBreakoutData.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let trendingViewCell: TrendingViewCell = clvTrendingData.dequeueReusableCell(withReuseIdentifier: "trendingViewCell", for: indexPath) as! TrendingViewCell
if(isTrending) {
print("Returning Trending Data Cell")
trendingViewCell.configureCell(scripModel: arrayTrendingData[indexPath.row],isEquityData: dataType!.isEquityData() )
} else {
print("Returning Derivative Data Cell")
trendingViewCell.configureCell(scripModel: arrayBreakoutData[indexPath.row],isEquityData: dataType!.isEquityData())
}
trendingViewCell.layoutIfNeeded()
return trendingViewCell
}
}
extension TrendingView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if(isTrending){
self.delegate?.didSelectScrip(scrip: arrayTrendingData[indexPath.row].scrip)}
else{
self.delegate?.didSelectScrip(scrip: arrayBreakoutData[indexPath.row].scrip)
}
}
}
My Trending View Cell is TrendingViewCell.swift
//
// TrendingViewCell.swift
//
// Created by Abhishek Chandrakant Gidde on 24/05/23.
//
import UIKit
enum CallType: String {
case BUY = "buy"
case SELL = "sell"
}
class TrendingViewCell: UICollectionViewCell {
@IBOutlet weak var lblScripTitle: UILabel!
@IBOutlet weak var ivTrendingIcon: UIImageView!
private var isStockPositive: Bool?{
didSet{
updateUI()
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func configureCell(scripModel: MarketScripModel, isEquityData:Bool){
if(!isEquityData){
let nameOfScrip = scripModel.marketScripModel.Name
let components = nameOfScrip?.components(separatedBy: "|")
// Extracting the underlying asset (e.g., "NIFTY")
let underlyingAsset = components?[0].trimmingCharacters(in: .whitespaces)
// Extracting the expiration date (e.g., "25MAY23")
let expirationDate = components?[1].trimmingCharacters(in: .whitespaces)
let strikePrice = scripModel.marketScripModel.LTP
let trendingStrikeText = "(underlyingAsset!) (strikePrice!) (expirationDate!)"
print("debugabhi:")
print(underlyingAsset!)
print(expirationDate!)
print(strikePrice!)
print(trendingStrikeText)
self.lblScripTitle.text=trendingStrikeText
// lblScripTitle.text = "NIFTY 18,400 25MAY2023"
}
else{
lblScripTitle.text = scripModel.scrip.scripNameWithExpiry()}
if let ltpChange = scripModel.marketScripModel.PerChange {
isStockPositive = ltpChange >= 0
}
}
func configureCell(scripModel: SignalListModel, isEquityData:Bool){
if(!isEquityData){
let nameOfScrip = scripModel.scrip.scripNameWithExpiry()
self.lblScripTitle.text = nameOfScrip
if scripModel.callType.removingWhitespaces().lowercased() == CallType.BUY.rawValue {
isStockPositive = true
}
else {
isStockPositive = false
}
}
else{
lblScripTitle.text = scripModel.scrip.scripNameWithExpiry()
if scripModel.scrip.changePer() >= 0{
isStockPositive = true
}
else{
isStockPositive = false
}
}
}
func updateUI(){
self.ivTrendingIcon.image = isStockPositive! ? UIImage(named: "icn_stockPositive") : UIImage(named: "icn_stockNegative")
}
}
I have tried my best to do everything I can but it is too frustrating to get it the way it is needed, I have also tried using a custom class as suggested by ChatGPT like class WrapFlowLayout: UICollectionViewFlowLayout {...
and setting collectionView.collectionViewLayout = WrapflowLayout()
but that leaves me with the cells aligning to the left and wrapping just as i need but then the cell width is not as per my constrains or my data it looks like this.
2
Answers
Use this layout class for tags layout
after that give estimated size to cell
You may use Composure – a 3rd party open source library to achieve this fairly easily. No need to create your own custom class or flow layout. It looks like you have 2 sections in your view but both of these sections need to be laid out the same way. Your cells appear to have a fixed height but dynamic width. In other words, your cells in both sections need to grow width-wise depending on the contents.
Step 1: Once you add Composure to your project define an enum like so:
Step 2: In your View Controller while configuring your collection view, use this line for your collectionViewLayout instead of whatever you currently have.
Result
If your cell is properly constrained, you should be able to achieve the layout you are looking for. Let us know in the comments if you have additional questions.