I'm try to add UICollectionView to my custom UICollectionReusableView in runtime, in main form I have a UICollectionView(named mainView) to show first-level menus to user, when user did select cell in mainView it will expand second-level menus in foot section(and call FolderContentView::loadSubMenus method),
following is my wont work code, please help.
.h file
#interface FolderContentView : UICollectionReusableView<UICollectionViewDataSource,UICollectionViewDelegate>
-(void) loadSubMenus:(NSArray<EnabledModule*>*) subModules;
#end
.m file
#import "FolderContentView.h"
#implementation FolderContentView {
UICollectionView *_collectionView;
NSArray* _subMenus;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.itemSize = CGSizeMake(100, 100);
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
_collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:flowLayout];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cell"];
_collectionView.delegate = self;
_collectionView.dataSource = self;
}
[_collectionView reloadData];
return self;
}
-(void) loadSubMenus:(NSArray<EnabledModule*>*) subModules {
if(subModules != nil) {
_subMenus = [subModules copy];
} else {
_subMenus = nil;
}
[_collectionView reloadData];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _subMenus!=nil?_subMenus.count:0;
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"cell"
forIndexPath:indexPath];
if(!cell){
cell=[[UICollectionViewCell alloc]init];
}
EnabledModule *model = _subMenus[indexPath.row];
cell.hidden = !model.enabled;
UILabel *lblText = [[UILabel alloc] init];
lblText.text = model.moduleName;
[cell.contentView addSubview:lblText];
return cell;
}
#end
try reloadData from main thread, it's wont work.
set bp to numberOfItemsInSection numberOfItemsInSection, not hint.
try to set delegate and dataSource to my mainView's controller, not work too.
What's 'reloadData not work' actually means? Did the delegate methods run correctly or nothing happen?
1.Maybe you should check the codes 'add UICollectionView to my custom UICollectionReusableView in runtime'.
2.Maybe you forgot to do:
[self addSubview:_collectionView];
3.The 'cell' will not be nil in this code:
UICollectionViewCell *cell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"cell"
forIndexPath:indexPath];
if(!cell){
cell=[[UICollectionViewCell alloc]init];
}
4.Don't do this:
[cell.contentView addSubview:lblText];
Use subClass of UICollectionViewCell instead.
I'm very new to UICollectionViews. I'm making a universal app. What I want is that on the iPad there are 2 cells on each row. But on the iPhone there is only one cell on each row. I was able to get the iPad version set correctly. But I can't get my head around the iPhone settings.
Here are the settings so far. And has you can see on my storyboard it looks oké. But when I run it I see that there are still two cells on each row.
Can someone help me with this ?
Kind regards
- (void)viewDidLoad
{
[super viewDidLoad];
[self.collectionView registerClass:[CVCell class] forCellWithReuseIdentifier:#"cvCell"];
// Configure layout
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(100, 100)];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[self.collectionView setCollectionViewLayout:flowLayout];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
deviceModel = (NSString*)[UIDevice currentDevice].model;
NSLog(#"device is = %#",deviceModel);
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
if ([deviceModel isEqualToString:#"iPhone Simulator"]) {
return 1;
}else
{
return 2;
}
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 3;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// Setup cell identifier
static NSString *cellIdentifier = #"cvCell";
/* Uncomment this block to use subclass-based cells */
CVCell *cell = (CVCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if (indexPath.section == 0) {
cell.backgroundColor = [UIColor redColor];
}
if (indexPath.section == 1) {
cell.backgroundColor = [UIColor blueColor];
}
cell.titleLabel.text = [NSString stringWithFormat:#"item %ld",(long)indexPath.section];
// Return the cell
return cell;
}
Check this code which will give you one item in iPhone cell and two item in iPad..
Hi i have created one UIViewController in that i added one UICollectionView with CustomCell. Then, i created a uicollectionviewcell class for cell. Finally set the delegate and datasource in storybord itself. But it showing empty page. like in below image
CODE:
- (void)viewDidLoad
{
[super viewDidLoad];
self.collectionview = _collectionview;
self.collectionview.dataSource = self;
self.collectionview.delegate = self;
speakersImages=[[NSMutableArray alloc]init];
// Do any additional setup after loading the view, typically from a nib.
imageLabels=[[NSMutableArray alloc]initWithObjects:#"RB Forum 2013",#"Agenda",#"Speakers",#"Contact",#"Map",#"Websites",#"#Responsible Biz",#"Partners",#"Sustainability",nil];
imagePaths=[[NSMutableArray alloc]initWithObjects:[UIImage imageNamed:#"RBform.png"],[UIImage imageNamed:#"agenda.png"],[UIImage imageNamed:#"speakers.png"],[UIImage imageNamed:#"contact.png"],[UIImage imageNamed:#"map.png"],[UIImage imageNamed:#"website.png"],[UIImage imageNamed:#"twitter.png"],[UIImage imageNamed:#"partners.png"],[UIImage imageNamed:#"sustainability.png"], nil];
[self.collectionview registerClass:[NewCell class] forCellWithReuseIdentifier:#"Cell"];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
return [imageLabels count];
}
//THE BELOW DELEGATE METHOD DOES NOT GET CALLED!
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
// we're going to use a custom UICollectionViewCell, which will hold an image and its label
//
static NSString *CellIdentifier = #"Cell";
NewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
// make the cell's title the actual NSIndexPath value
cell.labels.text = [NSString stringWithFormat:#"%#",[imageLabels objectAtIndex:indexPath.row]];
// load the image for this cell
NSLog(#"%#",imageLabels);
//NSString *imageToLoad = [NSString stringWithFormat:#"%d.JPG", indexPath.row];
// cell.image.image = [UIImage imageNamed:imageToLoad];
cell.images.image = [imagePaths objectAtIndex:indexPath.row];
return cell;
}
Solution is remove
[self.collectionview registerClass:[NewCell class] forCellWithReuseIdentifier:#"Cell"];
in storybord we dont need this
Code is ok but order is different.Assigning datasource and delegates before array is populated makes the number of cell returned to 0 ,since array is not yet populated
Try like this
speakersImages=[[NSMutableArray alloc]init];
// Do any additional setup after loading the view, typically from a nib.
imageLabels=[[NSMutableArray alloc]initWithObjects:#"RB Forum 2013",#"Agenda",#"Speakers",#"Contact",#"Map",#"Websites",#"#Responsible Biz",#"Partners",#"Sustainability",nil];
imagePaths=[[NSMutableArray alloc]initWithObjects:[UIImage imageNamed:#"RBform.png"],[UIImage imageNamed:#"agenda.png"],[UIImage imageNamed:#"speakers.png"],[UIImage imageNamed:#"contact.png"],[UIImage imageNamed:#"map.png"],[UIImage imageNamed:#"website.png"],[UIImage imageNamed:#"twitter.png"],[UIImage imageNamed:#"partners.png"],[UIImage imageNamed:#"sustainability.png"], nil];
[self.collectionview registerClass:[NewCell class] forCellWithReuseIdentifier:#"Cell"];
[self.collectionview reloadData];
this line is not needed
self.collectionview = _collectionview;
self.collectionview.dataSource = self;
self.collectionview.delegate = self;
also make sure your collection view is connected via outlet
make sure the <UICollectioniewDatasource,UICollectionviewDelegate> is there in the class header
In Your storyboard give your imageview a tag e.g. 1, and your label another tag e.g. 2
Give your cell reuse identifier that you have already gave eg. "Cell"
Now in your storyboard right click on collection view and create datasource and delegate to your viewController or file owner. alternatively you can use <UICollectionViewDataSource,UICollectionViewDelegate> in your viewController and set the delegate and datasource to self.
Now in your viewDidLoad
-(void) viewDidLoad {
self.collectionview.dataSource = self;
self.collectionview.delegate = self;
speakersImages=[[NSMutableArray alloc]init];
// Do any additional setup after loading the view, typically from a nib.
imageLabels=[[NSMutableArray alloc]initWithObjects:#"RB Forum 2013",#"Agenda",#"Speakers",#"Contact",#"Map",#"Websites",#"#Responsible Biz",#"Partners",#"Sustainability",nil];
imagePaths=[[NSMutableArray alloc]initWithObjects:[UIImage imageNamed:#"RBform.png"],[UIImage imageNamed:#"agenda.png"],[UIImage imageNamed:#"speakers.png"],[UIImage imageNamed:#"contact.png"],[UIImage imageNamed:#"map.png"],[UIImage imageNamed:#"website.png"],[UIImage imageNamed:#"twitter.png"],[UIImage imageNamed:#"partners.png"],[UIImage imageNamed:#"sustainability.png"], nil];
}
number of items in collection view
(NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
return [imageLabels count];
}
create cell and reuse
(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionView *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell"];
// make the cell's title the actual NSIndexPath value
UIlabel *label = (UIlabel *) [cell viewWithTag:2]
label.text = [NSString stringWithFormat:#"%#",[imageLabels objectAtIndex:indexPath.row]];
// load the image for this cell
UIImageView *imageView = (UIImageView *) [cell viewWithTag:1]
imageView.image = [imagePaths objectAtIndex:indexPath.row];
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.collectionView registerNib:[UINib nibWithNibName:#"NewCell" bundle:nil] forCellWithReuseIdentifier:#"CELL"];
self.collectionview.dataSource = self;
self.collectionview.delegate = self;
self.imageLabels = [NSArray arrayWithObjects:#"RB Forum 2013",#"Agenda",#"Speakers",#"Contact",#"Map",#"Websites",#"#Responsible Biz",#"Partners",#"Sustainability",nil];
self.imagePaths=[NSArray arrayWithObjects::#"RBform.png",#"agenda.png",#"speakers.png",#"contact.png",#"map.png",#"website.png",#"twitter.png",#"partners.png",#"sustainability.png", nil];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
return [self.imageLabels count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CELL" forIndexPath:indexPath];
cell.labels.text = [self.imageLabels objectAtIndex:indexPath.row]];
cell.images.image = [UIImage imageNamed:[self.imagePaths objectAtIndex:index path.row]];
return cell;
}
I have a problem I can't solve for some hours now...
I have multiple UICollectionViews with different numbers of cells and different cellsizes. The CollectionViews are created programmatically and the delegates and datasources are set.
The collectionviews are created like this:
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
[layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
collectionViewOne = [[UICollectionView alloc] initWithFrame:CGRectMake(0,0,320,150) collectionViewLayout:layout];
[collectionViewOne setTag:99];
[collectionViewOne setDataSource:self];
[collectionViewOne setDelegate:self];
[collectionViewOne registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cell"];
[collectionViewOne setBackgroundColor:bgColor];
[collectionViewOne setShowsHorizontalScrollIndicator:NO];
[collectionViewOne setBounces:YES];
[collectionViewOne setAlwaysBounceHorizontal:YES];
[collectionViewOne setScrollEnabled:YES];
[collectionViewOne setRestorationIdentifier:#"collectionViewOne"];
[scrollView addSubview:collectionViewOne];
My functions are like these:
- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section {
NSLog(#"restorationIdentifier: %#", collectionView.restorationIdentifier);
if (collectionView == collectionViewOne)
{
return 3;
}
else if (collectionView == collectionViewTwo)
{
return 4;
}
else
{
return 1;
}
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"restorationIdentifier: %#", collectionView.restorationIdentifier);
if (collectionView == collectionViewOne)
{
return CGSizeMake(100, 150);
}
else if (collectionView == collectionViewTwo)
{
return CGSizeMake(200, 150);
}
else
{
return CGSizeMake(200, 150);
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"restorationIdentifier: %#", collectionView.restorationIdentifier);
if (collectionView == collectionViewOne)
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.image = [UIImage imageNamed:#"image.png"];
[cell addSubview:imageView];
}
else if (collectionView == collectionViewTwo)
{
//create cell of type 2
}
return cell;
}
In my log I get the following output (for example):
restorationIdentifier in numberOfItemsInSection: (null)
restorationIdentifier in sizeForItemAtIndexPath : (null)
restorationIdentifier cellForItemAtIndexPath: collectionViewOne
collectionViewOne is the restorationIdentifier on collectionViewOne. So why is it not recognized in the first two methods?
Ofcourse the result is that I get crashes because the cellsizes and number of cells in the different CollectionViews are not properly set. The error:
*** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /SourceCache/UIKit_Sim/UIKit-2903.2/UICollectionViewData.m:341
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView recieved layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0x8c31c10> {length = 2, path = 0 - 2}'
How can I fix this?
This is solved. The problem was the fact that I used the same UICollectionViewFlowLayout for both UICollectionViews. This caused the exception.
In the end I used different classes for the two controllers, this solved the problem of selecting the right CollectionView in the delegate's methods.
Thanks all for your input!
You could use the tag property of the collection view to identify which collection view is which. Like this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"restorationIdentifier: %#", collectionView.restorationIdentifier);
if (collectionView.tag == collectionViewOneTag)
{
//create cell of type 1
}
else if (collectionView.tag == collectionViewTwoTag)
{
//create cell of type 2
}
return cell;
}
Where collectionViewOneTag and collectionViewTwoTag are integers that can be defined in code or in the xib file.
Hope that helps.
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);
}