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;
}
Related
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.
Actually I have taken a two collection view controllers. In first collection view controller iIhave passed array of images.on the click of the one image ,i want to display another collection view controller.how to do this...please suggest me .
- (void)viewDidLoad
{
[super viewDidLoad];
recipeImages = [NSArray arrayWithObjects:#"angry_birds_cake.jpg", #"creme_brelee.jpg", #"egg_benedict.jpg", #"full_breakfast.jpg", #"green_tea.jpg", #"ham_and_cheese_panini.jpg", #"ham_and_egg_sandwich.jpg", #"hamburger.jpg", #"instant_noodle_with_egg.jpg", nil];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return recipeImages.count;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:[recipeImages objectAtIndex:indexPath.row]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"photo-frame.png"]];
for (UIView *view in cell.subviews) {
if ([view isKindOfClass:[UILabel class]]) {
[view removeFromSuperview];
}
}
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, 150, 170)];//Set the frame as per your requirement.
label.font=[UIFont systemFontOfSize:10];
[label setText:[NSString stringWithFormat:#"%#",[categoryArray objectAtIndex:indexPath.row]]];
[cell addSubview:label];
//NSLog(#"hiiiii");
return cell;
}
If you would like to show the second collection view in another view controller, you would just push the view controller with the collection view in it in didSelectItemAtIndexPath:.
If you would like to show the second collection view in the same view controller, you could create two collection views and set the second to hidden. Then in didSelectItemAtIndexPath:, you could set the second collection view to show and the first to hidden. This would be done through collectionView.hidden = YES or NO.
For displaying another UICollectionViewController on the click of an item in another UICollectionViewController, you have to use the didSelectItemAtIndexPath function. You can do it like this :
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
int itemNo = (int)[indexPath row]+1;
CollectionViewCell *selectedCell = (CollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
switch (itemNo) {
case 1:
{
//perform segue to another UICollectionViewController.
}
case 2:
{
//perform segue to another UICollectionViewController.
}
.
.
.
}
}
Here itemNo is the item(Cell), that is clicked from the Collection View. If you have to redirect to the same UICollectionViewController for all item clicks, the you won't be needing the switch and you can do it directly with Segue.
Hope this helps.
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 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'm writing an app with a UICollectionViewController.
I've created the layout in with a storyboard. Subclassed UICollectionViewController and linked the two in IB. I've also given it a Storyboard ID of "ImageCollectionViewController".
I've also subclassed a UICollectionViewCell and linked it with my prototype cell in IB as well as given it the Identifier "ImageCell".
This is my constructor I call to create my custom UICollectionViewController:
-(id)initWithGroup:(CatalogGroup*)group
{
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle: nil];
self = [storyboard instantiateViewControllerWithIdentifier:#"ImageCollectionViewController"];
if (self) {
[self setGroup:group];
// Register the type of cell for collection view
[self.collectionView registerClass:[ImageCell class] forCellWithReuseIdentifier:#"ImageCell"];
}
return self;
}
Now this all seems to work. All the delegate methods are being called in the collectionViewController. For example:
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self fetchedResultsController] sections][section];
return [sectionInfo numberOfObjects];
}
My cells are being initialized and passed back with:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* identifier = #"ImageCell";
ImageCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
ImageItem* item = [[self fetchedResultsController].fetchedObjects objectAtIndex:indexPath.row];
if(cell) {
// Set up label
cell.label.lineBreakMode = NSLineBreakByCharWrapping;
cell.label.numberOfLines = 0;
[cell.label setText:item.labelText];
NSData* imageData = item.imageData;
UIImage* image = [UIImage imageWithData:imageData];
UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
[cell setImageView:imageView];
}
return cell;
}
Yet when I push the view controller onto the stack. None of the cells are there?
Is there anything wrong with my stack?
The background color I set for the CollectionView in IB is showing so I believe it's being created properly. The cells are initialized. The delegate methods are being called so I don't know where to start debugging.
I'm not sure what your custom cell looks like, but generally, it should be a subview of cell.contentView:
[cell.contentView addSubview:myContentView];
So, instead of [cell setImageView:imageView], you might try
[cell.contentView addSubview:imageView];
You can test your collection view by setting the background color of the cell in collection view cell custom class. This will ensure your cells are configured properly. Now come to your problem(It is only a guess since you are not shown your cell class). If you are setting image view of the cell in nib, you should _registerNib instead of _registerClass_for the cell. Other wise you should add the image view as the sub view to the cell.contentView