Re: Design for custom tableviewcell button action
Re: Design for custom tableviewcell button action
- Subject: Re: Design for custom tableviewcell button action
- From: mmalc Crawford <email@hidden>
- Date: Fri, 26 Jun 2009 18:08:29 -0700
On Jun 24, 2009, at 11:30 PM, Bryan Hansen wrote:
I'd like to add a custom button to my own custom tableview subclass
that will perform an action on the tableviewcontroller class. This
is pretty much identical to the way accessoryviews call a method on
the tableviewdelegate when it is tapped. The difference is it will
be my own button placed where I choose in the cell. The problem I'm
having is figuring out how to propagate this to the
tableviewcontroller. The UITableViewCell class does not hold a
reference back to the tableview or controller, so I'm a little
confused on the best way to set up this behavior. Can anyone offer
some insight on the correct way to get a button tap in a cell to
call a method on the tableviewcontroller? One that does not have bad
coupling in its design?
Since this doesn't seem to have been fully addressed yet, and
particularly given that there are errors and an omission in one of the
responses:
There are two possible scenarios, for "static" or "replicated" content.
Static content
--------------
If you have a table view whose contents are "static" -- that is, you
have a small fixed set of unique cells are simply using the table view
for layout -- then it is reasonable to configure the individual cells,
including your button (and perhaps to make things easiest, the table
view itself), in a nib file. Broadly speaking the technique is then
as follows.
The class of the File's Owner should be your table view controller
class.
In your table view controller class, you declare outlets for the
various cells.
In the nib file, you connect the outlets to the cells (and if you
choose to put the table view in the nib file, connect the table view
outlet to the table view), and connect the button's action to the
File's Owner.
If you choose to put the table view in the nib file, you initialise
the table view controller using initWithNibName:bundle: and you're
pretty much done. In your tableView:cellForRowAtIndexPath: method you
check for the section/row and return the appropriate cell (configured
if necessary):
// Simplified code example
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
return cell0;
}
if (indexPath.row == 1) {
return cell1;
}
// (or use a case statement) etc. -- configuring cells where
appropriate.
If you don't put the table view in the same nib file and don't
initialise the table view controller using initWithNibName:bundle:,
then you load the cell's nib files in tableView:cellForRowAtIndexPath:
// Simplified code example
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
if (cell0 == nil) {
[[NSBundle mainBundle] loadNibNamed:@"MyCell" owner:self
options:nil];
}
return cell0;
}
// etc. -- configuring cells where appropriate.
Replicated content
------------------
If you replicate a cell within a table view, then (assuming you want
to use a nib file) the cell must go in a separate nib file so that you
can load it an arbitrary number of times, as discussed here: <https://devforums.apple.com/thread/3469?start=0&tstart=0
>
To summarise the general approach, though, with a particular
implementation:
In your table view controller class, declare an outlet for the custom
cell and an action method for the button:
@property (nonatomic, assign) IBOutlet <#Your table view cell class#>
*<#cell outlet property#>;
- (IBAction)<#button action#>:(UIButton *)sender;
In your table view cell class, declare an outlet for the button (and
any other UI elements as appropriate):
@property (nonatomic, retain) IBOutlet UIButton *<#button property#>;
In the cell's nib file:
The class of the File's Owner should be your table view controller
class.
Set an identifier for the cell.
Connect the appropriate outlet from File's Owner to the cell
Connect the button's action to the File's Owner
Connect the cell's button outlet to the button
In your table view controller subclass, then implement the
tableView:cellForRowAtIndexPath: method along the following lines:
// Simplified code example
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"<#your cell's idenitifier#>";
<#Your table view cell class#> *cell = (<#Your table view cell
class#> *)
[tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:@"<#Your nib file name#>"
owner:self options:nil];
cell = <#cell outlet property#>;
self.<#cell outlet property#> = nil;
}
cell.<#button property#>.tag = indexPath.row;
// implementation continues...
In your implementation of the action method, you can ask the button
for its tag (which will indicate the row with which it's associated).
- (IBAction)<#button action#>:(UIButton *)sender {
NSInteger row = sender.tag;
For an example that illustrates several aspects of this approach, see:
<http://developer.apple.com/iphone/library/samplecode/TaggedLocations/index.html
>
On Jun 25, 2009, at 1:16 AM, WT wrote:
I have a custom table view cell in a separate nib file whose File
Owner's class is the table view controller class.
[...]
There are errors and an omission in this description:
static NSString* cellID = @"cellID";
Note that you must specify the same identifier for the cell in the nib
file.
NSArray* nib = [[NSBundle mainBundle]
loadNibNamed: @"CellNibName" owner: self options: nil];
cell = (CustomCell*) [nib objectAtIndex: 0];
In general, the appropriate way to refer to an object in the nib file
is using an outlet.
See above and <https://devforums.apple.com/thread/3469?
start=0&tstart=0> for a discussion of the technique.
2) you must pass 'self' as the owner in the -
loadNibNamed:owner:options: call.
This way, when the button is tapped, the action in the table view
controller is triggered. Now, presumably, you know which cell is
currently selected (you probably keep track of that in your table
view controller class), so you always know which cell the button
action came from.
There is no guarantee that if a button is tapped the corresponding row
is selected (in fact it typically won't be). Moreover, *in general*,
selections in a table view should be temporary, so you should actually
not be keeping track of the current selection (see the iPhone UI
Guidelines for details).
Alternatively, you can set the button's tag to an index that depends
on the cell's index path. If your table has only one section, then
button.tag = [index_path row];
would do be sufficient. You should set the tag inside the if (cell
== nil) block above, assuming that you expose the button as a cell
property and assuming that you're not adding or deleting cells. If
you do add or delete cells, you should set the button tag *outside*
the if (cell == nil) block. Either way, you can identify which row
(ie, which cell) is responsible for triggering the action by looking
at the action's sender's tag.
Whether or not cells are added or deleted is not relevant.
If (as will typically be the case) cells may be *reused*, then you
should set the tag for the cell *outside* of the (cell == nil) block
since the identifier should be updated each time the cell is reclaimed
to display a different row.
mmalc
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden