nicklockwood / layout

A declarative UI framework for iOS
MIT License
2.23k stars 97 forks source link

Creating a custom UICollectionViewCell #152

Open vegidio opened 6 years ago

vegidio commented 6 years ago

I'm trying to create a custom UICollectionViewCell, but I'm having some problems. Here is what I'm doing:

  1. I created a cell layout called MyCustomCell.xml:
<UICollectionViewCell>

    <UILabel
        outlet="labelName"
        text="Name" />

</UICollectionViewCell>
  1. I created a custom UICollectionViewCell called MyCustomCollectionViewCell.swift that reads the layout above:
import Layout
import UIKit

class MyCustomCollectionViewCell: UICollectionViewCell, LayoutLoading
{
    @IBOutlet private weak var labelName: UILabel?
    var layoutNode: LayoutNode?

    override init(frame: CGRect) {
        super.init(frame: frame)
        loadLayout(named: "MyCustomCell.xml")
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        // Ensure layout is updated after screen rotation, etc
        self.layoutNode?.view.frame = self.bounds

        // Update frame to match layout
        self.frame.size = self.intrinsicContentSize
    }

    public override var intrinsicContentSize: CGSize {
        return layoutNode?.frame.size ?? .zero
    }
}

Then I tried to use this custom cell in my UICollectionView. My collection view also uses Layout and it's created like this:

  1. This is the layout file:
<UICollectionView
    outlet="collectionView"
    backgroundColor="#ffffff">

    <MyCustomCollectionViewCell reuseIdentifier="cell" />

</UICollectionView>
  1. And this the class that loads the layout above:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
    let cell = collectionView.dequeueReusableCellNode(withIdentifier: "cell", for: indexPath)
    return cell.view as! MyCustomCollectionViewCell
}

But after I do all this, I get the error message UICollectionViewCells must be created by UICollectionView in MyCustomCollectionViewCell.xml

My idea is to reuse MyCustomCollectionViewCell in different UICollectionViews so, how can I prevent the error above?

Thanks.

nicklockwood commented 6 years ago

@vegidio calling loadLayout(named: "...") loads the contents of that xml file as a subview of the view that called it. The root node in your MyCustomCell.xml file is a UICollectionViewCell, and you are loading it inside a subclass of UICollectionViewCell, so you are effectively trying to mount one cell inside another one.

It's fine to load the contents of a custom cell from an xml file like this, but you should either remove the root <UICollectionViewCell> node from the xml, or change it to a plain <UIView>.

vegidio commented 6 years ago

@nicklockwood Thanks for the explanation!

I removed the <UICollectionViewCell> from MyCustomCell.xml and I don't get the red error message anymore, but my cells are completely empty, even though I left a UILabel in the layout file.

I inspected the view (please see screenshot attached) and I noticed that there's nothing there, so I have the impression that the views were not loaded from the XML file into the UICollectionView, so I suspect that the code in my class MyCustomCollectionViewCell.swift that I posted before, might be wrong:

screenshot 2018-10-22 at 18 16 50

Can you see something that I might be missing?

Thanks!