A: 

I do something similar (starring a favorite) like this, but I think you're demanding a lot from the thumbs of iPhone users with the cell directing people to another view. First of all, I would check out the detail disclosure option on cells. One of the options is a pre-made arrow button that you can attach to. Your call.

You might be able to get away with catching the didSelectRowAtIndexPath event and then doing some other logic instead of redirecting if the touch was on your checkbox, although I don't know how you would get the position. This means you might need to find a way to get ahold of the touch event before it calls didSelectRowAtIndex path, which I'm not quite sure how to do. Have you worked with handling touchesBegan and the like yet?

TahoeWolverine
The screenshot I linked is from the Groceries app by Sophiestication (http://www.sophiestication.com/groceries/). I know it's possible because that app does it and it works quite well.I've been avoiding creating my own UITableViewCell class - so far I've got away with putting views in the contentView. To implement touchesBegan correctly I think I have no choice but to create my own UITableViewCell class. How did you write the code to star your rows as favorites?
rein
+8  A: 

It's actually pretty easy.

Just create a new subclass of UIControl and put it all in there (no need for a separate controller.) Let's call it ToggleImageControl.

@interface ToggleImageControl : UIControl
{
   BOOL selected;
   UIImageView *imageView;
   UIImage *normalImage;
   UIImage *selectedImage;
}

Create a ToggleImageControl for each cell, and add it at the appropriate position.

ToggleImageControl *toggleControl = [[ToggleImageControl alloc] initWithFrame: <frame>];
toggleControl.tag = indexPath.row;  // for reference in notifications.
[cell.contentView addSubview: toggleControl];

Add a UIImageView to contain the image. Add a target for the touch event.

- (void) viewDidLoad
{
   normalImage = [UIImage imageNamed: @"normal.png"];
   selectedImage = [UIImage imageNamed: @"selected.png"];
   imageView = [[UIImageView alloc] initWithImage: normalImage];
   // set imageView frame
   [self.view addSubview: imageView];

[self addTarget: self action: @selector(toggleImage) forControlEvents: UIControlEventTouchUpInside];

}

Set the UIImageView's image property to update the image; that will trigger the redraw without side-effects.

- (void) toggleImage
{
   selected = !selected;
   imageView.image = (selected ? selectedImage : normalImage); 

   // Use NSNotification or other method to notify data model about state change.
   // Notification example:
   NSDictionary *dict = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: self.tag forKey: @"CellCheckToggled"];
   [[NSNotificationCenter defaultCenter] postNotificationName: @"CellCheckToggled" object: self userInfo: dict];

}

You will obviously need to massage some stuff. You probably want to pass in the two image names to make it more reusable, and also I'd recommend specifying the notification name string from outside the object as well (assuming you are using the notification method.)

Amagrammer
P.S. -- I actually do some things this way -- it works.
Amagrammer
This is great! Thanks. One question, when the toggleImage control is tapped the tap won't be bubbled to the UITableViewControl's didSelectRowAtIndexPath. Correct?
rein
No, the button intercepts it and prevents it being seen by the cell.
Amagrammer
Can you also just make this control an empty frame and use the existing cell.imageView property to draw the actual image?
sehugg
sehugg: Probably. Almost anything can be done by layering controls.
Amagrammer
thanks for this exactly what i'm looking for
nathan_hc
A: 

So the "..obviously need to massage some stuff.." comment means "...this code doesn't work...".

So

- (void) viewDidLoad

should be

- (id)initWithFrame:(CGRect)frame 
{
 if ( self = [super initWithFrame: frame] ){
  normalImage = [UIImage imageNamed: @"toggleImageNormal.png"];
  selectedImage = [UIImage imageNamed: @"toggleImageSelected.png"];
  imageView = [[UIImageView alloc] initWithImage: normalImage];

 // set imageView frame
  [self addSubview: imageView];

  [self addTarget: self action: @selector(toggleImage) forControlEvents: UIControlEventTouchDown];
 }

 return self;
}

As - (void) viewDidLoad never gets called.

Peter Brooke
+2  A: 

where can i get the checkmark image shown in the above screenshot...???

satish
The screenshot is from the App "Groceries" (http://www.sophiestication.com/groceries/). Unfortunately I can't tell you where they got it from.
rein
+1  A: 

There's an even EASIER way to do this, if you override touchesBegan: you need to do an if statement to decide if it's within the check marks proximity, if it's not call [super touchesBegan:touches withEvent:event] and it will act as though it was selected.

Yakattak
+1  A: 

Here's an implementation of the "override touchesBegan:" approach I'm using that is simple and seems to work well.

Just include this class in your project and create and configure a TouchIconTableViewCell instead of a UITableView cell in your tableView:cellForRowAtIndexPath: method.

TouchIconTableViewCell.h:

#import <UIKit/UIKit.h>

@class TouchIconTableViewCell;

@protocol TouchIconTableViewCellDelegate<NSObject>

@required
- (void)tableViewCellIconTouched:(TouchIconTableViewCell *)cell indexPath:(NSIndexPath *)indexPath;

@end

@interface TouchIconTableViewCell : UITableViewCell {
    id<TouchIconTableViewCellDelegate> touchIconDelegate;       // note: not retained
    NSIndexPath *touchIconIndexPath;
}

@property (nonatomic, assign) id<TouchIconTableViewCellDelegate> touchIconDelegate;
@property (nonatomic, retain) NSIndexPath *touchIconIndexPath;

@end

TouchIconTableViewCell.m:

#import "TouchIconTableViewCell.h"

@implementation TouchIconTableViewCell

@synthesize touchIconDelegate;
@synthesize touchIconIndexPath;

- (void)dealloc {
    [touchIconIndexPath release];
    [super dealloc];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    CGPoint location = [((UITouch *)[touches anyObject]) locationInView:self];
    if (CGRectContainsPoint(self.imageView.frame, location)) {
        [self.touchIconDelegate tableViewCellIconTouched:self indexPath:self.touchIconIndexPath];
        return;
    }
    [super touchesBegan:touches withEvent:event];
}

@end

Each time you create or re-use the cell, set the touchIconDelegate and touchIconIndexPath properties. When your icon is touched, the delegate will be invoked. Then you can update the icon or whatever.

Archie
sorry but what exactly do you mean by "set the touchIconDelegate and touchIconIndexPath properties"?
isaaclimdc
A: 

hey rein can you provide a tutorial for the above problem.

I am able to create the checkbox on tableview but how to manipulate with it. On button click i want to check the status(selected/Deselected) of all the checkbox present on each row?

Actually am displaying products on each row and to compare between two products i need to select any two of the products.So how should i relate the checkbox with the Product Details in the tableview.

TableCellView.h

@interface TableCellViewController : UITableViewCell {
IBOutlet UILabel *lblProduct;
IBOutlet UILabel *lblPrice;
IBOutlet UIImageView *imgProduct;}
-(void)setProductText:(NSString *)_text;
-(void)setPriceText:(NSString *)_text;
-(void)setProductImage:(NSString *)_text;

Chekbox.h

@interface Checkbox : UIButton {
BOOL isChecked;
}
@property (nonatomic,assign) BOOL isChecked;

-(IBAction) checkBoxClicked;

Product.m

import "TableCellView.h"

import "Checkbox.h"

.......

abhikpw