Reputation: 2601
I would like to create a planning app, what I have already done, but my problem is that now, I would like to create different cells as following:
I don't know if it is possible...
Today, I have this code:
func numberOfSections(in collectionView: UICollectionView) -> Int { //colonnes: models
if _event != nil && _event!.models.count > 0
{
return _event!.models.count + 1
}
return 0
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int //lignes: slots
{
if _event != nil && _event!.models.count > 0 && _event!.models[0].slots.count > 0
{
if (section == 0) // A vérifier
{
return _event!.models[0].slots.count + 1
}
else
{
return _event!.models[section - 1].slots.count + 1
}
}
return 0
}
Upvotes: 2
Views: 1142
Reputation: 10712
UICollectionViewLayout
is definitely the way to go.
I've made a start on an example of this for you, It gets the number of sections and items and generates the layout you asked for, evenly distributing the items across the height of the UICollectionView
.
I've cropped the screenshot above so I don't take up to much space, heres the code
class OrganiserLayout:UICollectionViewLayout {
let cellWidth:CGFloat = 100
var attrDict = Dictionary<IndexPath,UICollectionViewLayoutAttributes>()
var contentSize = CGSize.zero
override var collectionViewContentSize : CGSize {
return self.contentSize
}
override func prepare() {
// Generate the attributes for each cell based on the size of the collection view and our chosen cell width
if let cv = collectionView {
let collectionViewHeight = cv.frame.height
let numberOfSections = cv.numberOfSections
self.contentSize = cv.frame.size
self.contentSize.width = cellWidth*CGFloat(numberOfSections)
for section in 0...numberOfSections-1 {
let numberOfItemsInSection = cv.numberOfItems(inSection: section)
let itemHeight = collectionViewHeight/CGFloat(numberOfItemsInSection)
let itemXPos = cellWidth*CGFloat(section)
for item in 0...numberOfItemsInSection-1 {
let indexPath = IndexPath(item: item, section: section)
let itemYPos = itemHeight*CGFloat(item)
let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attr.frame = CGRect(x: itemXPos, y: itemYPos, width: cellWidth, height: itemHeight)
attrDict[indexPath] = attr
}
}
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Here we return the layout attributes for cells in the current rectangle
var attributesInRect = [UICollectionViewLayoutAttributes]()
for cellAttributes in attrDict.values {
if rect.intersects(cellAttributes.frame) {
attributesInRect.append(cellAttributes)
}
}
return attributesInRect
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
// Here we return one attribute object for the specified indexPath
return attrDict[indexPath]!
}
}
To test this I made a basic UIViewController
and UICollectionViewCell
, here is the view controller:
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Use our new OrganiserLayout subclass
let layout = OrganiserLayout()
// Init the collection view with the layout
let collection = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collection.delegate = self
collection.dataSource = self
collection.backgroundColor = UIColor.white
collection.register(OrganiserCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
self.view.addSubview(collection)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0:
return 8
case 1:
return 6
case 2:
return 4
case 3:
return 2
case 4:
return 4
default:
return 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! OrganiserCollectionViewCell
cell.label.text = "\(indexPath.section)/\(indexPath.row)"
switch indexPath.section {
case 1:
cell.backgroundColor = UIColor.blue
case 2:
cell.backgroundColor = UIColor.red
case 3:
cell.backgroundColor = UIColor.green
default:
cell.backgroundColor = UIColor.cyan
}
return cell
}
}
And the UICollectionViewCell
looks like this:
class OrganiserCollectionViewCell:UICollectionViewCell {
var label:UILabel!
var seperator:UIView!
override init(frame: CGRect) {
super.init(frame: frame)
label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(label)
seperator = UIView()
seperator.backgroundColor = UIColor.black
seperator.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(seperator)
let views:[String:UIView] = [
"label":label,
"sep":seperator
]
let cons = [
"V:|-20-[label]",
"V:[sep(1)]|",
"H:|[label]|",
"H:|[sep]|"
]
for con in cons {
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: con, options: [], metrics: nil, views: views))
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Hope some of this helps, It's a very simplified example and you will probably need to modify it in order to achieve the calendar looking result that you require.
Upvotes: 1
Reputation: 19757
I believe you have to resolve to implementing your own custom layout for the collectionView
- you will need to implement custom UICollectionViewLayout
. This is not a trivial thing, but there are several good tutorials for it, for example this tutorial.
Upvotes: 1