32

I have added a collection view in viewDidLoad like this...

self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 10, 10) collectionViewLayout:flowLayout];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
self.collectionView.backgroundColor = [UIColor whiteColor];
[self.collectionView registerClass:[CameraCell class] forCellWithReuseIdentifier:CameraCellReuseIdentifier];
[self.collectionView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.collectionView];

And I have a UICollectionViewCell subclass called CameraCell with an init like this...

- (id)init
{
    self = [super init];
    if (self) {
        // Add customisation here...
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageChanged:) name:@"image" object:nil];

        self.contentView.backgroundColor = [UIColor yellowColor];

        self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        self.imageView.contentMode = UIViewContentModeScaleAspectFill;
        self.imageView.clipsToBounds = YES;
        [self.imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [self.contentView addSubview:self.imageView];

        NSDictionary *views = NSDictionaryOfVariableBindings(_imageView);

        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageView]|"
                                                                                 options:0
                                                                                 metrics:nil
                                                                                   views:views]];

        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_imageView]|"
                                                                                 options:0
                                                                                 metrics:nil
                                                                                   views:views]];
    }
    return self;
}

But when I run the app the collection view is there and I can scroll it but I can't see any cells. I have added a breakpoint in the cell's init and it never gets called. Is there another method I have to override?

EDIT

When I log the cell in cellForItemAtIndexPath...

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CameraCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CameraCellReuseIdentifier forIndexPath:indexPath];

    NSLog(@"%@", cell);

    return cell;
}

It displays the correct class...

<CameraCell: 0x1f07ba30; baseClass = UICollectionViewCell; frame = (0 20; 320 280); layer = <CALayer: 0x1f07bb40>> 
Charles
  • 50,943
  • 13
  • 104
  • 142
Fogmeister
  • 76,236
  • 42
  • 207
  • 306

6 Answers6

48

The method you need to implement is - (instancetype)initWithFrame:(CGRect)rect

Alejandro
  • 3,726
  • 1
  • 23
  • 29
28

I recently tried this in the iOS7 SDK, and I had to override initWithCoder because initWithFrame was never called.

Glen Selle
  • 3,966
  • 4
  • 37
  • 59
Archit Baweja
  • 973
  • 7
  • 18
  • 1
    Were you loading it from a storyboard or nib? Or all in code? – Fogmeister Sep 19 '13 at 06:40
  • The cell is loaded from a Storyboard. – Archit Baweja Sep 19 '13 at 17:55
  • 42
    ^Then that's why. `initWithCoder` is the designated initializer for views built from the storyboard. `initWithFrame` is for views that are built programatically. – Matthew Quiros Jan 21 '14 at 07:42
  • 2
    Frequent problem :) I usually put all construction in `init`, and then call it from overridden `initWithFrame` and `initWithCoder`. it makes cell class more universal. Or, when I need to save original initWithCoder behavior, I make another one method `universalInit`, put all my custom code there and call it from all default initializers. Of course, with Swift this trick is no more handy. – Cemen Mar 13 '15 at 10:01
24

If the cell is loaded from a StoryBoard then you will want to override the initializers like this:

Swift

override init?(coder aDecoder: NSCoder) {
  super.init(coder:aDecoder)
  //You Code here
} 

Objective-C

- (id)initWithCoder:(NSCoder *)aDecoder {
  self = [super initWithCoder:aDecoder];
  //You code here
}

If you are loading from a nib, then you will want to override the initializers like this:

Swift

override init(frame: CGRect) {
  super.init(frame:frame)
  //You Code here
} 

Objective-C

- (id)initWithFrame:(CGRect)frame {
  self = [super initWithFrame:frame];
  //You code here
}
Barlow Tucker
  • 6,229
  • 3
  • 36
  • 42
2

I know the question is way old, but personally I was lazy and wanted to copy-paste code from somewhere. Unfortunately, there is no 100% correct solution above so I wrote common pattern (for both IB-based and code-based cells)

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    override func awakeFromNib() {
        commonInit()
    }
    
    private func commonInit() {
        
    }
iago849
  • 1,818
  • 1
  • 13
  • 9
-1

In my case, when I am using [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 10, 10) collectionViewLayout:flowLayout] to programatically instantiate a collectionView, initWithFrame never get called, but initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout did.

Jakehao
  • 1,019
  • 8
  • 15
-1

Swift 5:

required init?(coder aDecoder: NSCoder) {
    super.init(coder:aDecoder)
    // Your code here.
}
Joe
  • 3,772
  • 3
  • 33
  • 64