Design for custom tableviewcell button action

  • 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?

    Thanks,
    Bryan
  • Set your controller as the buttons taget:

    – addTarget:action:forControlEvents:
    UIControlEventTouchUpInside

    atze

    Am 25.06.2009 um 08:30 schrieb Bryan Hansen:

    > 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?
    >
    > Thanks,
    > Bryan
  • Hi Bryan,

    I had exactly the same problem and here's how I solved it. I have a
    custom table view cell in a separate nib file whose File Owner's class
    is the table view controller class. I then have an IB action defined
    in the table view controller class and I link the button in the cell
    nib to the action in the table view controller by linking the button
    to the cell's File Owner's action instead. Of course, I still need to
    attach the table view controller object to each actual cell's File
    Owner, which I do here:

    - (UITableViewCell*) tableView: (UITableView*) table_view
              cellForRowAtIndexPath: (NSIndexPath*) index_path
    {
        static NSString* cellID = @"cellID";

        CustomCell* cell = (CustomCell*) [table_view
            dequeueReusableCellWithIdentifier: cellID];

        if (cell == nil)
        {
            NSArray* nib = [[NSBundle mainBundle]
                loadNibNamed: @"CellNibName" owner: self options: nil];

            cell = (CustomCell*) [nib objectAtIndex: 0];

            // other one-time cell configuration stuff here
        }

        // per-cell configuration stuff here
    }

    Note the two key steps:

    1) the File Owner proxy object in the custom cell's nib file must have
    its class set to your table view controller class.

    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.

    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.

    Wagner

    On Jun 25, 2009, at 8:30 AM, 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?
    >
    > Thanks,
    > Bryan
  • Am 25.06.2009 um 10:16 schrieb WT:

    > I had exactly the same problem and here's how I solved it.

    Why didn’t you put the cell into the controllers nib?
    Make an outlet in your controller to reach the cell and just return it
    when you need it?

    The second nib seems to much work. And you get a speed bump by loading
    it.

    > - (UITableViewCell*) tableView: (UITableView*) table_view
    > cellForRowAtIndexPath: (NSIndexPath*) index_path
    > {
    > static NSString* cellID = @"cellID";
    >
    > CustomCell* cell = (CustomCell*) [table_view
    > dequeueReusableCellWithIdentifier: cellID];
    >
    > if (cell == nil)
    > {
    > cell = customCellOutlet;
    >
    > // other one-time cell configuration stuff here
    > }
    >
    > // per-cell configuration stuff here
    > }

    atze
  • On Jun 25, 2009, at 10:26 AM, Alexander Spohr wrote:

    > Am 25.06.2009 um 10:16 schrieb WT:
    >
    >> I had exactly the same problem and here's how I solved it.
    >
    > Why didn’t you put the cell into the controllers nib?
    > Make an outlet in your controller to reach the cell and just return
    > it when you need it?
    >
    > The second nib seems to much work. And you get a speed bump by
    > loading it.
    >
    >> - (UITableViewCell*) tableView: (UITableView*) table_view
    >> cellForRowAtIndexPath: (NSIndexPath*) index_path
    >> {
    >> static NSString* cellID = @"cellID";
    >>
    >> CustomCell* cell = (CustomCell*) [table_view
    >> dequeueReusableCellWithIdentifier: cellID];
    >>
    >> if (cell == nil)
    >> {
    >> cell = customCellOutlet;
    >>
    >> // other one-time cell configuration stuff here
    >> }
    >>
    >> // per-cell configuration stuff here
    >> }

    Because -tableView:cellForRowAtIndexPath: needs to return a different
    cell object for each visible row. Your code above returns always the
    same object, the one table view cell archived in the nib, which means
    that changing the data in one cell would change the data in all
    visible cells, and all visible cells would have the same data all the
    time.

    Wagner
  • Am 25.06.2009 um 10:54 schrieb WT:

    > On Jun 25, 2009, at 10:26 AM, Alexander Spohr wrote:
    >
    >> Why didn’t you put the cell into the controllers nib?
    >> Make an outlet in your controller to reach the cell and just return
    >> it when you need it?
    >
    > Because -tableView:cellForRowAtIndexPath: needs to return a
    > different cell object for each visible row. Your code above returns
    > always the same object, the one table view cell archived in the nib,
    > which means that changing the data in one cell would change the data
    > in all visible cells, and all visible cells would have the same data
    > all the time.

    Of course, you are right. I forgot that the OP wanted something like
    an accessory which needs to be repetitive.

    atze
  • On Jun 25, 2009, at 11:46 AM, Alexander Spohr wrote:

    > Am 25.06.2009 um 10:54 schrieb WT:
    >
    >> On Jun 25, 2009, at 10:26 AM, Alexander Spohr wrote:
    >>
    >>> Why didn’t you put the cell into the controllers nib?
    >>> Make an outlet in your controller to reach the cell and just
    >>> return it when you need it?
    >>
    >> Because -tableView:cellForRowAtIndexPath: needs to return a
    >> different cell object for each visible row. Your code above returns
    >> always the same object, the one table view cell archived in the
    >> nib, which means that changing the data in one cell would change
    >> the data in all visible cells, and all visible cells would have the
    >> same data all the time.
    >
    > Of course, you are right. I forgot that the OP wanted something like
    > an accessory which needs to be repetitive.
    >
    > atze

    Even with a custom cell with no extra controls, you'd need to return a
    different cell object for each visible row. Try this and see what
    happens:

    - (NSInteger) tableView: (UITableView*) table_view
      numberOfRowsInSection: (NSInteger) section
    {
        return 10;
    }

    - (UITableViewCell*) tableView: (UITableView*) table_view
    cellForRowAtIndexPath: (NSIndexPath*) index_path
    {
        NSInteger row = [index_path row];

        static NSString* cellID = @"cellID";

        UITableViewCell* cell = nil;

        cell = [table_view dequeueReusableCellWithIdentifier: cellID];

        if (cell == nil)
        {
            cell = customCell;
        }

        if (row % 2 == 0)
        {
            cell.contentView.backgroundColor = [UIColor blueColor];
        }
        else
        {
            cell.contentView.backgroundColor = [UIColor redColor];
        }

        return cell;
    }

    You will NOT see a table with alternating colors for its cells. I had
    thought that you'd see all cells with the same color, but in fact I
    was wrong about that. What you do see is that only ONE cell has its
    background colored. And it makes perfect sense, since cells are views
    and the ONE custom cell is repeatedly added as a subview to the table
    view "content" area for each row, but only the last one "sticks"
    because, again, you only have one cell object.

    Wagner
  • Am 25.06.2009 um 12:20 schrieb WT:

    > On Jun 25, 2009, at 11:46 AM, Alexander Spohr wrote:
    >
    >> Of course, you are right. I forgot that the OP wanted something
    >> like an accessory which needs to be repetitive.
    >
    > Even with a custom cell with no extra controls, you'd need to return
    > a different cell object for each visible row.

    That’s what I meant by "repetitive" ;)
    I have some tables (for setup purposes) that use specialized cells in
    only one row.

    atze
  • On Jun 25, 2009, at 12:36 PM, Alexander Spohr wrote:

    > Am 25.06.2009 um 12:20 schrieb WT:
    >
    >> On Jun 25, 2009, at 11:46 AM, Alexander Spohr wrote:
    >>
    >>> Of course, you are right. I forgot that the OP wanted something
    >>> like an accessory which needs to be repetitive.
    >>
    >> Even with a custom cell with no extra controls, you'd need to
    >> return a different cell object for each visible row.
    >
    > That’s what I meant by "repetitive" ;)
    > I have some tables (for setup purposes) that use specialized cells
    > in only one row.
    >
    > atze
    >

    Ok, fair enough. My apologies for misunderstanding what you meant.

    Wagner
  • 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
  • On Jun 26, 2009, at 9:08 PM, mmalc Crawford wrote:

    > if (cell == nil) {
    > [[NSBundle mainBundle] loadNibNamed:@"<#Your nib file name#>"
    > owner:self options:nil];
    > cell = <#cell outlet property#>;
    > self.<#cell outlet property#> = nil;
    > }

    Please pardon my noobness, but could you expand a little on how this
    works?  I'm assuming that the first line somehow assigns the <#cell
    outlet property#>, since I don't see how else this could work, but I
    sure don't see how that actually happens.  It certainly looks like a
    alloc/assign/release pattern, but I feel like there should be some
    more code.

    Thanks,
    Brian
  • On Jun 26, 2009, at 7:20 PM, Brian Slick wrote:

    > Please pardon my noobness, but could you expand a little on how this
    > works?  I'm assuming that the first line somehow assigns the <#cell
    > outlet property#>, since I don't see how else this could work, but I
    > sure don't see how that actually happens.  It certainly looks like a
    > alloc/assign/release pattern, but I feel like there should be some
    > more code.

    From the set-up:

    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 **

    When the nib file is loaded, the cell property is set to point to the
    newly-loaded cell.
    (It's not related to memory management.)

    mmalc
  • One more question about this approach...

    On Jun 26, 2009, at 9:08 PM, mmalc Crawford wrote:

    > 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

    Does the first line mean that I cannot reuse the XIB-based cells in
    different view controllers?

    I currently have several custom cells (code only) that are used in 3
    different view controllers.  The approach I am using is a combination
    of the Apple-provided sample 'TouchCells' and this post: http://weblog.bignerdranch.com/?p=56

    It looks like this:

    if (cell == nil)
    {
        cell = (MyCustomCell *)[[[MyCustomCell alloc]
    initWithFrame:CGRectZero
          reuseIdentifier:MyCustomCellIdentifier] autorelease];
        [[cell checkButton] addTarget:self
          action:@selector(buttonPressed:)
          forControlEvents:UIControlEventTouchUpInside];
    }

    ...where checkButton is a UIButton property of the custom cell.  (and
    I am aware that I need to update my initWithFrame: method).  This
    exact code works equally well in all 3 view controllers.

    This is serving me well so far, though now that I am past my "I avoid
    IB for iPhone development" phase, I'm interested in reworking my cells
    around IB.  But, if that means I have to have 3 copies of the same
    file just so that File's Owner can be different, I'm losing interest.
    Is there a way to use this technique and share the cells with multiple
    view controllers?

    This might be more of a general question regarding XIB reuse.  At the
    moment, I have a handful of XIB files for view controllers that are
    identical except for File's Owner.  It would be nice to get away with
    using a single file.

    Thanks,

    Brian
  • On Jun 30, 2009, at 4:36 PM, Brian Slick wrote:

    > Does the first line mean that I cannot reuse the XIB-based cells in
    > different view controllers?
    >
    No.  As long as the view controllers all provide the same API (the
    actions and outlets) you use in the nib file, then the actual class
    doesn't really matter.  (Obviously this puts a little more
    responsibility on you to ensure that all your classes are kept in sync.)

    (It might actually be useful if Interface Builder allowed you to set a
    protocol rather than a class for the identity of an object:
    <rdar://problem/7022113> Allow object identity to be set to a protocol
    )

    mmalc
  • Well, at first I really liked this technique, and it seems simple
    enough.  I restructured my program to use it, and in the simulator all
    went well.  Then I uploaded to the phone (1st-gen) and saw absolutely
    horrid scrolling performance.  I fired up the CA tool, and saw numbers
    that were well under 10 FPS, usually around 4.

    I did make a slight adjustment to the technique - most significantly
    the omitting of the identifier in the XIB file, because I have a few
    different display options that hide various aspects of my cell -
    because I wasn't getting the flexibility I wanted.  In order to
    determine if my changes were causing a problem, I built the
    TaggedLocations example, and also ran it on the phone with the CA
    tool.  Similar numbers, similarly horrid scrolling performance.
    Rebooting the phone didn't seem to make a difference for either program.

    I flipped on the "Color Blended Layers" toggle for both programs, to
    see if there were any key differences.  My UIButton image has a
    transparent background, so that lit up, but that has been the case all
    along, and I had decent scrolling performance before.  The primary
    event name label in the TaggedLocations example lit up.  1 field in
    each case, although in terms of number of pixels the TaggedLocations
    program is even worse than mine.

    So, if TaggedLocations was flying while mine was sucking, I'd probably
    concede that what I was doing was wrong (and would probably need more
    custom cells).  But since neither one is performing very well, and I
    can't find any significant differences between them, I'm thinking
    there is either a flaw with this approach, or my trusty 1st-gen just
    isn't bringing enough horsepower to handle it.

    For now, I think I'll just go back to code-only cells, although if
    there are any thoughts about what the problem is and/or what I can do
    about it, I'd be interested in hearing them.

    Brian

    On Jun 26, 2009, at 9:08 PM, mmalc Crawford wrote:

    > 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 Jul 3, 2009, at 11:54 AM, Brian Slick wrote:

    > So, if TaggedLocations was flying while mine was sucking, I'd
    > probably concede that what I was doing was wrong (and would probably
    > need more custom cells).  But since neither one is performing very
    > well, and I can't find any significant differences between them, I'm
    > thinking there is either a flaw with this approach, or my trusty 1st-
    > gen just isn't bringing enough horsepower to handle it.
    >
    The only time you should see a performance hit with this approach is
    when the cells are first loaded; once they're loaded (and cached),
    performance should be identical whether you code the cell
    programmatically or use a nib file.  *Without looking at any
    performance metrics* (I don't have a device to hand), I'd *guess* the
    issue is likely to be with transparency.  Try switching off
    transparency wherever you can find it and see if that helps.
    If you can exactly recreate your hand-coded cell in IB, it would be
    interesting to see what the performance comparison is like.

    > I did make a slight adjustment to the technique - most significantly
    > the omitting of the identifier in the XIB file,
    >

    This is actually a very significant factor.
    If you haven't set an identifier, it's not clear how you're retrieving
    cached cells in tableView:cellForRowAtIndexPath:?

    I assume you have a test along the lines of:

        UITableViewCell *cell = [tableView
    dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = ...

    Could you check whether you're creating a new cell each time?

    All this aside, please file a performance bug against the
    TaggedLocations sample.

    mmalc
  • On Jul 3, 2009, at 4:39 PM, mmalc Crawford wrote:

    > On Jul 3, 2009, at 11:54 AM, Brian Slick wrote:
    >
    >> I did make a slight adjustment to the technique - most
    >> significantly the omitting of the identifier in the XIB file,
    >
    > This is actually a very significant factor.
    > If you haven't set an identifier, it's not clear how you're
    > retrieving cached cells in tableView:cellForRowAtIndexPath:?
    >
    > I assume you have a test along the lines of:
    >
    > UITableViewCell *cell = [tableView
    > dequeueReusableCellWithIdentifier:CellIdentifier];
    > if (cell == nil) {
    > cell = ...
    >
    > Could you check whether you're creating a new cell each time?

    That's impressive; good call.  It was indeed creating a new cell each
    time.  And now that I take a second look at the code, I'm pretty sure
    I see why.  The XIB-loading command itself doesn't actually have the
    identifier as a parameter, and is thus totally dependent on that info
    being in the XIB.  Therefore, the cell never gets queued.  My initial
    thought was that omitting it would make it generic, but I now see the
    flaw in that thinking.  Simply declaring the identifier doesn't make
    it get used anywhere.  :p

    Incidentally, that's what is wrong with TaggedLocations... it also
    does not have the identifier in the XIB.  I manually added it, then
    retested, and saw dramatically better performance (for my own program,
    too).

    So now I either need to figure out how to make my scenarios all work
    with a single identifier, or I may just go ahead with the code-only
    cell I just built in response to the performance issue.  If would be
    nice if there was some kind of queueWithReuseIdentifier: method for
    these XIB cases.  (This is what I'm trying to do:  http://bit.ly/l55eH )

    > All this aside, please file a performance bug against the
    > TaggedLocations sample.

    Done, although I'm not sure if I did so correctly.  Thank you for your
    help.

    Brian
  • On Jul 3, 2009, at 2:45 PM, Brian Slick wrote:

    > So now I either need to figure out how to make my scenarios all work
    > with a single identifier, or I may just go ahead with the code-only
    > cell I just built in response to the performance issue.  If would be
    > nice if there was some kind of queueWithReuseIdentifier: method for
    > these XIB cases.
    >
    Oh, you can have as many identifiers as you want.
    For a nib-based approach, simply have as many nib files as you want --
    where each nib file has a single cell but a different identifier --
    and you probably(*) have an outlet for each type.

    // Caveat: Typed in Mail
    - (UITableViewCell *)tableView:(UITableView *)tableView
    cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    if (condition1) {
      static NSString *CellType1Identifier = @"CellType1Identifier";

      CellType1 *cell = (CellType1 *)[tableView
    dequeueReusableCellWithIdentifier:CellType1Identifier];
      if (cell == nil) {
      [[NSBundle mainBundle] loadNibNamed:@"CellType1" owner:self
    options:nil];
      cell = cellType1;
      self. cellType1 = nil;
      // configure cell ...
      return cell;
    }

    if (condition2) {
      static NSString *CellType12dentifier = @"CellType12dentifier";

      CellType1 *cell = (CellType1 *)[tableView
    dequeueReusableCellWithIdentifier:CellType2Identifier];
      if (cell == nil) {
      [[NSBundle mainBundle] loadNibNamed:@"CellType2" owner:self
    options:nil];
      cell = cellType2;
      self. cellType2 = nil;
      // configure cell ...
      return cell;
    }

    // ...

    (*) You wouldn't actually *need* a different outlet for each type,
    since the outlet is used only very briefly for each load, and each
    load happens sequentially, but you may prefer to identify each type
    nevertheless.

    > Incidentally, that's what is wrong with TaggedLocations... it also
    > does not have the identifier in the XIB.  I manually added it, then
    > retested, and saw dramatically better performance (for my own
    > program, too).
    > [...]
    >> All this aside, please file a performance bug against the
    >> TaggedLocations sample.
    > Done, although I'm not sure if I did so correctly.  Thank you for
    > your help.
    >

    Ah, thanks.
    If you could send the bug number, that would be useful.

    mmalc