I'm working on a project similar to a video album. In that I'm using UICollectionView to display the thumb images of those videos. The worst part is that I should not use storyboard or xib files. I have tried to do this programatically. I'm currently working on this code:
- (void)viewDidLoad
{
i = 0;
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
[layout setItemSize:CGSizeMake(self.view.frame.size.width/2.5,self.view.frame.size.width/2.5)];
collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
[collectionView setDataSource:self];
[collectionView setDelegate:self];
collectionView.backgroundColor = [UIColor whiteColor];
[collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"MyCell"];
[self.view addSubview:collectionView];
[super viewDidLoad];
}
I have given 1 for return in numberOfSectionsInCollectionView and [myArray count] for return in numberOfItemsInSection.
-(UICollectionViewCell *) collectionView:(UICollectionView *)cV cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [cV dequeueReusableCellWithReuseIdentifier:#"MyCell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor blackColor];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(cell.frame.origin.x , cell.frame.origin.y, cell.frame.size.width, (cell.frame.size.height - cell.frame.size.height/3))];
cell.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(cell.frame.origin.x , cell.frame.origin.y, cell.frame.size.width, (cell.frame.size.height - cell.frame.size.height/3))];
imageView.image = [UIImage imageNamed:[storeData objectAtIndex:i]];
[cell addSubview:imageView];
i++;
return cell;
}
I have rechecked the images in myArray. When the view loads, the collection view shows only the first image. Other 4 cells are empty. What is wrong with my code?
For those who is experiencing this problem, frame is the key. I've encountered this and changed:
cell.imageView.frame = cell.frame;
into
cell.imageView.frame = cell.bounds;
The op is using:
cell.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(cell.frame.origin.x , cell.frame.origin.y, cell.frame.size.width, (cell.frame.size.height - cell.frame.size.height/3))];
That's why this happened.
You shouldn't be using i as a counter. The whole point of the delegate method sending you an indexPath is that it tells you what information to get from your array of source data. So, remove i and use the indexPath.row instead.
You also don't need 2 image views. But you should probably keep your special subview and not use the cells built in image view.
You do not need a counter. As indicated by Wain, use indexPath.row.
Importantly, you should not create new subviews in cellForItemAtIndexPath, but rather use this method to fill them appropriately with content. You could put the image views into your storyboard prototype cells and identify them with tags. Each cell returned from dequeueReusableCellWithReuseIdentifier will already contain the image view.
you should never create ui elements in cellForRowAtIndexPath:. Subclass a collection view cell like so:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"INIT WITH FRAME FOR CELL");
//we create the UIImageView here
imageView = [[UIImageView alloc] init];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.frame = CGRectMake(cell.frame.origin.x , cell.frame.origin.y, cell.frame.size.width, (cell.frame.size.height - cell.frame.size.height/3));
[self.contentView addSubview:imageView]; //the only place we want to do this addSubview: is here!
}
return self;
}
Then add that subclassed cell as a property and alter this code:]
[collectionView registerClass:[customCellClass class] forCellWithReuseIdentifier:#"MyCell"];
The perform these changes:
-(customCellClass *) collectionView:(UICollectionView *)cV cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = (customCellClass *)[cV dequeueReusableCellWithReuseIdentifier:#"MyCell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor blackColor];
imageView.image = [UIImage imageNamed:[storeData objectAtIndex:indexPath.row]];
return cell;
}
A final adjustment would be to move the the [super viewDidLoad] to this:
- (void)viewDidLoad
{
[super viewDidLoad];
//insert the rest of the code here rather than before viewDidLoad
}
Related
I create a cell using this func:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
BOOL isFocusOn = [_userDefault boolForKey:#"mixFocusOn"];
CDCChannelStrip *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
if (isFocusOn == TRUE) {
NSNumber *setChan = [self.focusChannels objectAtIndex:indexPath.section];
NSInteger chanInt = [setChan intValue];
[cell initData:(chanInt)];
[self getParameters:(setChan)];
} else {
NSInteger chanInt = indexPath.section;
NSNumber *chanNum = [NSNumber numberWithInt:chanInt]; // doesnt matter
[cell initData:(chanInt)]; // init
[self getParameters:(chanNum)]; // params
}
[self.mixMonitorView setChannelsStripToType:(cell)];
cell.clipsToBounds = YES;
return cell;
}
The func in question is initData which looks like this:
- (void)initData:(NSInteger)channel {
self.isBus = false;
self.isSendChan = false;
self.recallSafeFade = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"recallSafe"]];
self.recallSafeFade.y = 80;
[self.recallSafeFade setUserInteractionEnabled:YES];
self.recallSafeHead = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"recallSafeHeadamp"]];
[self.recallSafeHead setUserInteractionEnabled:NO];
self.recallSafePan = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"recallSafePan"]];
self.recallSafePan.y = 768 - 141;
[self.recallSafePan setUserInteractionEnabled:YES];
self.channelNumber = channel;
[self setClipsToBounds:NO];
_background = [[UIView alloc] initWithFrame:CGRectMake(0, 80, 122, 547)];
[_background setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"singleFader"]]];
[self addSubview:_background];
[self addChannelName];
[self addInput];
[self addPan];
[self addOn];
[self addMeter];
[self addFader];
}
So the issue im having is, i need to call this func to setup my custom cell to look and function how i need it to. However, every time i call reload on the collectionview due to state changes, it layers the new views over the top of the old views (due to reocurring imageview alloc's)
So how can i apply this func to each cell automatically, yet not reapply it over the top every time i reload the cell?
Image of the issue ina view debugger here:
UICollectionViewCells are reused. So, avoid doing addSubView: (directly or indirectly as in your case: collectionView:cellForRowAtIndexPath: is calling initData: which is calling addSubView:), each time in collectionView:cellForItemAtIndexPath:.
You are using a UICollectionView and UICollectionViewCell only by code.
So there is no XIB/Storyboard were is the cell, and no IBOutlet.
According to the doc of dequeueReusableCellWithReuseIdentifier:forIndexPath:, that's the part you fall into:
If you registered a class for the specified identifier and a new cell
must be created, this method initializes the cell by calling its
initWithFrame: method.
So, in CDCChannelStrip.m, override initWithFrame: and initialize its properties (that are "always" visible/used).
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
_myImageViewProperty = [[UIImageView alloc] initWithFrame:someFrame];
[self addSubview:_myImageViewProperty];
etc.
}
return self;
}
-(void)updateWithChannel:(NSInteger)channel
{
self.channelNumber = channel;
self.channelLabelName = #"Something";
//etc.
}
You can also use prepareForReuse to "reinit" the cell as "virgin" (like it was just after initWithFrame: for instance.
-(void)prepareForReuse
{
[super prepareForReuse];
self.channelLabelName = #"";
self.backgroundColor = [UIColor clearColor];
//etc.
[self.sometimesThatImageIsShown setHidden:YES];
}
Simplest way is to create a separate xib for cell or prototype cell. Design you cell there and in cellForItemAtIndexPath you just manipulate your views and set values to your labels, images and other controllers.
I created a UIView with a UICollectionView, and that UIView is added on a UIViewController. UIViewController using the delegate (photoDidTapped) of that UIView to determine is something was clicked/tapped. First row, is okay, means whole cell area is tappable. Second row, horizontal half of that is clickable. 3rd row, almost 10% horizontal of the cell is clickable and so on.
Code:
- (UICollectionView *)collectionView
{
if (!_collectionView) {
UICollectionViewFlowLayout *flowLayout=[[UICollectionViewFlowLayout alloc] init];
flowLayout.itemSize = CGSizeMake(THUMB_DIMENSION, THUMB_DIMENSION);
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:PHOTO_MARGIN];
_collectionView = [[UICollectionView alloc] initWithFrame:self.frame collectionViewLayout:flowLayout];
_collectionView.frame = CGRectMake(0, 0, SCREEN_WIDTH, self.frame.size.height);
_collectionView.backgroundColor = [UIColor clearColor];
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.allowsSelection = YES;
_collectionView.alwaysBounceVertical = YES;
_collectionView.scrollEnabled = NO;
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"Cell"];
}
return _collectionView;
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
return (_photos.count > 9 ? 9 : _photos.count);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath; {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height)];
imgView.clipsToBounds = YES;
imgView.backgroundColor = RGB(250, 250, 250);
if (_photos.count) {
PhotoDM *photo = [_photos objectAtIndex:indexPath.row];
[imgView setImageWithURL:[NSURL URLWithString:photo.largeLink]
placeholderImage:[UIImage imageNamed:#"placeholder"]];
}
[cell addSubview:imgView];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"tapped: %ld", (long)indexPath.row);
if([delegate respondsToSelector:#selector(photoDidTapped:)])
[delegate performSelector:#selector(photoDidTapped:) withObject:[[NSNumber alloc] initWithInteger:indexPath.row] ];
}
Your code contains 2 problems.
1. If you create a subview, you should use self.bounds.
2. The view can be resized, after UICollectionView has been added. It means that you should configure autoresizingMask or NSLayoutConstrans. (UICollectionView can be out of view's bounds).
To debug this problem, you can create different background colors for the view, the collectionView, the viewController's view.
I have a working sample of UICollectionView, I can move a cell [by indexPath], but I need a specific cell [indexpath.row?] So I move the cell i need, At the moment a cell moves but not the correct one, so
when I move for example
NSIndexPath *index = [indexPaths objectAtIndex:0];
the cell on indexpath.row = 3 moves, how to call the cell by indexpath row?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor lightGrayColor];
CGRect frame = CGRectMake(10, 100, 300, 500);
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
self.collectionView=[[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
[self.collectionView setDataSource:self];
[self.collectionView setDelegate:self];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[self.collectionView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:self.collectionView];
UIButton *animus = [UIButton buttonWithType:UIButtonTypeRoundedRect];
animus.frame = CGRectMake(100, 20, 90, 30);
[animus setTitle:#"Animus" forState:UIControlStateNormal];
[animus addTarget:self action:#selector(animusPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:animus];
}
- (void)animusPressed:(id)sender
{
NSIndexPath *index = [indexPaths objectAtIndex:0]; //en el index!
NSLog(#"moving tha cell :: %d", index.row);
__weak UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:index]; // Avoid retain cycles
CGRect frame = cell.frame;
frame.origin.y = 300;
cell.frame = frame;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor=[UIColor greenColor];
UILabel *title = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, cell.bounds.size.width, 40)];
[cell.contentView addSubview:title];
NSString *titleLbl = [NSString stringWithFormat:#"i = %d", indexPath.row];
title.text = titleLbl;
return cell;
}
So a cell moves, but not the one I want, I need to move a specific cell, by indexpath.row like it is shown on the cellForRow,
How to specify what cell I want to move?
Please note im moving cell on index = 0 , but the indexpath.row = 2 is the one moving
Thanks
Have you tried something like this?
[NSIndexPath indexPathForItem:0 inSection:0]
First of all, it will be more difficult to make any adjustments your UICollectionView without using an array as your data source. Most commonly, you will have one array holding data and in your cellForItemAtIndexPath: method you grab index-specific data from that array via:
MyData *myData = [self.arrayOfMyData objectAtIndex:indexPath.row];
This way you can populate the cell using data related to the current indexPath. Once you do that, just update your data array and then call:
[self.collectionView reloadData];
Then the updates will be represented in the collection view. If you want to animate those changes, use:
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadData];
} completion:nil];
If you want to "move" cells around with the appearance that it changed its original position, you could manage your arrayOfMyData to add a signifier at the index you want empty to set the background of the cell as clear and put the cell to move at the end of your array, then call reloadData.
I'm setting UITableView separator color this way:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.separatorColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.1f];
}
And SOMETIMES on iOS7 my table view separators look different (cells with data and empty cells separators have different alpha I guess), look at attached image:
How can I solve this issue?
If you want to remove all cell's separator which has empty content then use following line of code.
self.tableView.tableFooterView = [[UIView alloc] init];
in viewDidLoad method.
Other wise set self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; and set your custom UIImagefor separator.
EX:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"cell"; //[NSString stringWithFormat:#"S%1dR%1d", indexPath.section, indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
.
. // your other code.
.
UIImageView *imgLine = [[UIImageView alloc] init];
imgLine.tag = 101;
[cell.contentView addSubview: imgLine];
}
.
. // your other code.
.
UIImageView *imgLine = (UIImageView *) [cell.contentView viewWithTag:101];
imgLine.frame = CGRectMake(5, 69, 310, 1);
imgLine.image = [UIImage imageNamed:#"queTblLine.png"];
return cell;
}
And don't forget to set self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
EDITE :
Not sure but might be helpful in your case:
set self.tableView.separatorInset = UIEdgeInsetsZero;
Get From question/answer.
Swap the order of the two statements. Set the color first, then the inset:
self.tableView.separatorColor = [UIColor redColor];
self.tableView.separatorInset = UIEdgeInsetsZero;
You can achieve this visually in storyboard or nib/xib
Put UIImageView at bottom edge of cell and you can set image as per your requirement
STEP 1: Set divider as per image
STEP 2: Set Separator style to none
You can set it programatically:
- (void)viewDidLoad {
[super viewDidLoad];
[[UITableView appearance] setSeparatorColor:[UIColor redColor]];
}
I have a collection cell and want to change the image when touched, and back again, how should I structure it?
After highlighting (works well below), I want it to go back to the old image when touched again. Thank you. At viewWillDissapear I want to know which cells are highlighted.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
UIImage *backGround =[UIImage imageNamed:#"IconHighlight.png"];
UIImageView *av = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, backGround.size.width, backGround.size.height)];
av.backgroundColor = [UIColor clearColor];
av.opaque = NO;
av.image = backGround;
[[collectionView cellForItemAtIndexPath:indexPath] setBackgroundView:av];
[[collectionView cellForItemAtIndexPath:indexPath].backgroundView setTag:1];
}
Create CustomCell - subclass of UICollectionViewCell. Customize init to the following
//CustomCell.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
backgroundImageView.image = [UIImage imageNamed:#"background.png"];
highlightImageView.image = [UIImage imageNamed:#"highlight.png"];
self.backgroundView = backgroundImageView;
_isHighlight = -1;
}
return self;
}
-(void)tapToChangeBackGround{
self.isHighlight = -self.isHighlight;
if (self.isHighlight==1) {
self.backgroundView = highlightImageView;
}
else{
self.backgroundView = backgroundImageView;
}
}
//didSelect
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
CustomCell *cell = (CustomCell *)[collectionView cellForItemAtIndexPath:indexPath];
[cell tapToChangeBackGround];
}