I mean initially there is dummy data then data gets loaded.
I am using collection view for the EPG. I want to load the data dynamically and at the same time I want to set the collection view cell width dynamically.
For example please see the JIOTV app. I am trying to do same thing in my application.
Below is my code of the custom layout.
import UIKit
class CustomCollectionViewLayout: UICollectionViewLayout {
var numberOfColumns = 8
var shouldPinFirstColumn = true
var shouldPinFirstRow = true
var sectionNumber = 0
var itemAttributes = [[UICollectionViewLayoutAttributes]]()
var itemsSize = [CGSize]()
var contentSize: CGSize = .zero
var arr = [String]()
var generalArr = [[String]]()
var durationArr = [String]()
override func prepare() {
guard let collectionView = collectionView else {
return
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
numberOfColumns = appDelegate.timeArr.count//appDelegate.eventNameArr.count //appDelegate.multipleColumns
// print(numberOfColumns)
if appDelegate.generalArr.count > 0 {
durationArr = appDelegate.generalArr[0]
generalArr = appDelegate.generalArr
if collectionView.numberOfSections == 0 {
return
}
if itemAttributes.count != collectionView.numberOfSections {
generateItemAttributes(collectionView: collectionView)
return
}
for section in 0..<collectionView.numberOfSections {
for item in 0..<collectionView.numberOfItems(inSection: section) {
if section != 0 && item != 0 {
continue
}
let attributes = layoutAttributesForItem(at: IndexPath(item: item, section: section))!
if section == 0 {
var frame = attributes.frame
frame.origin.y = collectionView.contentOffset.y
attributes.frame = frame
}
if item == 0 {
var frame = attributes.frame
frame.origin.x = collectionView.contentOffset.x
attributes.frame = frame
}
}
}
}
}
override var collectionViewContentSize: CGSize
{
return contentSize
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return itemAttributes[indexPath.section][indexPath.row]
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var attributes = [UICollectionViewLayoutAttributes]()
for section in itemAttributes {
let filteredArray = section.filter { obj -> Bool in
return rect.intersects(obj.frame)
}
attributes.append(contentsOf: filteredArray)
}
return attributes
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
func convertHourtoMin(strTime : String) -> Int {
var components: Array = strTime.components(separatedBy: ":")
let hours = Int(components[0]) ?? 0
let minutes = Int(components[1]) ?? 0
let seconds = Int(components[2]) ?? 0
return ((hours * 60) + (minutes) + (seconds / 60))
}
}
// MARK: - Helpers
extension CustomCollectionViewLayout {
func generateItemAttributes(collectionView: UICollectionView) {
if itemsSize.count != numberOfColumns {
calculateItemSizes()
}
var column = 0
var xOffset: CGFloat = 0
var yOffset: CGFloat = 0
var contentWidth: CGFloat = 0
itemAttributes = []
for section in 0..<collectionView.numberOfSections {
var sectionAttributes: [UICollectionViewLayoutAttributes] = []
arr = generalArr[section]
// print("General Array : \(arr)")
// print("General Array count : \(arr.count)")
numberOfColumns = arr.count
durationArr = arr
let appDelegate = UIApplication.shared.delegate as! AppDelegate
for index in 0..<numberOfColumns {
var itemSize = itemsSize[index]
if numberOfColumns == appDelegate.timeArr.count {
itemSize = itemsSize[index]
}
else {
calculateItemSizes()
itemSize = itemsSize[index]
}
let indexPath = IndexPath(item: index, section: section)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = CGRect(x: xOffset, y: yOffset, width: itemSize.width, height: itemSize.height).integral
if section == 0 && index == 0 {
// First cell should be on top
attributes.zIndex = 1024
} else if section == 0 || index == 0 {
// First row/column should be above other cells
attributes.zIndex = 1023
}
// Below code with section == 0 and index till end
/* if section == 0 && 0 < numberOfColumns {
attributes.frame = CGRect(x: xOffset, y: yOffset, width: 100, height: 54).integral
}
*/
if section == 0 {
var frame = attributes.frame
frame.origin.y = collectionView.contentOffset.y
attributes.frame = frame
}
if index == 0 {
var frame = attributes.frame
frame.origin.x = collectionView.contentOffset.x
attributes.frame = frame
}
sectionAttributes.append(attributes)
xOffset += itemSize.width
column += 1
if column == numberOfColumns {
if xOffset > contentWidth {
contentWidth = xOffset
}
column = 0
xOffset = 0
yOffset += itemSize.height
}
}
itemAttributes.append(sectionAttributes)
}
if let attributes = itemAttributes.last?.last {
contentSize = CGSize(width: contentWidth, height: attributes.frame.maxY)
}
}
func calculateItemSizes() {
itemsSize = []
let appDelegate = UIApplication.shared.delegate as! AppDelegate
if numberOfColumns == appDelegate.timeArr.count{
for index in 0..<numberOfColumns {
itemsSize.append(sizeForItemWithColumnIndexA(index))
}
}
else {
for index in 0..<numberOfColumns {
itemsSize.append(sizeForItemWithColumnIndex(index))
}
}
}
func sizeForItemWithColumnIndex(_ columnIndex: Int) -> CGSize {
var text: NSString
switch columnIndex {
case 0: return CGSize(width: 80, height: 40) //54
// case 0: return CGSize(width: 106, height: 54)
// case 1:
// text = "MMM-99"
default:
text = "Content"
//return CGSize(width: 100, height: 54)
// for Stringresult in durationArr[columnIndex]
// Below is code to make the cell dynamic
var width:Float = Float(convertHourtoMin(strTime: durationArr[columnIndex]))
var actualWidth:Float = Float((width / 60) * 200) // * 100
actualWidth = actualWidth + actualWidth
// print("Actual Width : \(actualWidth)")
return CGSize(width: Int(actualWidth), height: 40) // 54
//}
}
// let size: CGSize = text.size(withAttributes: [kCTFontAttributeName as NSAttributedStringKey: UIFont.systemFont(ofSize: 14.0)])
// let width: CGFloat = size.width + 16
return CGSize(width: 100, height: 54)
}
// Below method is for time row in EPG VIEW
func sizeForItemWithColumnIndexA(_ columnIndex: Int) -> CGSize {
var text: NSString
switch columnIndex {
case 0: return CGSize(width: 80, height: 25)
// case 0: return CGSize(width: 106, height: 54)
// case 1:
// text = "MMM-99"
default:
text = "Content"
return CGSize(width: 200, height: 25) // originally width : 100
// for Stringresult in durationArr[columnIndex]
// Below is code to make the cell dynamic
var width:Float = Float(convertHourtoMin(strTime: durationArr[columnIndex]))
var actualWidth:Float = Float((width / 60) * 100)
actualWidth = actualWidth + actualWidth
// print("Actual Width : \(actualWidth)")
return CGSize(width: Int(actualWidth), height: 54)
//}
}
// let size: CGSize = text.size(withAttributes: [kCTFontAttributeName as NSAttributedStringKey: UIFont.systemFont(ofSize: 14.0)])
// let width: CGFloat = size.width + 16
return CGSize(width: 100, height: 35)
}
}
Check this out
For an EPG you will basically need to calculate the size of the cell and also it's position for each program for each channel.