UICollectionViewCell register class fails, but register nib works

13,517

Solution 1

I see this link Overview Collection View

If the cell class was written in code, the registration is performed using the registerClass: method of UICollectionView. For example: [self.myCollectionView registerClass:[MyCollectionViewCell class] forCellWithReuseIdentifier:@"MYCELL"]; In the event that the cell is contained within an Interface Builder NIB file, the registerNib: method is used instead.

So your collection cell create with nib, you should register with nib. If your cell written totally in code you will need register with class.

Hope this help.

Solution 2

Actually if you register the class in the storyboard and give it a reuse identifier there, then you shouldn't be registering it's class or it's nib in code.

Solution 3

It is not collectionview that is failing here. You custom class contains label which is implicitly unwrapped optional defined as this,

@IBOutlet weak var title: UILabel!

And that is the reason for failure. Where do you instantiate it ? And your datasource methods gets called which is like this,

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("myCell", forIndexPath: indexPath) as! MyCollectionViewCell

    cell.title.text = data[indexPath.row]

    return cell
}

There you are trying to set text to this property title which is nil, which crashes your app.

Initialize your label inside collectionView initWithFrame: method if you use it in code that should should be fixed.

Add this code to your cell subclass when using in code,

class MyCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var title: UILabel!

    override init(frame: CGRect) {
        super.init(frame: frame)
        let title = UILabel(frame: CGRectZero)
        title.translatesAutoresizingMaskIntoConstraints = false;
        contentView.addSubview(title)
        self.title = title

        title.topAnchor.constraintEqualToAnchor(contentView.topAnchor).active = true
        title.leftAnchor.constraintEqualToAnchor(contentView.leftAnchor).active = true
        self.backgroundColor = UIColor.redColor()
    }

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

}

Solution 4

if you define cell from a nib file,system will choose nib file to instantiate the cell. If not define any nib file(totally maintain by code), the cell should be init by the

- initWithStyle:reuseIdentifier:

method. When you use

- dequeueReusableCellWithIdentifier:

the method - awakeFromNib will be called if you use nib to maintain the view; else

- initWithStyle:reuseIdentifier:

will be called.

Share:
13,517
Jason Sturges
Author by

Jason Sturges

Avant-garde experimental artist - creative professional leveraging technology for immersive experiences.

Updated on June 06, 2022

Comments

  • Jason Sturges
    Jason Sturges about 2 years

    Creating a custom UICollectionViewCell for my UICollectionViewController, it works by registering the nib; however, fails to register by class.

    Using registerClass() - fails

    Registering by class seems correct - it builds, but throws an exception at runtime unwrapping optional elements from the UIView. Without referencing outlets, it runs; however, no cells appear within the collection view.

    collectionView?.registerClass(MyCollectionViewCell.self, 
                                  forCellWithReuseIdentifier: "myCell")
    

    Clearly the view is not loaded; however, I'd think setting the custom class of the cell would provide necessary linkage.

    Using registerNib() - works

    Registering by nib works, which makes sense as it is explicitly loading the view.

    let nib = UINib(nibName: "MyCollectionViewCell", bundle: nil)
    collectionView?.registerNib(nib, forCellWithReuseIdentifier: "myCell")
    

    Here the view is explicitly referenced, and custom class is set for the view; however, something about this seems wrong.

    Sample project

    Isolating the issue in a sample project, below are views and code to replicate; or, this project is available at GitHub.

    Main.storyboard

    My main storyboard initial view controller is a UICollectionViewController subclassed as MyCollectionViewController:

    UICollectionViewController

    MyCollectionViewController.swift

    Showing both registering by nib, and by class:

    import UIKit
    
    class MyCollectionViewController: UICollectionViewController {
    
        var data = [String]()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            data = ["a", "b", "c"]
    
            // This works, by nib
            let nib = UINib(nibName: "MyCollectionViewCell", bundle: nil)
            collectionView?.registerNib(nib, forCellWithReuseIdentifier: "myCell")
    
            // This fails, by class
            //collectionView?.registerClass(MyCollectionViewCell.self, forCellWithReuseIdentifier: "myCell")
        }
    
        override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return data.count
        }
    
        override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCellWithReuseIdentifier("myCell", forIndexPath: indexPath) as! MyCollectionViewCell
    
            cell.title.text = data[indexPath.row]
    
            return cell
        }
    
    }
    

    MyCollectionViewCell.xib

    View is a UICollectionViewCell subclassed as MyCollectionViewCell with a UILabel:

    MyCollectionViewCell

    In my nib the Collection Reusable View Identifier has been set to: myCell:

    MyCollectionViewCell-identifier

    MyCollectionViewCell.swift

    Defines the class, and has an IBOutlet to the label:

    import UIKit
    
    class MyCollectionViewCell: UICollectionViewCell {
    
        @IBOutlet weak var title: UILabel!
    
    }
    

    Using the nib, execution appears as:

    iphone-4s

    Why can't I register UICollectionViewCell by class?

    As well, I'm a little fuzzy as to whether the prototype cell needs to remain on the main storyboard collection view controller. There, I have not defined any reusable view identifier.

  • Jason Sturges
    Jason Sturges over 8 years
    It's not - I don't want to use the prototype cell as there's no way to subclass it. This cell only provides size, unless I'm missing something.
  • Jason Sturges
    Jason Sturges over 8 years
    title is just an outlet to a UILabel instantiated by the view, unless I'm missing something, here. This still would not link the view, though, as myCell is defined in the nib. This fails with: reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier myCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'` - really appreciate this insight, though, thanks.
  • Sandeep
    Sandeep over 8 years
    It just does not matter if your property has @IBOutlet. It is just some qualifier to let nib know that a view has some connection to property in code. If you are using code to registerClass then use the code as I did above. Or else if you use nib then your code should work just fine without any modification.
  • Sandeep
    Sandeep over 8 years
    Where do you register your class not in code ?? Where in nib file or storyboard ?
  • Jason Sturges
    Jason Sturges over 8 years
    Aha - your solution works perfectly. This provides a code implementation that works using registerClass. Sorry for the confusion - this is correct.