Is anyone having a problem with Xcode 6 when adding a Collection View Controller to storyboards and naming the custom class?
-> I drag a Collection View Controller to my scene -> add a new file -> name it "LCCollectionViewController" with a subclass of UICollectionViewController.
Next, I select the Collection View Controller in storyboard, and name the custom class LCCollectionViewController in the identity inspector.
Problem: The document outline still shows the name as Collection View Controller.
Code just in case:
LCCollectionViewController.m
#interface LCCollectionViewController ()
{
NSArray *theImages;
}
#end
#implementation LCCollectionViewController
static NSString * const reuseIdentifier = #"Cell";
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
moonImages = [NSArray arrayWithObjects:#"image1.gif", #"image2.gif", #"image3.gif", #"image4.gif", nil];
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [moonImages count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
UIImageView *theImageView = (UIImageView *)[cell viewWithTag:100];
theImageView.image = [theImages objectAtIndex:indexPath.row];
return cell;
}
#end
I encountered same bug in xcode6. What helped me was to change subclass to any other viewcontroller subclass you have set up already (perhaps create temporary one if none is available) and then change it back to LLCollectionViewController.
Related
I'm trying to create a simple View : a tableView which takes like 80% of the screen, and a collectionView with 4 rows at the bottom. Everything works fine with my tableView but I can't get things right with my collectionView. I always get the following exception :
Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'could not dequeue a view of kind: UICollectionElementKindCell with
identifier simpleCollectionCell - must register a nib or a class for the
identifier or connect a prototype cell in a storyboard'.
I've been looking in my View Controller : there are 2 refering outlets for my collectionVIew : datasource and delegate. So it's fine. And I'm using the method "withReuseIdentifier" so I don't have to declare an identifier in the nib. I've been trying to create a property for collectionView, but it changes nothing.
So I really don't see what I am missing ...
.h :
#import <UIKit/UIKit.h>
#interface MainViewController : UIViewController <UITableViewDelegate,
UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource>
#end
.m :
#import "MainViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController {
NSArray *recipes;
NSArray *images; }
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self; }
- (void)viewDidLoad {
[super viewDidLoad];
recipes = [NSArray arrayWithObjects:#"Label1", #"Label2", #"Label3", #"Label4", #"Label5", #"Label6", #"Label7", #"Label8", #"Label9", #"Label10", #"Label11", #"Label12", #"Label13", #"Label14", #"Label15", nil];
images = [NSArray arrayWithObjects:#"img1", #"img2", #"img3", #"img4", nil];
// Do any additional setup after loading the view. }
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated. }
#pragma mark TableView methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [recipes count]; }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
return cell;
}
#pragma mark CollectionView methods
- (NSInteger) numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection: (NSInteger)section {
return [images count];
}
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
return cell;
}
#end
Thanks in advance !
Make sure you have set the proper Custom Class(if any) as well as the Cell Identifier for the Collection View Cells in the Interface builder. Check this Screenshot.
This is for the Custom Class and the one below is for Cell Identifier.
The Identifier in your case is "cell". Make sure you have these set. They might be the problem here. Also the Class name of your CollectionViewCell and the one in your cellForItemAtIndexPath should be same.
Hope this helps.
You have To register your collection view cell
[self.dashBoardCollection registerNib:[UINib nibWithNibName:#"DashBoardCell" bundle:nil] forCellWithReuseIdentifier:#"DashBoardCell"];
may be in identity Inspector your are missing to name the Class
Oky u can try with this, just a simple way,
First create a new file in user interface select view and name as NibCell.
Then select the device (choose any one of them).
Delete what ever the content in the view.
Just drag and drop a new CollectionViewCell that's it.. :)
in the collectinview cell u can add image view (or any other objects u want) and set its tag to access.
for example
in viewDidLoad method
- (void)viewDidLoad
{
[super viewDidLoad];
//....other code
//nib register
UINib *cellNib = [UINib nibWithNibName:#"NibCell" bundle:nil];
[_collectionView registerNib:cellNib forCellWithReuseIdentifier:#"Cell"];
}
and in this method
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell"; //u know the identifier
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath]; //get the cell
UILabel *titleLabel = (UILabel *)[cell viewWithTag:100]; //for example i put a label and set it's tag to 100 and i an accessing it
[titleLabel setText:cellData];
return cell;
}
I create 2 UICollectionView classes, each one use 2 different UICollectionViewCell
#interface PhotosCollectionViewController : UICollectionViewController
#interface FullScreenCollectionViewController : UICollectionViewController
#interface PhotoCell : UICollectionViewCell
#interface FullPhotoCell : UICollectionViewCell<UIScrollViewDelegate>
In PhotosCollectionViewController.m, I register PhotoCell class and choose next view controller is FullScreenCollectionViewController when didSelect
//Register Cell
-(id)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)layout{
if (self = [super initWithCollectionViewLayout:layout])
{
[self.collectionView registerClass:[PhotoCell class] forCellWithReuseIdentifier:CELL_ID];
}
return self;
}
//dequence resuse code
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
PhotoCell* cell = (PhotoCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CELL_ID forIndexPath:indexPath];
NSLog(#"reuse CELL");
FICDPhoto *photo = [_photos objectAtIndex:indexPath.row];
cell.imageView.image = [photo sourceImage];
return cell;
}
//Next ViewController transition code
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *vc = [self nextViewControllerAtPoint:CGPointZero];
[self.navigationController pushViewController:vc animated:YES];
}
-(UICollectionViewController*)nextViewControllerAtPoint:(CGPoint)p
{
FullScreenCollectionViewController* nextCollectionViewController = [[FullScreenCollectionViewController alloc] initWithCollectionViewLayout:[[FullScreenFlowLayout alloc] init]];
nextCollectionViewController.useLayoutToLayoutNavigationTransitions = YES;
nextCollectionViewController.title = #"FullScreen";
return nextCollectionViewController;
}
In FullScreenCollectionViewController, I register FullPhotoCell class
[self.collectionView registerClass:[FullPhotoCell class] forCellWithReuseIdentifier:CELL_ID_FULL];
But sequence code never call
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
FullPhotoCell* cell = (FullPhotoCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CELL_ID_FULL forIndexPath:indexPath];
NSLog(#"Reuse FULL Cell");
FICDPhoto *photo = [_photos objectAtIndex:indexPath.row];
cell.imageView.image = [photo sourceImage];
return cell;
}
New layout is apply, viewDidload of FullScreenCollectionViewController also call, but the log message "reuse CELL" tell that fullScreen CollectionView still use old PhotoCell class.
I still don't understand what's problem. Please help me!
The answer is "a bit" late but this might be useful for someone else. When you use useLayoutToLayoutNavigationTransitions the collection view from the view controller initiating the transition is actually reused. This collection view has a different data source and delegate and that's why FullScreenCollectionViewController's cellForRowAtIndexPath is not being called. Take a look at this similar question:
UICollectionView UseLayoutToLayoutNavigationTransitions with different datasources
I'm trying to get a UICollectionView to display inside a modally presented view controller. The app is for iPad iOS 7.
I've created subclass of UIViewController (with a nib) and added it like this:
MyViewController *controller = [[MyViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
navController.modalPresentationStyle = UIModalPresentationFullScreen;
navController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
[self presentViewController:navController animated:YES completion:nil];
This view controller is to be the delegate and dataSource for my UICollectionView so I've added UICollectionViewDataSource and UICollectionViewDelegate to the header.
I've put a UICollectionView into the nib and added an outlet to MyViewController:
#property (strong, nonatomic) IBOutlet MyCollectionView *collectionViewController;
I've added this in viewDidLoad in MyViewController:
self.collectionViewController.dataSource = self;
self.collectionViewController.delegate = self;
I've also added the following to MyViewController:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
NSLog(#"Items in section: %d", itemsArray.count); // returns correct amount
return itemsArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cellForItemAtIndexPath %#", indexPath); // returns as expected
static NSString *identifier = #"MyCell";
[self.collectionViewController registerClass:[MyCollectionCell class] forCellWithReuseIdentifier:identifier];
MyCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *myImageView = (UIImageView *)[cell viewWithTag:100];
myImageView.image = [UIImage imageNamed:[itemsArray objectAtIndex:indexPath.row]];
return cell;
}
I've also set up a subclass of UICollectionViewCell with the identifier set to MyCell and added to it a UIImageView with the tag 100.
Whenever I bring up this view controller I'm getting the navigation bar as expected, but the UICollection view I've added to my nib is nowhere to be seen. All I'm seeing is black where the collection view should be. If I change the background colour of MyCollectionView from default to white, I see white where the collection view ought to be. It seems to be bringing up MyCollectionView, but not showing any cells.
One other point of interest is that if you set the cell's identifier in the nib or storyboard, don't register the nib/class in the collection view controller. Do one or the other, but not both.
If you link your collectionView and its own dataSource and delegate in your xib file, you don't need to set up this on your code.
Next, you need to register your UICollectionViewCell :
- (void)viewDidLoad
{
[super viewDidLoad];
// Register Nib
[self.collectionView registerNib:[UINib nibWithNibName:CollectionViewCell_XIB bundle:[NSBundle mainBundle]] forCellWithReuseIdentifier:CollectionViewCell_ID];
}
CollectionViewCell_XIB is the name of your cell xib
CollectionViewCell_ID is the ID of your cell
And you need to implement cellForItemAtIndexPath like this :
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionViewCell *cell = (CollectionViewCell *)[self.collectionView dequeueReusableCellWithReuseIdentifier:CollectionViewCell_ID forIndexPath:indexPath];
// Configure cell with data
UIImageView *myImageView = (UIImageView *)[cell viewWithTag:100];
myImageView.image = [UIImage imageNamed:[itemsArray objectAtIndex:indexPath.row]];
// Return the cell
return cell;
}
I am configuring a UICollectionViewCell in a subclass, it adds 2 subviews to the contentView property, both are UIImageView and both have the hidden property set to YES. These subviews are "checked" and "unchecked" images that overlay the primary UIImageView in the cell to indicate whether or not the current cell is selected using UICollectionView's "multiple select" feature.
When the cell is tapped, collectionView:didSelectItemAtIndexPath: is called on the delegate, and I'd like to setHidden:NO on the "checked" UIImageView. Calling this on the cell does nothing at all -- the cell is seemingly locked in its originally drawn state.
Is it possible to make changes to a cell outside collectionView:cellForItemAtIndexPath:? I have tried manually adding subviews within collectionView:didSelectItemAtIndexPath:, but it just makes absolutely no change to the UI. I have verified that the delegate method is getting called, it's just not making my cell changes.
- (void) collectionView(UICollectionView *)cv didSelectItemAtIndexPath(NSIndexPath *)indexPath {
ShotCell *cell = [self collectionView:cv cellForItemAtIndexPath:indexPath];
UILabel *testLabel = UILabel.alloc.init;
testLabel.text = #"FooBar";
testLabel.sizeToFit;
[cell.contentView.addSubview testLabel];
}
The way you're trying to do this is incorrect. You need to keep a reference to the selected cell or cells in a property. In this example, I use an array to hold index paths of the selected cells, then check whether the index path passed in to cellForItemAtIndexPath is contained in that array. I unselect the cell if you click on one that's already selected:
#interface ViewController ()
#property (strong,nonatomic) NSArray *theData;
#property (strong,nonatomic) NSMutableArray *paths;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.paths = [NSMutableArray new];
self.theData = #[#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight"];
[self.collectionView registerNib:[UINib nibWithNibName:#"CVCell" bundle:nil] forCellWithReuseIdentifier:#"cvCell"];
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self.collectionView setCollectionViewLayout:flowLayout];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.theData.count;
}
-(CVCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cvCell";
CVCell *cell = (CVCell *) [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor whiteColor];
cell.label.text = self.theData[indexPath.row];
if ([self.paths containsObject:indexPath]) {
[cell.iv setHidden:NO]; // iv is an IBOutlet to an image view in the custom cell
}else{
[cell.iv setHidden:YES];
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if ([self.paths containsObject:indexPath]) {
[self.paths removeObject:indexPath];
}else{
[self.paths addObject:indexPath];
}
[self.collectionView reloadItemsAtIndexPaths:#[indexPath]];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(150, 150);
}
I have a UICollectionViewCell subclass called AlbumCVC that contains a single IBOutlet --- a UIImageView called cellView. I'm setting the value of cellView for each cell inside the following method:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell;
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"albumPhotoCell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor blueColor];
if ([cell isKindOfClass:[AlbumCVC class]]){
AlbumCVC *albumCVC = (AlbumCVC *)cell;
ALAsset *thisImage = [self.albumPhotos objectAtIndex:indexPath.item];
albumCVC.imageView.frame = albumCVC.contentView.frame;
albumCVC.contentView.contentMode = UIViewContentModeScaleAspectFit;
albumCVC.imageView.image = [UIImage imageWithCGImage:[thisImage aspectRatioThumbnail]];
}
}
return cell;
}
where albumPhotos is an NSMutableArray of ALAssets. I'm sure that the property is getting set correctly because I get sensible results when I log the albumCVC.cellImage.image.bounds.size. Cells are also sized properly as the frames are visible when I set the background color. But for some reason, cellImage won't display. Is there another method call I need to make inside collectionView:cellForItemAtIndexPath: in order to get the image to show up?
Update: On the advice of a very smart friend, I tried moving the UIImageView out of the cell, putting it elsewhere in the main view, and everything worked lovely. The problem appears to have something to do with the frame / bounds of the UIImageView. I think there's a method call I need to make so that the cell's subview expands to fit the newly-resized cell following the call to collectionView:layout:sizeForItemAtIndexPath:. The problem now is that UIImageView.image.size is a read-only property, so I can't resize it directly.
Update 2: On another piece of advice I looked at the frame and bounds of the cell's contentView and cellImage and found that they weren't matching up. Added another method call to make them equal, and even changed contentMode to UIViewContentModeScaleAspectFit in order to try and get the cell to render the thumbnail properly. Unfortunately, I'm still getting tiny thumbnails inside huge cells. Any idea why? Updated code above and below.
For the sake of completeness, here's the entire class implementation:
#import "AlbumViewController.h"
#import "AlbumCVC.h"
#import <AssetsLibrary/AssetsLibrary.h>
#interface AlbumViewController ()
#end
#implementation AlbumViewController
#pragma constants
const int IPHONE_WIDTH = 320;
#pragma delegate methods
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section{
// Get the count of photos in album at index albumIndex in the PhotoHandler
NSInteger numCells = [self.group numberOfAssets];
return numCells;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell;
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"albumPhotoCell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor blueColor];
if ([cell isKindOfClass:[AlbumCVC class]]){
AlbumCVC *albumCVC = (AlbumCVC *)cell;
ALAsset *thisImage = [self.albumPhotos objectAtIndex:indexPath.item];
}
albumCVC.imageView.frame = albumCVC.contentView.frame;
albumCVC.contentView.contentMode = UIViewContentModeScaleAspectFit;
albumCVC.imageView.image = [UIImage imageWithCGImage:[thisImage aspectRatioThumbnail]];
}
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
//Copy a pointer to an asset from the album
ALAsset *thisImage = [self.albumPhotos objectAtIndex:indexPath.item]; //Zen - are you sure thisImage represents a valid image?
//Copy that asset's size and create a new size struct
CGSize thisSize = thisImage.defaultRepresentation.dimensions;
CGSize returnSize;
// force all previews to be full width
returnSize.width = IPHONE_WIDTH;
returnSize.height = IPHONE_WIDTH * thisSize.height / thisSize.width;
return returnSize;
}
#pragma lifecycle methods
- (void)viewWillAppear:(BOOL)animated{
[self.albumPhotos removeAllObjects];
//"handler" is a class that manages calls to the ALAssetLibrary. self.albumIndex is an integer that gets set on segue. As far as I can tell, everything in the below method is working fine --- cells are sized properly.
self.group = self.albumDelegate.handler.groups[self.albumIndex];
[self.group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
NSLog(#"Just added an object to albumPhotos.");
[self.albumPhotos addObject:result];
NSLog(#"The item in albumPhotos is class %#", [self.albumPhotos[0] class]);
}
}];
}
#pragma instantiation
- (ALAssetsGroup *)group{
if (!_group) {
_group = [[ALAssetsGroup alloc]init];
}
return _group;
}
- (NSMutableArray *)albumPhotos{
if (!_albumPhotos) {
_albumPhotos = [[NSMutableArray alloc]init];
}
return _albumPhotos;
}
#end
Update 3: I can't be certain what the problem was initially, but I know that it now works with the following cellForItem implementation:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell;
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"albumPhotoCell" forIndexPath:indexPath];
if ([cell isKindOfClass:[AlbumCVC class]]) {
AlbumCVC *albumCVC = (AlbumCVC *)cell;
albumCVC.albumImageView.image = [[UIImage alloc] initWithCGImage:[self.albumAssets[[self reverseAlbumIndexForIndex:indexPath.item]] thumbnail]];
}
cell.alpha = [self alphaForSelected:self.selectedItems[#(indexPath.item)]];
return cell;
}
There's no screwing around with frames or bounds anywhere, everything just works. Maybe it's the difference between [[UIImage alloc]initWithCGImage] and [UIImage imageWithCGImage]?
I've had a similar issue and resolved it by setting the UICollectionViewCell frame property to be the same as the UIImageView's frame. I'm not 100% sure that this is your issue, I was building the collection purely in code (no Storyboard)