Trouble with UICollectionViewCell - iphone

I have added another UICollectionViewCell onto my UICollectionView as shown:
Each are connected to a different class the Yellow one is connected to a class called ImageCell and the Blue one is connected to a class called TwitterCell. The Image Cell is supposed to Display an Instagram picture with the UIImageView (Which BTW works fine right now). However I want to add other cells under the TwitterCell which is supposed to show Twitter Tweets of the user with the UILabel. Here is my code right now:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ImageCell *cell = (ImageCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"imageCell"
forIndexPath:indexPath];
NSURL *url = [self imageUrlForEntryAtIndexPath:indexPath];
//NSLog(#"%#", url);
[cell.imageView setImageWithURL:url];
cell.backgroundColor = [UIColor whiteColor];
TwitterCell *tweetCell = (TwitterCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"twitterCell" forIndexPath:indexPath];
return cell;
return tweetCell;
}
Here is the end image of what I want (keep in mind everything is ordered by time posted):

Remember to registerClass:forCellWithReuseIdentifier: in order to get the dequeue to work properly. Also, you can only answer one cell in cellForItemAtIndexPath: check to see if the row is even or odd to decide which kind of cell to dequeue, then initialize the contents only of that cell.

Related

UICollectionView repeats cell

I have a UICollectionView where I display a grid of images downloaded from the Internet. I am using SDWebImage to load the images. The problem I am facing is that, when I scroll through the UICollectionView, sometimes the cells repeat themselves, displaying the same image. But when the cell is scroll out of view and then brought back, it has the right image set.
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSString *CellIdentifier = #"Gallery_Cell";
GalleryCell *cell;
if (cell==nil) {
cell= (GalleryCell *)[self.flowCollection dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
ImageItem *dets = [self.imageList objectAtIndex:indexPath.row];
NSURL *mainImageURL = [NSURL URLWithString:dets.smallImageURL];
cell.image.contentMode = UIViewContentModeScaleAspectFill;
cell.image.clipsToBounds = YES;
[cell.image setImageWithURL:mainImageURL placeholderImage:nil];
}
return cell;
}
Has this happened to anyone else? Would highly appreciate any pointers.
On GalleryCell.m you need to add prepareForReuse method and there nullify the _image variable (assuming that you have a image #property in the cell):
- (void)prepareForReuse {
[super prepareForReuse];
[self setHighlighted:NO];
_image = nil;
}
The following is stated in the "UICollectionView Class Reference":"
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. For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell’s prepareForReuse method instead."
Do you have a prepareForReuse method for your GalleryCell cell class?
you cell class should be a subclass of UICollectionReusableView Class. And check it.
I ended up using “didEndDisplayingCell” method of UICollectionView and ending the current Image download and setting the image to nil. This worked perfectly and there was no more “shuffling” or repetition of images! :)
use this method this vl definitely work 100%.
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;
{
GalleryCell *cell1 = (GalleryCell*)[_resumeCollectionView cellForItemAtIndexPath:indexPath];
cell1= nil;
}
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
NSString *CellIdentifier = #"Gallery_Cell";
GalleryCell *cell;
if (cell==nil) {
cell= (GalleryCell *)[self.flowCollection dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
ImageItem *dets = [self.imageList objectAtIndex:indexPath.row];
NSURL *mainImageURL = [NSURL URLWithString:dets.smallImageURL];
[cell.image setImage:[UIImage new]];
cell.image.contentMode = UIViewContentModeScaleAspectFill;
cell.image.clipsToBounds = YES;
[cell.image setImageWithURL:mainImageURL placeholderImage:nil];
}
return cell;
}
Since the cells are being reused, you must set their contents unconditionally in cellForIndexPath:. Your code as it is sets the images eventually, but it leaves the chance for the images to be temporarily left as old values -- values set before they were reused.
A quick solution is (probably) to provide that setImageWithURL call with a placeholder image. I don't know the SDImage library, but it's a good guess that it immediately sets an imageView's image to the placeholder image if one is provided.
If you don't have or don't want a placeholder image, you can implement prepareForReuse in your UICollectionViewCell subclass and clear the imageView there.

UITableViewCell background color based upon NSFetchedResultsController - strange issues

This is driving me absolutely insane.
I have a UITableView with cells populated via an NSFetchedResultsController that should have their background color set based upon one of the Core Data parameters.
This table view is in the master view of a UISplitViewController and the selected cell needs to remain visibly selected to indicate what is being displayed in the detail view.
Based upon guidance from several other Stack Overflow questions, I have learned that the ideal place to configure the cell is during the willDisplayCell delegate call, like this:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
WorkTask *workTask = (WorkTask*) [self.fetchedResultsController objectAtIndexPath:indexPath];
if ([workTask.strStatus isEqualToString:#"A"]) {
cell.backgroundColor = [self colorWithHexString:#"fffdcf"];
// cell.textLabel.backgroundColor = [self colorWithHexString:#"fffdcf"];
// cell.detailTextLabel.backgroundColor = [self colorWithHexString:#"fffdcf"];
} else if ([workTask.strStatus isEqualToString:#"B"]) {
cell.backgroundColor = [self colorWithHexString:#"cfffd1"];
// cell.textLabel.backgroundColor = [self colorWithHexString:#"cfffd1"];
// cell.detailTextLabel.backgroundColor = [self colorWithHexString:#"cfffd1"];
} else if ([workTask.strStatus isEqualToString:#"C"]) {
cell.backgroundColor = [self colorWithHexString:#"ffcfcf"];
// cell.textLabel.backgroundColor = [self colorWithHexString:#"ffcfcf"];
// cell.detailTextLabel.backgroundColor = [self colorWithHexString:#"ffcfcf"];
} else {
cell.backgroundColor = [self colorWithHexString:#"ffffff"];
// cell.backgroundColor = cell.contentView.backgroundColor;
}
This mostly sort of works. But...
Depending on how I play around with different variants of accomplishing this, I end up with the background color being ignored sometimes (and only sometimes?!?) behind the textLabel and detailTextLabel. Or causing the cell to display incorrectly while selected. Or having the checkmark indicator displayed without a background color. Or having new items added to the core data database showing up in the table, but with the no background color for the cell, but the text labels having a background color.
No matter what I do, I have not found a simple and intuitive way to make things behave overall as expected - particularly when cells are being programmatically selected.
In fact - the cell selection seems like it might be at the root of the problem. The selected cell is usually the one that ends up drawn incorrectly after I change the selection to another, particularly if the color of the cell changed while the cell was selected.
Is there any example out there anywhere of how this is supposed to work?!?!
Thanks!
If I were you I would create a UITableViewCell subclass with your own titleLabel/subtitleLabel UILabels in it and stop using the textLabel/detailTextLabel. In the xib file you can just change the background color of the labels and of the cell. I have never had the sort of problem you are experiencing when I have used custom cells as opposed to the default cells.
Here's an example of how to load a UITableViewCell from an xib:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
if (cell == nil) {
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
}
cell.titleLabel.text = #"whatever";
cell.subtitleLabel.text = #"whatever";
return cell;
}
You could also try setting the background color of the cell's contentView.
cell.contentView.backgroundColor = ...;
If you really can't figure it out, then in a UITableViewCell subclass you could always just put a UIView of your own in the background of the cell and change the background color of that view instead.
willDisplayCell:forRowAtIndexPath: might not get called after insertRowsAtIndexPaths: is called (i.e. when you add items into core data w/ a fetchedresultscontroller). If absolutely necessary maybe you should try setting the background color of the cell and it's contentView in both cellForRowAtIndexPath: and willDisplayCell:forRowAtIndexPath:.
For some bizarre reason the background color of UITableViewCell objects can only be set just before the cell is drawn. In your UITableView's delegate implement this method:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
Set the background color of the cell there and it will draw the way you want.

one image or thumbnail for cells in a table view

i have the following code which will displays result in a UItable view along with an image.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//create a cell
UITableViewCell *cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
// fill it with contnets
NSDictionary *exercise = [exercises objectAtIndex:indexPath.row];
cell.textLabel.text = [exercise valueForKey:#"player"];
UIImage *image = [UIImage imageNamed:#"iphone.gif"];
cell.imageView.image = image;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
// return it
return cell;
}
is there an option where i can display one image for all the cells for ex. 1 image on the left side and then 3 rows by the right side. I am a new bid and still getting my grip on iPhone coding.Please suggest me how we can do this.Thanks.
Yup, a UITableViewCell is pretty much another UIView, so you can add subviews to it and customize it anyway you need. For example, if you need to add an image to all the cells, just add it onto the contentView;
[cell.contentView addSubview:myImageView];
If you have several customizations needed for your cell, and are looking for a highly custom look as opposed to the generic look provided by the standard cells, I'd recommend looking into creating a custom UITableViewCell. The reason is that the standard cells have already laid out UI's with labels, images etc, and anything you add onto it may interfere with the existing UI in ways you do not intend.

iphone Scrolling of tableview with custom cells is too slow

I have this problem when deploying my application on iphone, which wasn't detected on the simulator.
this the code of the cellforrow...
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"beginning cellforRowAtIndexPath for section %d, and cell %d",[indexPath indexAtPosition:0],[indexPath indexAtPosition:1]);
static NSString *MyIdentifier = #"MyIdentifier";
NSString *fieldTitle;
NSString*fieldDescription;
[_stopWatch start];
[PersonalSection GetField:&fieldTitle AndValue:&fieldDescription UsingIndexPath:indexPath AndPersonalInformation:_personalInfo];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"ViewContent" owner:self options:nil];
cell = tvCell;
self.tvCell=nil;
((UILabel*) [cell.contentView viewWithTag:1]).layer.cornerRadius=11;
((UILabel*) [cell.contentView viewWithTag:2]).layer.cornerRadius=11;
}
UILabel*mainLabel=(UILabel*) [cell.contentView viewWithTag:1];
mainLabel.text=fieldTitle;
//mainLabel.textColor = [UIColor colorWithRed:0.745 green:0.116 blue:0.176 alpha:1.0];
UILabel*detailLabel=(UILabel*)[cell.contentView viewWithTag:2];
detailLabel.text=fieldDescription;
[_stopWatch stop];
NSLog(#"---------End cellforRowAtIndexPath");
return cell;
}
the rest is for sections and it's like return 3 or 5 no real bottleneck there.
so i'm wondering what's slowing it so much.
now the data fetching "[PersonalSection GetField:&fieldTitle..." is rather fast, it takes on the iphone maximum 0.1 ms. The problem is somewhere else, i'm guessing there's a way for optimizing this code, and i'm wondering about the custom cell influence it's only a cell with label and textfield linked to this ViewController.
Any ideas.
When you create the cell you're setting the identifier to #"cell" but when you dequeue it you're looking for #"MyIdentifier". It looks like you're recreating the cell every time though this.
Ok, the main performance issue was rounding the corners of the subviews of the contentview.
using QuartzCore:
#import <QuartzCore/QuartzCore.h>
((UILabel*) [cell.contentView viewWithTag:1]).layer.cornerRadius=11;
I removed those and decreased the sizes of the controls to fit inside the cell of a sectioned table. and now they look round but the textfield and label are not.
this has fixed my scrolling performance noticeably.
Thank you all for your help.

What is a good way to animate UIViews inside UITableViewCell after a callback?

We couldn't find a way to animate UIViews inside a UITableCell as an action from a callback.
Suppose in a scenario where you click on a button on the UITableViewCell and it fires off an asynchronous action to download a picture. Suppose further that when the picture is downloaded we want a UIView in the cell to animate the picture to give user a visual feedback that something new is about to be presented.
We couldn't find a way to track down the UIVIew to invoke beginAnimation on because the original cell that the user clicked on might now be used for another row due to the nature of cells being reused when you scroll up and down in the table. In other words we can't keep a pointer to that UITableViewCell. We need to find another way to target the cell and animate it if that row is visible and don't animate if the row is scrolled out of range.
Keep the cell object different from the object being animated so the cell holds a UIView. When the animation callback occurs check to make sure that the UIView still exists and, if it does, animate the changes.
When the cell object gets bumped off the screen and recycled, release the UIView that would have been animated and create a new one. When the animation callback occurs it will have nothing to do because the UIView no longer exists.
A modification of the above is to keep some sort of object in the UIView that your callback can check to see if the animation is still appropriate. This could be some sort of unique identifier for the picture being downloaded. If the identifier changes, no animation is needed. If it matches, do the animation.
EDIT:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:MyIdentifier] autorelease];
} else {
UIView *oldViewToAnimate = [cell.contentView viewWithTag:1];
[oldViewToAnimate removeFromSuperview];
}
UIView *viewToAnimate = [[UIView alloc] initWithFrame:CGRectZero]; //replace with appropriate frame
viewToAnimate.tag = 1;
[cell.contentView addSubview:viewToAnimate];
return cell;
}
When you spawn your download process you pass in [cell.contentView viewWithTag:1]. When the download is done, it will update the appropriate view. If the table cell was reused the view will no longer have a superview and will not update the wrong cell.
There are things you can do to make this more efficient but this is the basic idea. If you have a custom UITableViewCell than this will probably look a bit different.
EDIT 2:
To reuse the viewToAnimate objects to make sure that they get updated if their parent cells were recycled, do something like the following:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:MyIdentifier] autorelease];
} else {
UIView *oldViewToAnimate = [cell.contentView viewWithTag:1];
[oldViewToAnimate removeFromSuperview];
}
UIView *viewToAnimate = [self viewToAnimateForIndexPath:indexPath];
viewToAnimate.tag = 1;
[cell.contentView addSubview:viewToAnimate];
return cell;
}
viewToAnimateForIndexPath will need to:
Check to see if a viewToAnimate has been created for this indexPath
Create a viewToAnimate if there isn't one
Save a reference to the view that can be looked up by indexPath
Return the viewToAnimate so the table cell can use it
I don't know enough about your data structure to do this for you. Once the download process completes it can call this same method to get the view and animate it.