ΩlostA
ΩlostA

Reputation: 2601

iOS: How to create a collectionView with different size & clickable cells

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:

enter image description here

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
}

With this result: enter image description here

Upvotes: 2

Views: 1142

Answers (2)

Wez
Wez

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.

Collection View Layout Example

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

Milan Nos&#225;ľ
Milan Nos&#225;ľ

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

Related Questions