Images from array not being displayed in UICollectionView - ios

I am trying to display an array of images in a UICollectionView. The cells are being displayed, but the images are not.
Here is the cell being built:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell =
[collectionView dequeueReusableCellWithReuseIdentifier:identifier
forIndexPath:indexPath];
UIImageView * catagoryImage = [[UIImageView alloc] initWithFrame:CGRectMake((cell.frame.origin.x),(cell.frame.origin.y), cell.frame.size.width, cell.frame.size.height)];
[catagoryImage setImage:[UIImage imageNamed:[Apps objectAtIndex:indexPath.row]]];
[cell.layer setCornerRadius:4];
[cell addSubview:catagoryImage];
cell.clipsToBounds = NO;
return cell;
}
And here is the array being declared:
Apps = [NSArray arrayWithObjects:#"Appstore.jpg", #"2.gif", #"3.gif", #"4.gif", #"5.gif", #"7.gif", #"8.gif",#"9.gif", nil];
Any ideas?

You have a few options, but I'll name 2:
This. It's a bit hacky. but it works
Subclass UICollectionViewCell

There are two problems with the code. The first, bigger one, is the idea of creating an image view on every invocation of cellForItemAtIndexPath.
This method is called every time a cell is scrolled into view, and the cells are reused, so your cells will end up with piles and piles of UIImageViews on top of one another as the user scrolls around.
Problem two is the coordinate system of the imageView frame. It should be placed relative to the cell's bounds, not the cell's frame which is in the parent view's (collection view's) coordinate space, so...
// see if this cell has an image view already
UIImageView *categoryImageView = (UIImageView *)[cell viewWithTag:128];
if (!categoryImageView) {
// only create one if we don't have one
CGRect frame = cell.bounds; // this is zero based, relative to the cell
categoryImageView = [[UIImageView alloc] initWithFrame:frame];
categoryImageView.tag = 128; // so we can find it later
[cell addSubview:categoryImageView];
}
// add it conditionally, but configure it unconditionally
categoryImageView.image = [UIImage imageNamed:[Apps objectAtIndex:indexPath.row]]];
Another possible problem with the code is access to the Apps objectAtIndex:. Apparently Apps is an unconventionally named instance of an NSArray. This is only okay if your numberOfItemsInSection datasource method returns Apps.count, and if all elements in the array are names of images in your app bundle.

Sussed it.
The images were outside the keyboard scheme and were in the container package.

Related

Addview image not displaying in table cell

I modified a subview table cell using addsubview to include an additional image on the right (besides the one on the left). I had it working great until I added a search bar at the top. Somehow in doing that, the right image stopped displaying. I tried removing the search bar but have been unable to get the image to display. I have a feeling I made a typo inadvertently or somehow messed up the code.
Here is the method. It works except that the image on the right no longer displays. Would appreciate it if anyone can spot my error or suggest what is going wrong.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *protoCell = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:protoCell];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:protoCell];
}
// IDModel * item;
IDModel * item = nil;
// if no search
//item = [self.tableItems objectAtIndex:indexPath.row];
//following for search
if (tableView == self.searchDisplayController.searchResultsTableView) {
item = [searchResults objectAtIndex:indexPath.row];
} else {
item = [self.tableItems objectAtIndex:indexPath.row];
}
// Configure the cell...
cell.textLabel.text = item.name;
cell.detailTextLabel.text = item.sub;
cell.imageView.image = [UIImage imageNamed:item.pic];
//following crops and rounds image
//add image to right
UIImageView *rightimage;
rightimage = [[UIImageView alloc] initWithFrame:CGRectMake(225.0,0.0,80.0,45.0)];
rightimage.autoresizingMask=UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
// rightimage.image = [UIImage imageNamed:#"pic.jpg"];
[cell.contentView addSubview:rightimage];
rightimage.image = [UIImage imageNamed:item.pic];
return cell;
}
Thanks in advance for any suggestions.
One concern here is that you shouldn't add views to a UITableViewCell from the view controller. Table view cells are reused, so each time this cell gets reused you'll be adding a new view to it without first removing the old one.
Instead, create a UITableViewCell subclass, and add rightimage (which I would rename rightImageView to be consistent with Apple's naming conventions) in the -initWithStyle:reuseIdentifier: method. Then set the rightView's frame in the -layoutSubviews method, or set up the auto layout constraints in the -initWithStyle:reuseIdentifier: right after creating the rightImageView.
Make rightImageView a public property (put it in the .h file) so you can assign the image from the view controller.

iOS managing nested UICollectionViews issue

I've encountered a problem while dealing with nested collection views.
Hierarchy is as follows:
outerCollectionView
UIImageView
Label
innerCollectionView
Label
UIImageView
OuterCollectionView's cells are of the size of the screen (works like swiping through pages)
I've set outerCollectionView's delegate and datasource to be UIViewController and innerCollectionView's delegate and datasource to be the containing cell as follows:
in the ViewController.m
-(FeaturedPageCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
FeaturedPageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"FeaturedCell" forIndexPath:indexPath];
cell.nestedCollectionView.dataSource = cell;
cell.nestedCollectionView.delegate = cell;
[cell.nestedCollectionView setBackgroundColor:[[UIColor whiteColor] colorWithAlphaComponent:.01]];
cell.venueThumbnail.image = [UIImage imageNamed:[self.venueThumbnails objectAtIndex:indexPath.item]];
cell.venueNameLabel.text = [self.venueNames objectAtIndex:indexPath.item];
cell.venueNameLabel.textColor = [UIColor whiteColor];
cell.venueAddressLabel.text = [self.venueAddresses objectAtIndex:indexPath.item];
cell.venueAddressLabel.textColor = [UIColor whiteColor];
[cell setBackgroundColor:[[UIColor grayColor]colorWithAlphaComponent:.1]];
return cell;
}
in implementation file for the cell that contains innerCollectionView
-(FeaturedEventCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
FeaturedEventCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"FeaturedEventCellReuseIdentifier" forIndexPath:indexPath];
cell.eventDateLabel.text = [self.events objectAtIndex:indexPath.item];
cell.eventDateLabel.textColor = [UIColor whiteColor];
cell.eventImage.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#Flyer%ld.png", self.venueNameLabel.text, (long)indexPath.item +1]];
return cell;
}
In this block of code I am using value of containing cell's label to load relevant to that cell data into innerCollectionView.
Pretty straight forward implementation. The result is not what I expected at all. Basically what's happening is that data gets loaded properly into cells of outerCollectionView, but not into the cells of innerCollectionView ( the very first one get loaded correctly, but not the rest of them). Also scrolling through items in innerCollectionView will cause scrolling in the rest of the cells.
I thought that making the containing cell a delegate of innerCollectionView would manage it's own collection view independently from others. I think am not understanding something fundamental about collection views, dequeuing and reusing.
Also is nesting collection views a good way of implementing of my scenario? I tried to look up on implementing nested collection views and couldn't find a whole lot maybe for a reason?
Please, explain what I am doing wrong in my case or direct me towards correct or better solution.

Reuse of UICollectionViewCells during scrolling

I'm having an issue,
I have a simple UICollectionView with a static 200 cells that load images from Flickr.
my CellForItemAtIndexPath looks like this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"FlickrCell" forIndexPath:indexPath];
cell.backgroundColor = [self generateRandomUIColor];
if(![[cell.subviews objectAtIndex:0] isKindOfClass:[PFImageView class]])
{
NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize];
PFImageView *imageView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell];
[cell addSubview:imageView];
}
return cell;
}
PFImageView is a subclass of UIImageView that loads a Flickr photo URL on a background thread and then updates it's own image on the main thread - this works fine.
The logic is really simple - I create a cell if there isn't one dequeueable.
If the cell (which I'm expecting to be dequeued and already have a PFImageView) doesn't have a PFImageView, I alloc and init an imageView for the cell and add it as a subview of the cell.
Thus I expect if the cell has been dequeued it should already have a PFImageView as a subview and as we should not get into the if statement to create a new imageView and kick off a new photo download request
Instead what I see is that the cells at the top and bottom of the UICollectionView that 'go off screen' momentarily - when they come back on screen they are not being reused and seemingly a new cell is created and the picture refreshed.
1) How can I achieve a static image once the cell has been created (i.e. not refreshing when the cell goes slightly off screen.
2) Why are the cells not being reused?
Many thanks for your time.
John
UICollectionView will reuse cells for maximum efficiency. It does not guarantee any particular reuse or population strategies. Anecdotally, it seems to place and remove cells based on integer power of two regions — e.g. on a non-retina iPad it might divide your scroll area up into regions of 1024x1024 and then populate and depopulate each of those regions as they transition into and out of the visible area. However you should not predicate any expectations on its exact behaviour.
In addition, your use of collection view cells is incorrect. See the documentation. A cell explicitly has at least two subviews — backgroundView and contentView. So if you add a subview it will be at index 2 at the absolute least and, in reality, the index will be undefined. In any case you should add subviews to contentView, not to the cell itself.
The most normal way of doing what you're doing would be to create a custom UICollectionView subclass that inherently has a PFImageView within it.
I see several potential issues:
You are looking specifically at index 0 of the cell for the child class that you are adding. The UICollectionViewCell may have other views as children, so you can't just assume that the only (or first) child is the one you added.
I don't see that you are calling registerClass:forCellWithReuseIdentifier: or registerNib:forCellWithReuseIdentifier:, one of which is required for proper use of dequeue (https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewCell_class/Reference/Reference.html).
You are only setting the URL of the PFImageView in the case that you have to construct the PFImageView. The idea with dequeuing reusable views is that you will only construct a small subset of the views needed, and the UITableView will recycle them as they move offscreen. You need to reset the value for the indexPath that is being requested, even when you don't construct the new content.
If your case is as simple as you describe, you can probably get away with adding your PFImageView to the contentView property of your dequeued UICollectionView.
In your controller:
// solve problem 2
[self.collectionView registerClass:[UICollectionViewCell class] forReuseIdentifer:#"FlickrCell"];
In collectionView:cellForItemAtIndexPath
UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"FlickrCell" forIndexPath:indexPath];
cell.backgroundColor = [self generateRandomUIColor];
// solve problem 1 by looking in the contentView for your subview (and looping instead of assuming at 0)
PFImageView *pfImageView = nil;
for (UIView *subview in cell.contentView.subviews)
{
if ([subview isKindOfClass:[PFImageView class]])
{
pfImageView = (PFImageView *)subview;
break;
}
}
NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize];
if (pfImageView == nil)
{
// No PFImageView, create one
// note the use of contentView!
pfImageView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.contentView.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell.contentView];
[cell.contentView addSubview:pfImageView];
}
else
{
// Already have recycled view.
// need to reset the url for the pfImageView. (Problem 3)
// not sure what PFImageView looks like so this is an e.g. I'd probably remove the
// URL loading from the ctr above and instead have a function that loads the
// image. Then, you could do this outside of the if, regardless of whether you had
// to alloc the child view or not.
[pfImageView loadImageWithUrl:staticPhotoURL];
// if you really only have 200 static images, you might consider caching all of them
}
return cell;
For less simple cases (e.g. where I want to visually lay out the cell, or where I have multiple children in the content), I typically customize my UICollectionViewCell's using Interface Builder.
Create a subclass of UICollectionViewCell in the project (In your case, call it PFImageCell).
Add an IBOutlet property to that subclass for the view I want to change in initialization (In your case, a UIImageView).
#property (nonatomic, assign) IBOutlet UIImageView *image;
In Interface Builder, create a prototype cell for the UITableView.
In the properties sheet for that prototype cell, identify the UICollectionViewCell subclass as the class.
Give the prototype cell an identifier (the reuse identifier) in the property sheet.
Add the view child in interface builder to the prototype cell (here, a UIImageView).
Use IB to map the IBOutlet property to the added UIImageView
Then, on dequeue in cellForRowAtIndexPath, cast the dequeued result to the subclass (PFImageCell) and set the value of the IBOutlet property instance. Here, you'd load the proper image for your UIImageView.
I am not sure if the cell is being re-used or not. It may be being reused but the subview may not be there. My suggestion would be to create a PFImageViewCollectionViewCell Class (sub class of UICollectionViewCell) and register it as the CollectionView Cell and try. That's how I do and would do if I need a subview inside a cell.
Try adding a tag on this particular UIImageView
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static int photoViewTag = 54353532;
UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"FlickrCell" forIndexPath:indexPath];
cell.backgroundColor = [self generateRandomUIColor];
PFImageView *photoView = [cell.contentView viewWithTag:photoViewTag];
// Create a view
//
if (!photoView) {
photoView = [[PFImageView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.height, cell.frame.size.width) andImageURL:staticPhotoURL andOwningCell:cell];
imageView.tag = photoViewTag;
[cell.contentView addSubview:imageView];
}
// Update the current view
//
else {
NSURL *staticPhotoURL = [self.context photoSourceURLFromDictionary:[self.photos objectAtIndex:indexPath.row] size:OFFlickrSmallSize];
photoView.imageURL = staticPhotoURL;
}
return cell;
}
I would really recommend to create your own UICollectionViewCell subclass though.
EDIT: Also, note that I used the contentView property instead of adding it directly to the cell.

set selected tag for collectionView

I made a UICollectionView with some cells in it and it displayed correct, now I want to set a selected tag for one or more cells, in custom cell, I can use two ways to implement it:
way 1: set selectedBackgoundView
self.selectedBackgroundView = backgroundView;
way 2: add a UIImageView as selected tag
[_coverImageView addSubview:_selectImageView];
//coverImageView is image for cell,
//selectImageView is a tag imageView for selected.
then the problem comes up:
For example I selected the first cell, When I scroll the UICollectionView, way 1 still displayed the first cell selected, but with way 2, the _selectImageView would be added to the other cell.
I know it is caused by Reuse Cell,but have no idea for deal with it.
Rather than adding your selected tag after you've created the cell, you should add it at the point of creation.
You don't say how you're creating your custom collection view cells, but it sounds as if you might not be using your own subclass, and are simply adding what you need to a plain UICollectionViewCell. You will find it much easier to create your own subclass, and set it up with an image view exposed that can be enabled/disabled as required. You can create custom cells either entirely in code, or in conjunction with a XIB - whichever you prefer.
Recently I am working on a similar project. Although It's long ago, but I hope to help someone who need it.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:collectionCellID forIndexPath:indexPath];
if (cell == nil) {
cell = [[MyCollectionViewCell alloc]init];
}
//Change Selected State
if([[collectionView indexPathsForSelectedItems] indexOfObject:indexPath] != NSNotFound){
UIView *bgView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 250, 250)];
bgView.backgroundColor = kLightBlueColor;
[cell setSelectedBackgroundView:bgView];
cell.selected = YES;
}
cell.title.text = #"Hello World";
return cell;
}

Dynamically display an image in a UITableViewCell

I have a UITableViewCell, it is scroll-disabled and with fixed sections and rows.
There are two sections, with 1 row in section 0, and several rows in section 1.
The tableView is for users to make some choices.
So, the first section (with only one row) is going to display the result of users' choices,
and no doubt the second section (with several rows) is for choosing.
Now I want to put an image in the cell of the only row of the first section,
and this image will change according to users' choose.
It is very easy to judge which png image should be displaying, but I have trouble update it.
I tried use the cell's imageView, and manually alloc a UIImageView or UIView there to display those images.
But all of them won't work, I mean they just keep what they are displaying at the beginning and never changes it, even if I set the view's background or its image to a new png.
I tried some method like
[myImage setNeedsDisplay] for the manually alloced view,
or
[thatCell setNeedsDiaplsy] & [self.tableView reloadData] for the imageView of that cell,
but in vain.
So I wonder how can I achieve this function that dynamically display an image in a UITableViewCell in different situations?
Thanks a lot!
_____update line_____
I'm sorry that I didn't provide my code.
and here they are.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *inOutTableCellIdentifier = #"DealTableViewIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:inOutTableCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:inOutTableCellIdentifier] autorelease];
cell.textLabel.font = [UIFont fontWithName:cMyFont size:[UIFont systemFontSize]];
cell.detailTextLabel.font = [UIFont fontWithName:cMyFont size:[UIFont smallSystemFontSize]];
// I tried using both imageView and UIView here, but won't work
if (indexPath.section == 0) {
//cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"moneyCell.png"]];
cell.backgroundColor = [UIColor cyanColor];
// cell.imageView.image = [UIImage imageNamed:#"dealDone2.png"];
self.undoneIcon = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)] autorelease];
//self.undoneIcon.image = [UIImage imageNamed:#"dealUndone2.png"];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"dealUndone2.png"]];
[cell.contentView addSubview:self.undoneIcon];
.... // deal with other rows in section 1
return cell;
}
// and this is the method that update the image, the images are named "dealDoneX.png",
// where X is an integer from 0 to 4.
- (void)checkUndoneDegree { // here I also tried 2 ways corresponding to UIView or a cell's imageView, but neither works well.
int i = 0;
if (self._date)
i++;
if (self.moneyTextField.text)
i++;
if (self._incomingAccount)
i++;
if (self._expensingAccount)
i++;
if (_dealType != kTransferView)
if (self._type)
i++;
NSLog(#"undone degree: %d",i);
NSString *imageName = [#"dealUndone" stringByAppendingFormat:#"%d",i];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:imageName]];
[self.undoneIcon setNeedsDisplay];
// NSUInteger p[2] = {0,0};
// UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathWithIndexes:p length:2]];
// [cell setNeedsDisplay];
}
and everytime I update the table's data, like changing some text of some cell,
I would call [self checkUndoneDegree] and then call [self.tableView reloadData],
But the picture is never updated, at least from the screen.
I even tried to put the codes that decide which png to set in the
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
method, but it can only make the view displaying the first png, without any updating.
Thanks for helping!
Make your undoneDegree (represented by i variable in your code) an ivar of your view controller, so it is accessible in all of it's methods, also in the UITableView delegate and data source methods.
Forget about setNeedsDisplay method. Since you are using UITableView to display your content, you need to play by its rules. This means you should use reloadSections:withRowAnimation: method.
Check again if you need self.undoneIcon. I'm pretty sure that imageView property should do. That is if you really want to display an image in the cell. If you want to create some kind of progress bar by manipulating cell's background, then use backgroundView property, or just place UIProgressView in your cell. This is what I think your want to do, after looking at your code.

Resources