AQGridView: How to Init cell - objective-c

I'm implementing the AQGridView based on examples who comes with it. But I'm working with xibs, and in the example, the code is:
if ( plainCell == nil )
{
plainCell = [[[ImageDemoGridViewCell alloc] initWithFrame: CGRectMake(0.0, 0.0, 200.0, 150.0)
reuseIdentifier: PlainCellIdentifier] autorelease];
plainCell.selectionGlowColor = [UIColor blueColor];
}
plainCell.image = [UIImage imageNamed: [_imageNames objectAtIndex: index]];
cell = plainCell;
}
My code looks like this:
- (AQGridViewCell *) gridView: (AQGridView *)inGridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * FilledCellIdentifier = #"FilledCellIdentifier";
AQGridViewCell * cell = nil;
MagazineCell * filledCell = (MagazineCell *)[gridView dequeueReusableCellWithIdentifier: FilledCellIdentifier];
if ( filledCell == nil ) {
}
filledCell.edicaoLabel.text = [[data objectAtIndex:index] name];
cell = filledCell;
return ( cell );
}
The example InitWith CGRect, but how init the cell when I work with xibs?

Generally speaking, one doesn't init a view when loading it from XIB. You actually would do the same thing inside your if (filledCell == nil) that you would do in a UITableView (although with a AQGridViewCell instead of a UITableViewCell). So if "GridCell.xib" has your AQGridViewController as File Owner and tempCell is bound to the laid-out GridCell in IB and you've set the identifer to filledCellIdentifer, you can just do:
[[NSBundle mainBundle] loadNibNamed:#"GridCell" owner:self options:nil];
filledCell = [self.tempCell autorelease];

Related

xCode: TableView cant see/acces all cells

When I press a button called "AllOK" I want the object.selectedIndex to be 0. This works perfectly with the cells that is visible. But it won't acces the cells which isn't viewable on the app / screen. If you scroll down and get vision of them, it will check them, but i want it to do it, without having to scroll down.
Do anyone know how to get the tableview to know that it "also" got the cells that it cannot see?
My code for my tableview and for the button:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FancyCell"];
cell = nil;
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"FancyCell"];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
// add the segmentedControl when you create a new cell
UIImage *correctImageGreen = [[UIImage imageNamed:#"green.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIImage *correctImageGul = [[UIImage imageNamed:#"gul.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIImage *correctImageRed = [[UIImage imageNamed:#"red.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIImage *correctImageGray = [[UIImage imageNamed:#"gray.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
NSArray *itemArray = [NSArray arrayWithObjects: correctImageGreen, correctImageGul, correctImageRed, correctImageGray, nil];
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:itemArray];
segmentedControl.frame = CGRectMake(310, 7, 150, 30);
[cell.contentView addSubview:segmentedControl];
// add an action so we can change our model if the view changes
[segmentedControl addTarget:self action:#selector(didChangeSegmentedControl:) forControlEvents:UIControlEventValueChanged];
// use a tag so we can retrieve the segmentedControl later
segmentedControl.tag = 42;
}
// either if the cell could be dequeued or you created a new cell,
// segmentedControl will contain a valid instance
UISegmentedControl *segmentedControl = (UISegmentedControl *)[cell.contentView viewWithTag:42];
UIImage *comment = [UIImage imageNamed:#"Checkmark-hvid"];
UIImage *imageRef = [UIImage imageNamed:#"Checkmark-hvid"];
UIImageView *commentView = [[UIImageView alloc] initWithImage: comment];
UIImageView *imageRefView = [[UIImageView alloc] initWithImage: imageRef];
commentView.frame = CGRectMake(625, 5, 30, 30);
imageRefView.frame = CGRectMake(515, 5, 30, 30);
commentView.tag = 98;
imageRefView.tag = 99;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
boolean_t didGetStates = [defaults boolForKey:#"didGetStates"];
MBFancyObject *object = _objects[indexPath.row];
if (didGetStates) {
// State
NSDictionary *dic = [tableData objectAtIndex:indexPath.row];
if (object.beingEdited == -1) {
int selectedState = [[dic valueForKey:#"State"] intValue];
object.selectedIndex = selectedState;
}
// Comment & ImageRef
int comment = [[dic valueForKey:#"Comment"] intValue];
int imageRef = [[dic valueForKey:#"Foto"] intValue];
if (comment == 0) {
[cell.contentView addSubview:commentView];
}
else {
[[cell.contentView viewWithTag:98]removeFromSuperview];
}
if (imageRef == 0) {
[cell.contentView addSubview:imageRefView];
}
else {
[[cell.contentView viewWithTag:99]removeFromSuperview];
}
}
cell.textLabel.text = object.title;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
segmentedControl.selectedSegmentIndex = object.selectedIndex;
object.currentIndexRow = indexPath.row;
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor whiteColor];
return cell;
}
- (IBAction)allOK:(id)sender {
for (MBFancyObject *object in _objects) {
object.selectedIndex = 0;
object.beingEdited = 0;
}
[[self tableView] reloadData];
}
So I hadn't noticed this at my first comment, but your problem here is obvious. You are not properly dequeueing cells from the cell pool, and therefore when you scroll, you are creating a brand new cell EVERY time, which causes them not to have your selected index the way you want. I am pretty sure that your solution lies right here:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FancyCell"];
cell = nil;
if (!cell) {
You are dequeuing a cell properly, but then immediately setting it to nil, meaning it will ALWAYS go through the !cell check and will ALWAYS create a new cell. Try removing this line and working with the dequeued cell.
Edit:
Since that didn't do it, this is what else I would try:
Create a call to the method willDisplayCell:forRowAtIndexPath:, and in this call check a boolean flag that you set to see if the segmented control at that index should be at index 0 or not. So basically whenever a cell is about to be shown, you check if it's segmented control should be set to index 0, and if it should, set it's index to 0.

UITableViewCell with alternate background color in customized cells

I'd like the background to of my UITableViewCells to have a different color every two cells displayed, but when I scroll down and back, they all get the same color. How can I get this effect knowing that my cells have different contentView size (according to their content) ?
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 20.0f
#define NAME_CELL_HEIGHT 20.0f
#import "CartCell.h"
#implementation CartCell
#synthesize nameLabel = _nameLabel;
#synthesize ingredientsLabel = _ingredientsLabel;
#synthesize myStore;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
myStore = [Store sharedStore];
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.nameLabel = nil;
self.ingredientsLabel = nil;
// SET "NAME" CELL
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.nameLabel setLineBreakMode:UILineBreakModeWordWrap];
[self.nameLabel setMinimumFontSize:FONT_SIZE];
[self.nameLabel setNumberOfLines:1];
[self.nameLabel setTag:1];
self.nameLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:18];
[self.nameLabel sizeToFit];
self.nameLabel.backgroundColor = [UIColor clearColor];
[[self contentView] addSubview:self.nameLabel];
// SET "INGREDIENTS" CELL
self.ingredientsLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self.ingredientsLabel setLineBreakMode:UILineBreakModeWordWrap];
[self.ingredientsLabel setMinimumFontSize:FONT_SIZE];
[self.ingredientsLabel setNumberOfLines:0];
[self.ingredientsLabel setFont:[UIFont systemFontOfSize:FONT_SIZE]];
[self.ingredientsLabel setTag:2];
self.ingredientsLabel.backgroundColor = [UIColor clearColor];
[[self contentView] addSubview:self.ingredientsLabel];
if (myStore.cellBackgroundShouldBeLight == YES) {
NSLog(#"clear [in] ? %#", myStore.cellBackgroundShouldBeLight ? #"Yes" : #"No");
self.contentView.backgroundColor = [[UIColor alloc]initWithRed:87.0/255.0 green:168.0/255.0 blue:229.0/255.0 alpha:1];
myStore.cellBackgroundShouldBeLight = NO;
} else {
NSLog(#"clear [in] ? %#", myStore.cellBackgroundShouldBeLight ? #"Yes" : #"No");
self.contentView.backgroundColor = [[UIColor alloc]initWithRed:187.0/255.0 green:268.0/255.0 blue:229.0/255.0 alpha:1];
myStore.cellBackgroundShouldBeLight = YES;
}
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
UPDATE:
I'm know trying to set it in cellForRowAtIndexPath as it was suggested, but I get the same result: scrolling down worked fine the first time, but then scrolling up again messed up the cells background color.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CartCell";
CartCell *cell = (CartCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Recipes *info = [_fetchedResultsController objectAtIndexPath:indexPath];
if (cell == nil)
{
cell = [[CartCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// if (!cell.nameLabel) {
// cell.nameLabel = (UILabel*)[cell viewWithTag:1];
// // cell.nameLabel = (UILabel*)[cell viewWithTag:1];
// }
// if (!cell.ingredientsLabel)
// cell.ingredientsLabel = (UILabel*)[cell viewWithTag:2];
CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
CGSize size = [info.ingredients sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
[cell.nameLabel setFrame:CGRectMake(10, 10, CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), NAME_CELL_HEIGHT)];
[cell.ingredientsLabel setFrame:CGRectMake(CELL_CONTENT_MARGIN, CELL_CONTENT_MARGIN + NAME_CELL_HEIGHT, CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), MAX(size.height, 44.0f))];
// SETTING TEXT CONTENT
cell.nameLabel.text = info.name;
cell.ingredientsLabel.text = info.ingredients;
// SETTING BACKGROUND COLOR
// UIView *lab = [[UIView alloc] initWithFrame:cell.frame];
// [lab setBackgroundColor:[UIColor blueColor]];
if (myStore.cellBackgroundShouldBeLight == YES) {
NSLog(#"clear? %#", myStore.cellBackgroundShouldBeLight ? #"Yes" : #"No");
cell.contentView.backgroundColor = [[UIColor alloc]initWithRed:87.0/255.0 green:84.0/255.0 blue:229.0/255.0 alpha:1];
// cell.backgroundView = lab;
// ingredientsLabel.backgroundColor = [UIColor clearColor];
// nameLabel.backgroundColor = [[UIColor alloc]initWithRed:87.0/255.0 green:168.0/255.0 blue:229.0/255.0 alpha:1];
// [cell setBackgroundColor: [[UIColor alloc]initWithRed:87.0/255.0 green:168.0/255.0 blue:229.0/255.0 alpha:1]];
// [cell setBackgroundColor:[UIColor colorWithRed:.8 green:.8 blue:1 alpha:1]];
myStore.cellBackgroundShouldBeLight = NO;
} else {
// cell.contentView.tag = 2;
NSLog(#"clear? %#", myStore.cellBackgroundShouldBeLight ? #"Yes" : #"No");
cell.contentView.backgroundColor = [[UIColor alloc]initWithRed:187.0/255.0 green:184.0/255.0 blue:229.0/255.0 alpha:1];
myStore.cellBackgroundShouldBeLight = YES;
}
return cell;
}
It is very simple, the indexPath tells you everything you need to know. If the indexPath.row is even then use one color. If the indexPath.row is odd use a different color.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
…
// SETTING BACKGROUND COLOR
// UIView *lab = [[UIView alloc] initWithFrame:cell.frame];
// [lab setBackgroundColor:[UIColor blueColor]];
if (indexPath.row % 2) {
cell.contentView.backgroundColor = [[[UIColor alloc]initWithRed:87.0/255.0 green:84.0/255.0 blue:229.0/255.0 alpha:1] autorelease];
} else {
cell.contentView.backgroundColor = [[[UIColor alloc]initWithRed:187.0/255.0 green:184.0/255.0 blue:229.0/255.0 alpha:1] autorelease];
}
…
return cell;
}
Your method is having problems because blindly assuming cells will be asked for in alternating pairs is a bad assumption. The tableView could ask for cells in any order is chooses. In your example, I believe cells could be asked for as follows. First, 0, 1,…, 9 are asked for. Next, you scroll down and 10, 11, and 12 are fetched. At this point, 0, 1, and 2 have gone off the screen. You scroll back up and 2 is asked for, but oh no, your model is on an odd number alternation, so you get the wrong color.
Use the -willDisplayCell method.
- (void)tableView: (UITableView *)tableView willDisplayCell: (UITableViewCell *)cell forRowAtIndexPath: (NSIndexPath *)indexPath {
if (indexPath.row %2) { //change the "%2" depending on how many cells you want alternating.
UIColor *altCellColor = [UIColor colorWithRed:255/255.0 green:237/255.0 blue:227/255.0 alpha:1.0]; //this can be changed, at the moment it sets the background color to red.
cell.backgroundColor = altCellColor;
}
else if (indexPath.row %2) {
UIColor *altCellColor2 = [UIColor colorWithRed:1 green:1 blue:1 alpha:1.0]; //this can be changed, at the moment it sets the background color to white.
cell.backgroundColor = altCellColor2;
}
}
The appropriate place to change your cell's background color would be the "cellForRowAtIndexPath:" method, where the cells data gets filled out and returned to the table view.
One way to do this would be: When the data goes into the cell, change the background color depending on what row you're on.
Put the color on the cellForRowAtIndexPath: don't set on custom cell.
Take a look what I use to customize my table
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
#if USE_CUSTOM_DRAWING
const NSInteger TOP_LABEL_TAG = 1001;
const NSInteger BOTTOM_LABEL_TAG = 1002;
UILabel *topLabel;
UILabel *bottomLabel;
#endif
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
//
// Create the cell.
//
cell =
[[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
autorelease];
#if USE_CUSTOM_DRAWING
UIImage *indicatorImage = [UIImage imageNamed:#"indicator.png"];
cell.accessoryView =
[[[UIImageView alloc]
initWithImage:indicatorImage]
autorelease];
const CGFloat LABEL_HEIGHT = 20;
UIImage *image = [UIImage imageNamed:#"imageA.png"];
//
// Create the label for the top row of text
//
topLabel =
[[[UILabel alloc]
initWithFrame:
CGRectMake(
image.size.width + 2.0 * cell.indentationWidth,
0.5 * (aTableView.rowHeight - 2 * LABEL_HEIGHT),
aTableView.bounds.size.width -
image.size.width - 4.0 * cell.indentationWidth
- indicatorImage.size.width,
LABEL_HEIGHT)]
autorelease];
[cell.contentView addSubview:topLabel];
//
// Configure the properties for the text that are the same on every row
//
topLabel.tag = TOP_LABEL_TAG;
topLabel.backgroundColor = [UIColor clearColor];
topLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
topLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
topLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
//
// Create the label for the top row of text
//
bottomLabel =
[[[UILabel alloc]
initWithFrame:
CGRectMake(
image.size.width + 2.0 * cell.indentationWidth,
0.5 * (aTableView.rowHeight - 2 * LABEL_HEIGHT) + LABEL_HEIGHT,
aTableView.bounds.size.width -
image.size.width - 4.0 * cell.indentationWidth
- indicatorImage.size.width,
LABEL_HEIGHT)]
autorelease];
[cell.contentView addSubview:bottomLabel];
//
// Configure the properties for the text that are the same on every row
//
bottomLabel.tag = BOTTOM_LABEL_TAG;
bottomLabel.backgroundColor = [UIColor clearColor];
bottomLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
bottomLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
bottomLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize] - 2];
//
// Create a background image view.
//
cell.backgroundView =
[[[UIImageView alloc] init] autorelease];
cell.selectedBackgroundView =
[[[UIImageView alloc] init] autorelease];
#endif
}
#if USE_CUSTOM_DRAWING
else
{
for (UIView *sub in [cell.contentView subviews]) {
// if([sub class] == [UITableViewCellContentView class])
NSLog(#"this is uilabel %#",[sub class]);
}
topLabel = (UILabel *)[cell viewWithTag:TOP_LABEL_TAG];
bottomLabel = (UILabel *)[cell viewWithTag:BOTTOM_LABEL_TAG];
}
topLabel.text = [NSString stringWithFormat:#"Cell at row %ld.", [indexPath row]];
bottomLabel.text = [NSString stringWithFormat:#"Some other information.", [indexPath row]];
//
// Set the background and selected background images for the text.
// Since we will round the corners at the top and bottom of sections, we
// need to conditionally choose the images based on the row index and the
// number of rows in the section.
//
UIImage *rowBackground;
UIImage *selectionBackground;
NSInteger sectionRows = [aTableView numberOfRowsInSection:[indexPath section]];
NSInteger row = [indexPath row];
if (row == 0 && row == sectionRows - 1)
{
rowBackground = [UIImage imageNamed:#"topAndBottomRow.png"];
selectionBackground = [UIImage imageNamed:#"topAndBottomRowSelected.png"];
}
else if (row == 0)
{
rowBackground = [UIImage imageNamed:#"topRow.png"];
selectionBackground = [UIImage imageNamed:#"topRowSelected.png"];
}
else if (row == sectionRows - 1)
{
rowBackground = [UIImage imageNamed:#"bottomRow.png"];
selectionBackground = [UIImage imageNamed:#"bottomRowSelected.png"];
}
else
{
rowBackground = [UIImage imageNamed:#"middleRow.png"];
selectionBackground = [UIImage imageNamed:#"middleRowSelected.png"];
}
((UIImageView *)cell.backgroundView).image = rowBackground;
((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;
// cell.backgroundView.backgroundColor = [UIColor colorWithPatternImage:rowBackground];
// cell.selectedBackgroundView.backgroundColor = [UIColor colorWithPatternImage:selectionBackground];
//
// Here I set an image based on the row. This is just to have something
// colorful to show on each row.
//
if ((row % 3) == 0)
{
cell.imageView.image = [UIImage imageNamed:#"imageA.png"];
}
else if ((row % 3) == 1)
{
cell.imageView.image = [UIImage imageNamed:#"imageB.png"];
}
else
{
cell.imageView.image = [UIImage imageNamed:#"imageC.png"];
}
#else
cell.text = [NSString stringWithFormat:#"Cell at row %ld.", [indexPath row]];
#endif
return cell;
}
past it after all #import lines
#define USE_CUSTOM_DRAWING 1
Heading ##Simplest way of changing alternate colors
if(indexPath.row%2) {
cell.backgroundColor=[UIColor nameUrColor] //brownColor, yellowColor, blueColor
} else {
cell.backgroundColor=[UIColor nameAnotherColor]
}
if(cell.contentView)
{
[cell.nameLbl setFont:[UIFont systemFontOfSize:24]];
int red_value = arc4random() % 210;
int green_value = arc4random() % 210;
int blue_value = arc4random() % 210;
cell.contentView.backgroundColor = [UIColor colorWithRed:red_value/255.0 green:green_value/255.0 blue:blue_value/255.0 alpha:0.6];
}

UIImage with UITapGestureRecognizer in first row of UITableViewController not working properly

I've got a UITableViewController subclass that is subclassed by two other classes. In some of the cells of both tables I have an image that the user can click on to perform an action. It all works and I'm happy except that in one of the 2 subclasses the first row of that table has an issue. You can only click the image by clicking to the rightmost edge of the image. If you click in the body of the image it calls the table's didSelectRowAtIndexPath instead.
What boggles me is that it works in the other class for all rows and even in the one it doesn't work for it works in all the other rows where that image shows up. Here's the code I'm using to add the image and gesture recognizer into my cell:
UIImageView *imgparent = [[UIImageView alloc] initWithFrame:CGRectMake(offsetleftmain, 24.0, 14.0, 16.0)];
imgparent.image = [UIImage imageNamed:#"item_open.png"];
imgparent.tag = ITEMOPENTAG;
// add listener
imgparent.userInteractionEnabled = YES;
UITapGestureRecognizer *singleFingerDTapParent = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(loadChildren:)];
[imgparent addGestureRecognizer:singleFingerDTapParent];
[singleFingerDTapParent release];
[cell.contentView addSubview:imgparent];
[imgparent release];
I'm testing in the simulator by the way. I've tried replacing the image with a button but still the problem persists. Any ideas?
EDIT: Here's the beginnings of the code that is called when the image is clicked. Again, this works but in the case of that one row it only works if you click on the right edge of the image. I've attached a screenshot to illustrate. The first circle indicates where I have to tap in order to get my gesture to be called. The second shows an example in the very same table that allows you to click the entire image. I'm confounded.
- (void)loadChildren:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"loadChildren");
// get the cell
UITableViewCell *cell = (UITableViewCell *)gestureRecognizer.view.superview.superview;
UIImageView *imgvw = (UIImageView *)[cell viewWithTag:ITEMOPENTAG]; // open arrow
[imgvw setHidden:YES]; // hide open arrow
imgvw = (UIImageView *)[cell viewWithTag:ITEMCLOSETAG]; // close arrow
[imgvw setHidden:NO]; // show close arrow
// get the record for the cell
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
}
EDIT: Here's the complete tableView:cellForRowAtIndexPath:indexPath. Keep in mind that it's being called from the cellForRowAtIndexPath of 2 other subclasses of this class, each simply passing in an identifier withType:
- (UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath
withType:(NSString *)s_type
{
UITableViewCell *cell = nil;
// if dealing with empty region then show no tasks cell
if(s_type == #"noTasksCell"){
cell = [tv dequeueReusableCellWithIdentifier:#"noTasksCell"];
if( cell == nil ) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"noTasksCell"] autorelease];
}
cell.textLabel.text = #"No tasks";
cell.textLabel.textColor = [UIColor lightGrayColor];
cell.textLabel.font = [UIFont systemFontOfSize:14];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
UILabel *lblMain;
NSDictionary *o_rec = [self getRecForPath:indexPath];
NSString *s_cell = #"rowCell";
BOOL b_parent = NO;
BOOL b_parentOpen = NO;
BOOL b_child = NO;
BOOL b_checked = NO;
if([self isParent:indexPath]){
b_parent = YES;
b_parentOpen = !([o_rec objectForKey:#"b_open"] == nil || [[o_rec objectForKey:#"b_open"] isEqualToNumber:[NSNumber numberWithInt:0]]);
s_cell = [s_cell stringByAppendingString:#"Parent"];
}
if([o_rec objectForKey:#"b_child"] != nil){
b_child = YES;
s_cell = [s_cell stringByAppendingString:[NSString stringWithFormat:#"Child%#",[o_rec objectForKey:#"indent"]]];
}
if([[o_rec objectForKey:#"checked"] isEqualToNumber:[NSNumber numberWithInt:1]]){
b_checked = YES;
s_cell = [s_cell stringByAppendingString:#"isComplete"];
}
// add the following to the name:
// - project id
// - width of table to the name so that rotations will change the cell dequeue names
// - priority
s_cell = [s_cell stringByAppendingFormat:
#"Proj%#Width%dP%#"
,[o_rec objectForKey:#"project_id"]
,(int)tv.bounds.size.width
,[[o_rec objectForKey:#"priority"] stringValue]
];
cell = [tv dequeueReusableCellWithIdentifier:s_cell];
if( cell == nil ) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:s_cell] autorelease];
cell.textLabel.hidden = YES; // hide the regular text label
cell.selectedBackgroundView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
cell.selectedBackgroundView.backgroundColor = [delegate colorForHexWithAlpha:0xffcc66ff];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
int offsetleftmain = 10;
if(b_child){
offsetleftmain += ([[o_rec objectForKey:#"indent"] intValue]-1) * 18;
}
if(b_parent){
// parent arrow
UIImageView *imgparent = [[UIImageView alloc] initWithFrame:CGRectMake(offsetleftmain, 24.0, 14.0, 16.0)];
imgparent.image = [UIImage imageNamed:#"item_open.png"];
imgparent.tag = ITEMOPENTAG;
// add listener
imgparent.userInteractionEnabled = YES;
UITapGestureRecognizer *singleFingerDTapParent = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(loadChildren:)];
singleFingerDTapParent.numberOfTapsRequired = 1;
singleFingerDTapParent.numberOfTouchesRequired = 1;
[imgparent addGestureRecognizer:singleFingerDTapParent];
[singleFingerDTapParent release];
[cell.contentView addSubview:imgparent];
[imgparent release];
// close arrow
UIImageView *imgparent2 = [[UIImageView alloc] initWithFrame:CGRectMake(offsetleftmain-2, 24.0, 16.0, 14.0)];
imgparent2.image = [UIImage imageNamed:#"item_close.png"];
imgparent2.tag = ITEMCLOSETAG;
imgparent2.userInteractionEnabled = YES;
UITapGestureRecognizer *singleFingerDTapParent2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideChildren:)];
[imgparent2 addGestureRecognizer:singleFingerDTapParent2];
[singleFingerDTapParent2 release];
[cell.contentView addSubview:imgparent2];
[imgparent2 release];
}
offsetleftmain += 18;
// checkbox
UIImageView *imgchk = [[UIImageView alloc] initWithFrame:CGRectMake(offsetleftmain, 20.0, 24.0, 24.0)];
imgchk.image = [UIImage imageNamed:#"check_empty.png"];
imgchk.tag = EMPTYCHECKTAG;
// add listener
imgchk.userInteractionEnabled = YES;
UITapGestureRecognizer *singleFingerDTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(checkOnItem:)];
[imgchk addGestureRecognizer:singleFingerDTap];
[singleFingerDTap release];
[cell.contentView addSubview:imgchk];
[imgchk release];
// checked checkbox
UIImageView *imgchk2 = [[UIImageView alloc] initWithFrame:CGRectMake(offsetleftmain, 20.0, 24.0, 24.0)];
imgchk2.image = [UIImage imageNamed:#"check_checked.png"];
imgchk2.tag = CHECKTAG;
// add listener
imgchk2.userInteractionEnabled = YES;
UITapGestureRecognizer *singleFingerDTap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(checkOffItem:)];
[imgchk2 addGestureRecognizer:singleFingerDTap2];
[singleFingerDTap2 release];
[cell.contentView addSubview:imgchk2];
[imgchk2 release];
offsetleftmain += 28;
// main label
lblMain = [[UILabel alloc] initWithFrame:CGRectMake(offsetleftmain, 22.0, tv.bounds.size.width-offsetleftmain-10, 30.0)];
lblMain.tag = MAINLABELTAG;
lblMain.numberOfLines = 4;
lblMain.font = delegate.font_dflt;
// change color based on priority
if (5-[[o_rec objectForKey:#"priority"] intValue] == 1)
lblMain.textColor = [UIColor redColor];
else if (5-[[o_rec objectForKey:#"priority"] intValue] == 2)
lblMain.textColor = [delegate colorForHexWithAlpha:H_P2COLOR];
else if (5-[[o_rec objectForKey:#"priority"] intValue] == 3)
lblMain.textColor = [delegate colorForHexWithAlpha:H_P3COLOR];
[cell.contentView addSubview:lblMain];
[lblMain release];
// show action sheet for long press and hold
UILongPressGestureRecognizer *clicknHold = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(cellClicknHold:)];
[cell addGestureRecognizer:clicknHold];
[clicknHold release];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// task text
lblMain = (UILabel *)[cell viewWithTag:MAINLABELTAG];
lblMain.text = [self formatContent:[o_rec objectForKey:#"content"]];
CGRect newFrame = lblMain.frame;
newFrame.size.height = [[o_rec objectForKey:#"height"] floatValue];
//newFrame.size.height = [[o_rec objectForKey:#"height"] floatValue]+12;
lblMain.frame = newFrame;
// set checked status
[(UIImageView *)[cell viewWithTag:EMPTYCHECKTAG]
setHidden:[[o_rec objectForKey:#"checked"] boolValue]];
[(UIImageView *)[cell viewWithTag:CHECKTAG]
setHidden:![[o_rec objectForKey:#"checked"] boolValue]];
// show open arrow if dealing with parent cell
if (b_parent) {
//NSLog(#"b_parentOpen:%d",b_parentOpen);
[(UIImageView *)[cell viewWithTag:ITEMOPENTAG] setHidden:b_parentOpen];
[(UIImageView *)[cell viewWithTag:ITEMCLOSETAG] setHidden:!b_parentOpen];
}
return cell;
}
Just in case someone runs into a similar issue as rare as that may be maybe this will help avoid someone going bald pulling their hair out. So my problem wasn't with the tableView itself but a sidebar I have that is similar to the facebook sidebar that slides in and out. My sidebar has a tableview in it and that table has a header on it that for some reason when it was right beside my other table was somehow preventing clicks to the image in that first row. My solution was simply to position that sidebar view a little bit further offscreen away from the view it was beside instead of being right next to it. Problem solved. Thanks to all who tried to help me out.

UITableView Lagging due to Shadows and Borders

I have the following code to add a border colour and drop shadow to the background of my UITableViewCell. My problem is that this code causes a huge lag on the tableView itself.
Please can you tell me how I can optimise my code, preventing the lag of the UITableView?
if ([cell viewWithTag:012] == nil && comment.isReply == NO) {
UIImageView *iv = [[[UIImageView alloc] initWithFrame:frame] autorelease];
[iv setImage:[UIImage imageNamed:#"paper"]];
[iv setTag:012];
[cell insertSubview:iv atIndex:0];
[iv.layer setBorderWidth:1.0];
[iv.layer setBorderColor:[[UIColor whiteColor] CGColor]];
[iv.layer setShadowColor:[[UIColor blackColor] CGColor]];
[iv.layer setShadowOffset:CGSizeMake(0, 1)];
[iv.layer setShadowOpacity:0.75];
}
else if ([cell viewWithTag:012] == nil && comment.isReply == YES) {
frame.origin.x += 35;
UIImageView *iv = [[[UIImageView alloc] initWithFrame:frame] autorelease];
[iv setImage:[UIImage imageNamed:#"paper"]];
[iv setTag:012];
[cell insertSubview:iv atIndex:0];
UIImage *arrow = [UIImage imageNamed:#"arrow"];
UIImageView *ivs = [[[UIImageView alloc] initWithFrame:CGRectMake(-12, ([cell frame].size.width / 2) + ([arrow size].width/2) , arrow.size.width, arrow.size.height)] autorelease];
[cell addSubview:ivs];
[iv.layer setBorderWidth:1.0];
[iv.layer setBorderColor:[[UIColor whiteColor] CGColor]];
[iv.layer setShadowColor:[[UIColor blackColor] CGColor]];
[iv.layer setShadowOffset:CGSizeMake(0, 0)];
[iv.layer setShadowOpacity:0.75];
}
In addition to the other optimization advice here, specifying a shadowPath on your CALayer will improve shadow drawing performance. You could determine a path for the shadow with something like this...
iv.layer.shadowPath = [UIBezierPath bezierPathWithRect:iv.bounds].CGPath;
You may also want to look into the shouldRasterize bit on CALayer. This causes the layer to be pre-rendered as a bitmap. Make sure to also provide a rasterizationScale that matches your device if you go this route.
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
You should avoid manipulating the cell on each load, instead you should adjust the UI when the cell is initialized/created.
To illustrate, every time you scroll a new cell (or multiple) could be loaded using the cellForRowAtIndexPath: method, currently you are doing a lot of view changes in this method, but there could be cases where this is not needed (for example the new cell is the same type as the one just scrolled off screen). Move this UI modification to where the cell is initialized not where the data is swapped. You could do this with a subclass or simply like this.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Reuse id
static NSString *identifier1 = #"identifer-1";
static NSString *identifier2 = #"identifier-2";
static NSString *regular = #"regular";
UITableViewCell *cell;
if (comment.isReply == NO) {
cell = [tableView dequeueReusableCellWithIdentifier: identifier1];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: identifier1] autorelease];
// Do the UI modification here
}
} else if (comment.isReply == YES) {
cell = [tableView dequeueReusableCellWithIdentifier: identifier2];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: identifier2] autorelease];
// Do the UI modification here
}
} else {
// Regular cell
cell = [tableView dequeueReusableCellWithIdentifier: regular];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: regular] autorelease];
}
}
// Load the data into the cell
return cell;
}
Hope you get where I'm going with this, the key is to do heavy stuff as little as possible and let the UITableView caching have a greater effect.

Weird updates when using reloadRowsAtIndexPaths: in a table

I've got a table where the accessoryView button in the cell is replaced by a custom check mark icon. If the time interval for a pair of temperature settings is enabled, the cell row shows a check mark, along with a time, heating and cooling value. If the time is disabled, no check mark, and time displayed in the timefield.text is modified to state: "Disabled". The code works fine when I use reloadData. However, since I'm updated only one row at a time, that's overkill for the big table that I'm using. I'm trying to use reloadRowsAtIndexPaths: but I get screen updates that affect not only the cell that was clicked on, but the previous cell that was clicked on. I was using no animations (UITableViewRowAnimationNone), but changed that to fading animations (UITableViewRowAnimationFade) to see what was going on. Sure enough, the fading occurs on the desired table row and the undesired table row. Also, the time value for the current cell row appears in the previous cell row time field. Once I stay with clicking on one row, there's no problem with the updates, but once I switch to another row, I have both rows affected the one time. It's like the update method is picking up cruft from somewhere.
I have also tried bracketing the reloadRowsAtIndexPaths: call with calls to beginUpdates and endUpdates, and nothing changes.
Clearly I'm not understanding something fundamental here. What am I doing wrong? Here is the method in question:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UIButton *button = (UIButton *)cell.accessoryView;
BOOL checked;
if (intervalDisabled[((indexPath.section * 4) + indexPath.row)] == NO) {
checked = YES;
intervalDisabled[((indexPath.section * 4) + indexPath.row)] = YES;
} else {
checked = NO;
intervalDisabled[((indexPath.section * 4) + indexPath.row)] = NO;
}
UIImage *newImage = (checked) ? [UIImage imageNamed:#"unchecked.png"] : [UIImage imageNamed:#"checked.png"];
[button setBackgroundImage:newImage forState:UIControlStateNormal];
timeField.text = [NSString stringWithFormat:#"%#", [self.dateFormatter stringFromDate:[timeOfDays objectAtIndex:((indexPath.section * 4) + indexPath.row)]]];
//[self.tableView reloadData]; <-- This works fine
NSArray *rowArray = [NSArray arrayWithObject:indexPath];
[self.tableView reloadRowsAtIndexPaths:rowArray withRowAnimation:UITableViewRowAnimationFade];
}
Edit:
OK. Here is code you requested. I hope it helps. I will be glad to post other portions. Sorry, I haven't got the hang for formatting code in this editor yet.
// Customized cell for setpoints
- (UITableViewCell *)setPointsCell:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *temperatureSymbol;
static NSString *kCustomCellID = #"setPointsCellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCustomCellID];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kCustomCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
// Set up the cell.
NSDictionary *dictionary = [listOfRows3 objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"Content"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
//Set properties of label and add its text
cell.textLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
cell.textLabel.text = cellValue;
// Add time field
timeField = [[UITextField alloc] init];
timeField.tag = (indexPath.section * 100) + (indexPath.row * 10) + TIME_FIELD;
CGRect frame = CGRectMake(80.0, 6.0, 90.0, 31.0);
timeField.frame = frame;
timeField.borderStyle = UITextBorderStyleRoundedRect;
timeField.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
// timeField.backgroundColor = [UIColor clearColor];
timeField.textAlignment = UITextAlignmentRight;
// Check and handle situation where time interval is disabled
BOOL rowHasCheck;
if (intervalDisabled[((indexPath.section * 4) + indexPath.row)] == NO) {
rowHasCheck = YES;
timeField.text = [NSString stringWithFormat:#"%#", [self.dateFormatter stringFromDate:[timeOfDays objectAtIndex:((indexPath.section * 4) + indexPath.row)]]];
} else {
rowHasCheck = NO;
timeField.text = #"Disabled";
}
UIImage *image = (rowHasCheck) ? [UIImage imageNamed:#"checked.png"] : [UIImage imageNamed:#"unchecked.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect bFrame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
button.frame = bFrame; // match the button's size with the image size
[button setBackgroundImage:image forState:UIControlStateNormal];
// set the button's target to this table view controller so we can interpret touch events and map that to a NSIndexSet
[button addTarget:self action:#selector(checkButtonTapped:event:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor clearColor];
cell.accessoryView = button;
// Override default keyboard handler so we can use a picker instead
timeField.delegate = self;
[cell.contentView addSubview:timeField];
// Set up termperature (F or C) to display
if (temperatureScale == FAHRENHEIT) {
temperatureSymbol = #"F";
} else {
temperatureSymbol = #"C";
}
// Add heating setpoint field
UITextField *heatingSetPointField = [[UITextField alloc] init];
heatingSetPointField.tag = (indexPath.section * 100) + (indexPath.row * 10) + HEATING_FIELD;
frame = CGRectMake(180.0, 6.0, 52.0, 31.0);
heatingSetPointField.frame = frame;
heatingSetPointField.borderStyle = UITextBorderStyleBezel;
heatingSetPointField.backgroundColor = [UIColor colorWithRed:1.0 green:0.2 blue:0.2 alpha:1.0];
heatingSetPointField.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
heatingSetPointField.textAlignment = UITextAlignmentRight;
heatingSetPointField.text = [NSString stringWithFormat:#"%i %#", hSetpoints[((indexPath.section * 4) + indexPath.row)], temperatureSymbol];
// Override default delegate handler
heatingSetPointField.delegate = self;
[cell.contentView addSubview:heatingSetPointField];
// Add cooling setpoint field
UITextField *coolingSetPointField = [[UITextField alloc] init];
coolingSetPointField.tag = (indexPath.section * 100) + (indexPath.row * 10) + COOLING_FIELD;
frame = CGRectMake(240.0, 6.0, 52.0, 31.0);
coolingSetPointField.frame = frame;
coolingSetPointField.borderStyle = UITextBorderStyleBezel;
coolingSetPointField.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
coolingSetPointField.backgroundColor = [UIColor colorWithRed:0.4 green:0.4 blue:1.0 alpha:1.0];
coolingSetPointField.textAlignment = UITextAlignmentRight;
coolingSetPointField.text = [NSString stringWithFormat:#"%i %#", cSetpoints[((indexPath.section * 4) + indexPath.row)], temperatureSymbol];
// Override default delegate handler
coolingSetPointField.delegate = self;
[cell.contentView addSubview:coolingSetPointField];
[timeField release];
[heatingSetPointField release];
[coolingSetPointField release];
return cell;
}
The main problem is that you seem to be using a single timeField reference across all cells. At a minimum, the timeField should be created separately in each cell (just like the heatingSetPointField) and then extract it from the cell later using its tag.
There are many other issues (eg. text fields will get added multiple times to each cell when a cell is reused).
I think reloadData is "working" because you haven't tried scrolling the table view up/down and seeing wrong data in the cells when they come back into view.
You may want to read the Table View Programming Guide (especially the Customizing Cells section) and start with a simpler table view cell and add one layer of complexity at a time until you reach your goal.