Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableViewCell. How do I programmatically align textLabel to top?

By default, a UITableViewCell instance positions the label pair textLabel/detailTextLabel in the center of its parent view. I prefer to have the pair aligned to the top of the cell. How do I do that programmatically?

Thanks,
Doug

like image 376
dugla Avatar asked Jul 28 '10 11:07

dugla


2 Answers

The docs describe -[UIView layoutSubviews] as

"Overridden by subclasses to layout subviews when layoutIfNeeded is invoked. The default implementation of this method does nothing."

That description is not elaborate, but is accurate. In this case, the method's behavior is to layout your subviews. It will be invoked anytime the device orientation changes. It is also scheduled for later invocation whenever you call -setNeedsLayout.

Since, UIView's implementation does nothing, (and I presume the same for UIControl), you get total creative freedom to make your UIView subclass subviews be positioned wherever you want them.

In subclassing a UITableViewCell, you have a couple of options to try:

  1. Override -layoutSubviews and
    1. manipulate the position of the built-in textLabel and -detailLabel views.
  2. Override -viewDidLoad,
    1. create two of your own UILabels to provide the text and detailed text,
    2. add them to self.contentView, and
    3. override -layoutSubviews to manipulate the position of your custom UILabel views

In a related SO question, the recommendation is to avoid #1, manipulating the built-in textLabel and detailTextLabel.

A more reliable bet would be to do something like this:


@interface MyTableViewCell : UITableViewCell {
    UILabel *myTextLabel;
    UILabel *myDetailTextLabel;
}
// ... property declarations
@end

@implementation MyTableViewCell 
@synthesize myTextLabel, myDetailTextLabel;

- (id) initWithFrame: (CGRect)frame {
    self = [super initWithFrame: frame];
    if (self) {
        myTextLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
        [self.contentView addSubview: myTextLabel];

        myDetailTextLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
        [self.contentView addSubview: myDetailTextLabel];
    }
    return self;
}

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

- (void) layoutSubviews {

    // Let the super class UITableViewCell do whatever layout it wants. 
    // Just don't use the built-in textLabel or detailTextLabel properties
    [super layoutSubviews]; 

    // Now do the layout of your custom views

    // Let the labels size themselves to accommodate their text
    [myTextLabel sizeToFit];
    [myDetailTextLabel sizeToFit];

    // Position the labels at the top of the table cell
    CGRect newFrame = myTextLabel.frame;
    newFrame.origin.x = CGRectGetMinX (self.contentView.bounds);
    newFrame.origin.y = CGRectGetMinY (self.contentView.bounds);
    [myTextLabel setFrame: newFrame];

    // Put the detail text label immediately to the right 
        // w/10 pixel gap between them
    newFrame = myDetailTextLabel.frame;
    newFrame.origin.x = CGRectGetMaxX (myTextLabel.frame) + 10.;
    newFrame.origin.y = CGRectGetMinY (self.contentView.bounds);
    [myDetailTextLabel setFrame: newFrame];
}

@end

In MyTableViewCell, you would ignore the built-in text labels and use your own custom UILabels. You take complete control over positioning them within the content rect of the table view cell.

I'm leaving a lot of stuff out. In doing custom layout with text labels, you'd want to consider:

  • Figuring out your own layout algorithm.

    I'm using a layout algorithm above that resizes the custom UILabels to fit their text content, then positions them side-by-side. You'll likely want something more specific to your app.

  • Keeping the custom labels within the content view.

    In -layoutSubviews, you might want logic to keep the custom UILabels sized and positioned so that they don't fall outside the bounds of the content rect. With my naive layout logic, any long text dropped into either UILabel (or both) could cause the labels to be positioned right out of the content view bounds.

  • How to handle -viewDidLoad/-viewDidUnload.

    As coded above, this subclass doesn't handle being loaded from a nib. You might want to use IB to layout your cell, and if you do, you'll need to think about -viewDidLoad/-viewDidUnload/-initWithCoder:

like image 190
Bill Garrison Avatar answered Oct 21 '22 01:10

Bill Garrison


The following method in your UITableViewCell subclass should quickly and concisely align both textLabel and detailTextLabel to the top of the cell (nod to Bill's code), without adding any custom views.

- (void) layoutSubviews 
{
    [super layoutSubviews]; 

    // Set top of textLabel to top of cell
    CGRect newFrame = self.textLabel.frame;
    newFrame.origin.y = CGRectGetMinY (self.contentView.bounds);
    [self.textLabel setFrame:newFrame];

    // Set top of detailTextLabel to bottom of textLabel
    newFrame = self.detailTextLabel.frame;
    newFrame.origin.y = CGRectGetMaxY (self.textLabel.frame);
    [self.detailTextLabel setFrame:newFrame];
}
like image 22
Jon Conner Avatar answered Oct 21 '22 03:10

Jon Conner