Accessory View Challenge


#1

I was able to add the accessory view by setting the accessory type and I am assuming that I have to use the delegate function, tableView:accessoryButtonTappedForRowWithIndexPath: in order to toggle between two different display modes but I am not sure how to actually implement this toggling. Do I have to create another view and swap them out? If so, how would I go about doing this?

Any help would be appreciated…


#2

I would implement the core of this in HomepwnerItemCell. Because each HomepwnerItemCell has a pointer to its Possession instance, it can swap the values for the fields with the other instance variables of the Possession.

Therefore, I would give each Possession a BOOL instance variable that determines whether or not it is displaying the name/value or the serial/date. When you get the delegate message to toggle, you flip this flag for each HomepwnerItemCell. In the implementation of the method that flips the flag for HomepwnerItemCell, I’d reconfigure each of the cells subviews with the appropriate data.


#3

Hi,

Personally I thought the boolean should be a property of the actual TableViewCell, since it will just be used for display purpose. It isn’t really part of the actual possession data. So I thought I would go in that direction. Sadly I didn’t really find how I could get hold of the cell for a given row in the accessoryButtonTappedForRowWithIndexPath method.

Regards,

Stefaan


#4

Hi guys,

I actually found a solution to my own problem … apparently I could simply call the [tableView cellForRowAtIndexPath:indexPath] to get the cell which corresponds to the row for which the accessory button was tapped. Once that was done, it was simply a matter of toggeling the display :slight_smile:

- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
	UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

	if ( cell && [cell isKindOfClass:[HomepwnerItemCell class]] ) {
		[(HomepwnerItemCell *)cell toggleAlternativeDispaly];
	}
				
	[[self tableView] reloadData];
}

So my HomepwnerItemCell has a BOOL flag alternativeDisplay and a method toggleAlternativeDisplay which simply toggles that alternativeDisplay flag, and the setPossession method on my HomepwnerItemCell simply checks the alternativeDisplay flag to see which data it should display.

Feel free to let me know what you think about this approach.

Regards,

Stefaan


#5

Hi,

One problem is that the cells get reused so if the display state is relevant to the actual Possession you’ve lost the association once they move off the screen.

If you really wanted to avoid dirtying the Possession class you could maintain the display state with another array in the controller that mirrored the possessions array.

FWIW
Gareth


#6

FWIW, I was stuck on this challenge, because I was digging through the documentation, reading “tableView:cellForRowAtIndexPath” and thinking to myself, “huh? We overrode that method in ItemsViewController.m, so it CREATES a new cell view each time we call it!”

Then I read sLesage’s solution above, and it kickstarted me in the right direction (thanks for that) - I realized that, essentially, that’s what we have to do - because we’re creating a new view every time the accessory button is tapped.

So I began to implement code similar to sLesage, but then I realized something else - implementing a toggle inside HomepwnerItemCell would end up duplicating code found inside setPossession, and if I tweaked setPossession to decide which view to implement, I would end up duplicating code found inside tableview:cellForRowAtIndexPath.

I hate duplicating code.

With a passion.

So, I played around a bit and came up with the following:


// Place this method inside ItemsViewController.m

- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
	Possession *p = [possessions objectAtIndex:[indexPath row]];
	if ([p defaultDisplay])
		[p setDefaultDisplay:NO];
	else 
		[p setDefaultDisplay:YES];
	
	[tableView cellForRowAtIndexPath:indexPath];
	[[self tableView] reloadData];	
}

// In HomepwnerItemCell.m replace setPossession:possession with the following:

- (void)setPossession:(Possession *)possession {
	
	if ([possession defaultDisplay]) {
		
		// Using a Possession instance, we can set the values of the subviews
		valueWidth = 40.0;
		[valueLabel setText:[NSString stringWithFormat:@"$%d", [possession valueInDollars]]];
		[nameLabel setText:[possession possessionName]];
	} else {
		valueWidth = 320.0;  // Note, I'm using an iPad, and came up with this value by trial and error.  Yours may differ.
		[valueLabel setText:[NSString stringWithFormat:@"Created on: %@", [possession dateCreated]]];
		[nameLabel setText:[NSString stringWithFormat:@"Serial #: %i", [possession serialNumber]]];
	}
	[imageView setImage:[possession thumbnail]]; 	
}

Implement valueWidth as an instance variable rather than a variable local to layoutSubviews to make it accessible to every method within the instance, remove its local declaration from layoutSubviews.
Finally, at the beginning of initWithStyle:style:reuseIdentifier, add valueWidth = 40.0; to set the initial valueWidth to the default value for the initial display.

As Joe said above, set a BOOL variable inside the Possession class to tell us if it’s the alternate or default display (I used defaultDisplay and synthesized appropriate setters/getters).

I also added [self setDefaultDisplay:YES]; to initWithPossessionName:pName:valueInDollars:serialNumber and initWithCoder:decoder inside Possession.m to ensure that the value was set to the default every time the program was run.

After all that, there is one thing I don’t particularly like about my implementation: and that is the use of the [[self tableView] reloadData]; message inside tableView:accessoryButtonTappedforRowWithIndexPath:indexPath method; it seems to be very “over-the-top” in the way we refresh the table view on the screen.

Is there a better implementation other than using the reloadData message?


#7

Hi,

Instead of reloadData there is also reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

which gives some nice animation options.

I like UITableViewRowAnimationMiddle but it looks a bit wierd when it’s the first or last row so I check for that and use UITableViewRowAnimationFade in that case.

Gareth