I'm trying to add a custom view to the header section of my UICollectionView. I have the xib file interface builder but I don't use storyboard. I have checked the Section Header in Interface Builder but doesn't appear any UICollectionReusableView, what can I do?
The easiest solution is to do is programatically (and keep your HeaderReusableView in another XIB file):
[self.collectionView registerNib:[UINib nibWithNibName:#"ItemHeaderView" bundle:nil]
forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:kItemSectionHeaderViewID];
Then:
- (CGSize) collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
referenceSizeForHeaderInSection:(NSInteger)section {
return CGSizeMake(60.0f, 30.0f);// width is ignored
}
And of course:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind
atIndexPath:(NSIndexPath *)indexPath {
NSString *productId = nil; ///
ItemHeaderView *view = nil;
view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
withReuseIdentifier:kItemSectionHeaderViewID
forIndexPath:indexPath];
view.titleLabel.text = productId;
view.backgroundColor = [UIColor yellowColor];
return view;
}
Related
I have a UICollectionView that is displaying many UICollectionViewCells that I have subclassed as CardCell. I pass the variable "type" to the CardCell in - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath I want the CardCell class to be able to load a different Nib file depending on what type is passed in. The different types need to have different layouts.
The problem is I cannot figure out where to change this in my CardCell.m. I tried using - (void)prepareForReuse but that does not call unless the user is scrolling.
You should register each nib file you need in viewDidLoad, something like this (substituting the correct names for the nib file and the identifier):
[self.collectionView registerNib:[UINib nibWithNibName:#"RDCell" bundle:nil] forCellWithReuseIdentifier:#"FirstType"];
Then, in itemForRowAtIndexPath, test for type and return the correct type of cell:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (type = #"firstType") {
FirstCell *cell = (FirstCell *) [collectionView dequeueReusableCellWithReuseIdentifier:#"FirstType" forIndexPath:indexPath];
return cell;
}else{
SecondCell *cell = (SecondCell *) [collectionView dequeueReusableCellWithReuseIdentifier:#"SecondType" forIndexPath:indexPath];
cell.whatever .....
return cell;
}
}
I am using PSUICollectionView in my app in which gallery with images thumbs are loaded with horizontal scrollview.
Now I need two more collectionviews(galleries) to show two other types of pictures.
Can anyone please help me out ??
Thanks in advance.
I got it working by adding some lines of code in delegate and data source methods as follows -
// setting tag
[self.imageCollection setTag:1];
[self.finishImageCollection setTag:2];
[self.prevImageCollection setTag:3];
pragma mark -
pragma mark Collection View Data Source
- (NSString *)formatIndexPath:(NSIndexPath *)indexPath {
return [NSString stringWithFormat:#"%ld", (long)indexPath.row+1];
}
// Populating cells
- (PSUICollectionViewCell *)collectionView:(PSUICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ImageGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if(collectionView.tag==1){
cell.statusImage.image= [UIImage imageNamed:#"checked.png"];
cell.imageThumb.image = [self.startImages objectAtIndex:indexPath.row];
}
if(collectionView.tag==2){
cell.statusImage.image= [UIImage imageNamed:#"checked.png"];
cell.imageThumb.image = [self.finishImages objectAtIndex:indexPath.row];
}
if(collectionView.tag==3){
cell.statusImage.image= [UIImage imageNamed:#"checked.png"];
cell.imageThumb.image = [self.prevImages objectAtIndex:indexPath.row];
}
return cell;
}
- (NSInteger)collectionView:(PSUICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
int imgCount;
if(collectionView.tag==1)
{
[self loadStartPictures];
imgCount=self.startImages.count;
}
if(collectionView.tag==2)
{
[self loadFinishPictures];
imgCount=[self.finishImages count];
}
if(collectionView.tag==3)
{
[self loadSupervisorPictures];
imgCount=[self.prevImages count];
}
return imgCount;
}
#pragma mark -
#pragma mark Collection View Delegate
- (void)collectionView:(PSTCollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
self.allImages=[[NSMutableArray alloc]init];
UIImage *imageAtIndexPath;
NSLog(#"Delegate cell %# : SELECTED", [self formatIndexPath:indexPath]);
ImageGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.label.backgroundColor=[UIColor underPageBackgroundColor];
if(collectionView.tag==1){
imageAtIndexPath=[self.startImages objectAtIndex:indexPath.row];
}
if(collectionView.tag==2){
imageAtIndexPath=[self.finishImages objectAtIndex:indexPath.row];
}
if(collectionView.tag==3){
imageAtIndexPath=[self.supervisorImages objectAtIndex:indexPath.row];
}
}
Simple. Just add the views like you would any other UIView's. Set their frames correctly, they should work.
Keep an outlet to each of them, then you can check to see which one of them is calling a delegate / data source method.
Anyway to hack into UICollectionView to call willDisplayCell delegate method, when displaying cell?
I need this for lazy loading, and I'm doing it nice with UITableView, but officially UICollectionView doesn't have that kind of delegate method.
So, any ideas? Thanks!
FYI, This has been added to the API for iOS 8:
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);
I'm doing something like this in cellForItemAtIndexPath: (for lazy loading images):
If my model has the image, just set the cell's image to it
If the model does not have an image, kick of an async load
When the load completes, the check if the original indexPath is still visible
If so, call cellForItemAtIndexPath for the original indexPath. Since the image now exists in the model, the cell's image will now be set.
Looks like this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
PhotoCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"PhotoCell" forIndexPath:indexPath];
id imageItem = [self.photoSet objectAtIndex:indexPath.row];
ImageManager * listingImage = (ImageManager *)imageItem;
if(listingImage.image){
cell.image = listingImage.image;
} else {
[listingImage loadImage:^(UIImage *image) {
[collectionView reloadItemsAtIndexPaths:#[indexPath]];
dispatch_async(dispatch_get_main_queue(), ^{
if ([[collectionView indexPathsForVisibleItems] containsObject:indexPath]) {
[(PhotoCell *)[collectionView cellForItemAtIndexPath:indexPath] setImage:image];
}
});
}];
}
}
return cell;
}
You can make use of SDWebImage https://github.com/rs/SDWebImage for lazy loading. You can use this effectively with UICollectionView.
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
/*---------
----Other CollectiveView stuffs------
-----------------*/
if([[NSFileManager defaultManager] fileExistsAtPath:YOUR_FILE_PATH isDirectory:NO])
{
imagView.image=[UIImage imageWithContentsOfFile:YOUR_FILE_PATH];
}
else
{
UIActivityIndicatorView *act=[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[imagView addSubview:act];
act.center=CGPointMake(imagView.frame.size.width/2, imagView.frame.size.height/2);
[act startAnimating];
__weak typeof(UIImageView) *weakImgView = imagView;
[imagView setImageWithURL:[NSURL URLWithString:REMOTE_FILE_PATH] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType){
for(UIView *dd in weakImgView.subviews)
{
if([dd isKindOfClass:[UIActivityIndicatorView class]])
{
UIActivityIndicatorView *act=(UIActivityIndicatorView *)dd;
[act stopAnimating];
[act removeFromSuperview];
}
}
NSString *extension=[YOUR_FILE_PATH pathExtension];
[self saveImage:image withFileName:YOUR_FILE_PATH ofType:extension];
}];
}
}
I have a similar situation when I need to know the index path of cell which is going to be displayed. Ended with - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath. Assumably, one is "didEndDisplaying", another is "willDisplayed".
Is this possible? I just want a small table in my current view... here is what I'm trying to do:
.h file
#import <UIKit/UIKit.h>
#interface IngredientsRootView : UIViewController <UITableViewDelegate, UITableViewDataSource> {
UITableView *ingredientsTable;
}
#property (nonatomic, retain) UITableView *ingredientsTable;
#end
.m file I have delegate and data source methods and this code:
ingredientsTable = [[UITableView alloc] initWithFrame:CGRectMake(10, 10, 300, 300) style:UITableViewStylePlain];
[ingredientsTable setDelegate:self];
[ingredientsTable setDataSource:self];
[self.view addSubview:ingredientsTable];
The app doesn't crash, but it doesn't show a table. At the moment I have just set everything definitely as per:
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 10;
}
// the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
cell.textLabel.text = #"Hello";
return cell;
}
What am I doing wrong? Thanks
Try calling -reloadData on the table view after adding it as a subview.
Also, how is the view set up? Is it created in a XIB or via -loadView?
Did you remembered to add the [window addSubview:[IngredientsRootView view]]; to your app delegate ?
And by the way, instead of inheriting from UIViewController you can just inherit from UITableViewController and you'll get all that functionality for you with no added code.
For performance sake it is usual to reuse UITableView' cells.
Is there a way to do the same thing with TableView header' views?
I am talking about the ones that are returned with delegate's method:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
I tried to do the following which doesn't seem to be working as expected:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *CellIdentifier = #"Header";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
if (cell == nil) {
cell = [self getHeaderContentView: CellIdentifier];
}
return cell;
}
Is there a way to reuse header' views?
The reason Apple built in the ability to reuse tableview cells is because while the tableview may have many rows, only a handful are displayed on screen. Instead of allocating memory for each cell, applications can reuse already existing cells and reconfigure them as necessary.
First off, header views are just UIViews, and while UITableViewCell is a subclass of UIView, they are not intended to be placed as the view of a section header.
Further, since you generally will have far fewer section headers than total rows, there's little reason to build a reusability mechanism and in fact Apple has not implemented one for generic UIViews.
Note that if you are just setting a label to the header, you can use -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section instead.
For something more custom, such as a label with red text (or a button, image, etc), you can do something like this:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0,0, 320, 44)] autorelease];
UILabel *label = [[[UILabel alloc] initWithFrame:headerView.frame] autorelease];
label.textColor = [UIColor redColor];
label.text = [NSString stringWithFormat:#"Section %i", section];
[headerView addSubview:label];
return headerView;
}
You can implement that by creating UITableViewHeaderFooterView class
it is subclass of UIView
You also need to create an individual XIB as it will not be created automatically with UITableViewHeaderFooterView creation.
Register Nib with tableview
[self.tblCart registerNib:[UINib nibWithNibName:#"CartHeaderView" bundle:nil] forHeaderFooterViewReuseIdentifier:#"CartHeader"];
Now You can Access that in viewForHeaderInSection
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CartHeaderView *sectionHeader=[tableView dequeueReusableHeaderFooterViewWithIdentifier:#"CartHeader"];
return sectionHeader;
}
Note :
To set background color you will need to create a subview with same frame as section header and set color for that view.
you can follow
Changing the background color on a UITableViewHeaderFooterView loaded from a xib says to use contentView.backgroundColor instead
A simple yet effective solution:
#interface SectionedTableViewController ()
#property (nonatomic, strong) UINib* sectionHeaderNib;
#property (nonatomic, strong) NSMutableArray* sectionHeaders;
#end
#implementation SectionedTableViewController
#synthesize sectionHeaderNib = sectionHeaderNib_;
#synthesize sectionHeaders = sectionHeaders_;
- (void) viewDidUnload
{
self.sectionHeaders = nil;
[super viewDidUnload];
}
- (NSMutableArray*) sectionHeaders
{
if (!sectionHeaders_)
self.sectionHeaders = [NSMutableArray array];
return sectionHeaders_;
}
- (UINib*) sectionHeaderNib
{
if (!sectionHeaderNib_)
self.sectionHeaderNib = [UINib nibWithNibName: NSStringFromClass(YourHeaderView.class) bundle: nil];
return sectionHeaderNib_;
}
- (YourHeaderView*) dequeueHeader
{
return [self.sectionHeaders firstObjectPassingTest: ^(id obj) { return (BOOL) ([obj superview] == nil); }];
}
- (NSString*) sectionHeaderTitleForSection: (NSInteger) section
{
return nil;
}
- (UIView*) tableView: (UITableView*) tableView viewForHeaderInSection: (NSInteger) section
{
YourHeaderView* headerView = [self dequeueHeader];
if (!headerView)
{
headerView = [YourHeaderView instanceFromNib: self.sectionHeaderNib];
[self.sectionHeaders addObject: headerView];
}
return headerView;
}
#end