Use more than one UICollectionViews in a ViewController - ios

I tried to use two UICollectionViews in a single ViewController. Application crashed with the following error
reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier ItemCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard
This is my implementation how looks like
HomeViewController.h
UICollectionView *colloctionViewItems;
UICollectionView *colloctionViewCats;
#property (nonatomic, retain) IBOutlet UICollectionView *colloctionViewItems;
#property (nonatomic, retain) IBOutlet UICollectionView *colloctionViewCats;
HomeViewController.m
#synthesize colloctionViewCats, colloctionViewItems;
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// read ItemList plist
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSDictionary *dicCats = [appDelegate readPlistIntoDictWithFile:#"CatagoryList.plist"];
// arrCategories
arrCats = [[NSArray alloc]initWithArray:[dicCats objectForKey:#"Catagories"]];
[self.colloctionViewItems registerClass:[ItemCell class] forCellWithReuseIdentifier:#"ItemCell"];
[self.colloctionViewCats registerClass:[CatCell class] forCellWithReuseIdentifier:#"CatCell"];
[colloctionViewItems reloadData];
[colloctionViewCats reloadData];
}
#pragma mark for Collection View Delegate methods
- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section
{
if(view == colloctionViewItems){
return [arrCats count];
}
if(view == colloctionViewCats){
return [arrCats count];
}
return 0;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
return CGSizeZero;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ItemCell *iCell = [cv dequeueReusableCellWithReuseIdentifier:#"ItemCell" forIndexPath:indexPath];
CatCell *cCell = [cv dequeueReusableCellWithReuseIdentifier:#"CatCell" forIndexPath:indexPath];
if(cv == colloctionViewItems){
iCell.lblBtnTitle.text = [arrCats objectAtIndex:indexPath.row];
return iCell;
}
if(cv == colloctionViewCats){
cCell.lblBtnTitle.text = [arrCats objectAtIndex:indexPath.row];
return cCell;
}
return iCell;
}
If I removed one UICollectionView, all works fine. Any ideas please.
FYI: I have created Custom Collection Cell class as "ItemCell" and "CatCell" correctly (without using nibs)

It's because you dequeue both cell at the same time, even if the collection view hasn't contain the right cell. You should first check which collection view is asking about cell and after that dequeue the right one:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
if(cv == colloctionViewItems){
// Item collection view contain item cell so you can safety dequeue it.
// if you try to dequeue CatCell it will fail this is why you had this issue before
ItemCell *iCell = [cv dequeueReusableCellWithReuseIdentifier:#"ItemCell" forIndexPath:indexPath];
iCell.lblBtnTitle.text = [arrCats objectAtIndex:indexPath.row];
return iCell;
}
if(cv == colloctionViewCats){
CatCell *cCell = [cv dequeueReusableCellWithReuseIdentifier:#"CatCell" forIndexPath:indexPath];
cCell.lblBtnTitle.text = [arrCats objectAtIndex:indexPath.row];
return cCell;
}
return nil;
}

Related

How to call delegate methods of CollectionView inside TableView in Objective C?

TableViewCell Class
- (void)awakeFromNib {
//Registering CollectionViewCell
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [productsData count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"collectionCell" forIndexPath:indexPath];
NSDictionary *cellData = [productsData objectAtIndex:[indexPath row]];
cell.imageView.image = [UIImage imageNamed:[cellData objectForKey:#"image"]];
return cell;
}
How to call delegate methods of collection view?
In your TableViewCell.h file add .
#property (nonatomic, assign) UICollectionView *yourCollectionView;
In your TableViewCell.m file add .
#synthesize yourCollectionView;
in your init method alloc collection view & set delegate & datasource .
yourCollectionView = [UICollectionView alloc] initWithFrame://( your frame) ];
yourCollectionView.dataSource = self;
yourCollectionView.delegate = self;
// set other properties as per tour needs .
[self.contentView addSubview:yourCollectionView];
Add check your delegate methods wotrking fine . hope it helps you .

Exception when creating collectionViewCell

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;
}

How to access IBOutlets of a UICollectionViewCell from a UIViewController?

I implemented a UICollectionView inside a UIViewController. The header cell is created in a separate.xib file and it is implemented in a separate .h and .m files with the IBOutlets and IBActions. The rest of the cells are implemented in the same UIVIewController (The reason is because I added this Parallax effect).
I would like to modify the info of the IBoutlets (labels and buttons) that are in the header cell (CSCellUser.h) from the viewcontroller (RankingViewController) which containts the collectionview, how can I do this?
Cell: CSCellUser.h
#interface CSCellUser : UICollectionViewCell
#property IBOutlet UILabel *myScoreValueLabel;
#property IBOutlet UILabel *myRankingValueLabel;
-(IBAction) sendButtonTouchHandler:(id) sender;
#end
UIViewController: RankingViewController.h
#interface RankingViewController : CommonViewController <UICollectionViewDelegate, UICollectionViewDataSource> {
}
#property IBOutlet UICollectionView *collectionView1;
#end
UIViewController:RankingViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
//Parallax Effect, UICollectionView
// Locate the layout
CSStickyHeaderFlowLayout *layout = (id)self.collectionView1.collectionViewLayout;
if ([layout isKindOfClass:[CSStickyHeaderFlowLayout class]]) {
layout.parallaxHeaderReferenceSize = CGSizeMake(320, 220);
layout.parallaxHeaderAlwaysOnTop = YES;
}
// Locate the nib and register it to your collection view
UINib *headerNib = [UINib nibWithNibName:#"CSHeaderRanking" bundle:nil];
[self.collectionView1 registerNib:headerNib
forSupplementaryViewOfKind:CSStickyHeaderParallaxHeader
withReuseIdentifier:#"TopViewCell"];
//get the position of the user and the ranking (this should update the IBOutlets in the CSCellUser.h)
[self getUserRanking];
//get the ranking of users (this updates each cell of the ranking in cellForItemAtIndexPath)
[self getRanking];
}
- (NSInteger) numberOfSectionsInCollectionView:
(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger) collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
return [ranking count];
}
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: #"UsersCell" forIndexPath:indexPath];
if ([ranking count] > 0)
{
UserRanking *user = [ranking objectAtIndex:indexPath.row]; //Fill the cell
UILabel *usernameLabel = (UILabel *)[cell viewWithTag:101];
usernameLabel.text = user.username;
UILabel *scoreLabel = (UILabel *)[cell viewWithTag:102];
scoreLabel.text = [NSString stringWithFormat:#"%d", user.score];
UILabel *gamesLabel = (UILabel *)[cell viewWithTag:103];
gamesLabel.text =[NSString stringWithFormat:#"%d", user.tries];
}
return cell;
}
//Header
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *reusableview = nil;
if ([kind isEqualToString:CSStickyHeaderParallaxHeader]) {
reusableview = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:#"TopViewCell" forIndexPath:indexPath];
}
return reusableview;
}
EDIT:
This is how I am trying to change one of the labels from cellForRowAtIndexPath but it does not change.
CSHeaderRanking *topcell = [collectionView dequeueReusableCellWithReuseIdentifier: #"TopCell" forIndexPath:indexPath];
topcell.myScoreValueLabel.text = #"32";
If TopCell is mapped in its xib, all you have to do is refer to its properties. If you want to customize, you'll need to over-ride the CCSticky... and refer to it in your xib and this class.

collectionviewCell not change when transition to other collectionviewController

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

Change attributes of a UICollectionViewCell in didSelectItemAtIndexPath

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);
}

Resources