1
votes

I am building a very simple App.net client as a sort of test app. It simply pulls the latest 20 posts from the App.net public timeline and displays the post, the user's name, and the user's avatar in a UITableView. Simple enough.

For the UI, I am using a storyboard and Auto Layout.

UIImageView Constraints

  • Height Equals: 69
  • Width Equals: 69
  • Align Top to: Label - Name (the username label)
  • Leading Space to: Superview Equals: 3
  • Trailing Space to: Label - Name Equals: 8
  • Trailing Space to: Label - Text (the text of the post) Equals: 8

Name Label Constraints

  • Align Top to: Image View
  • Top Space to: Superview Equals: 5
  • Trailing Space to: Superview Equals: 17
  • Leading Space to: Image View Equals: 8
  • Bottom Space to: Label - Text Equals: 6

Text Label Constraints

  • Bottom Space to: Superview Equals: 9
  • Trailing Space to: Superview Equals: 17
  • Top Space to: Label - Name Equals: 6
  • Leading Space to: Image View Equals: 8

Finally, to set the height of each cell, I am using the following code in tableView:heightForRowAtIndexPath::

// Solution greatly helped by http://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-heights
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    static ADNPostTableViewCell *cell;

    if (!cell) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"PostCell"];
    }

    cell.post = self.posts[indexPath.row];

    return fmax([cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height, cell.avatarImageView.frame.size.height + kImageViewPadding); // kImageViewPadding = 5
}

For Portrait, this works wonderfully. I get the following:

Portrait image

For Landscape, however, a very peculiar padding appears (a large amount of whitespace in between the Name label and the Text label and in between the Text label and the bottom cell divider), and I have no idea how to figure out what constraint is causing this. I've tried a wide variety of constraints, but I still can't seem to get rid of the padding.

Landscape image

Thanks in advance, and please let me know if there's anything else I can provide to assist in solving this problem!

2
What padding? Your pictures aren't very informative since you can't see the labels. - rdelmar
@rdelmar I'm not sure what you mean by not being able to see the labels; the labels are plainly visible. The mystery padding I'm referring to appears in the post by Kyle Isom. There is a large amount of whitespace between his name and the post and the post and the cell divider at the bottom of the cell. Compare this to the Portrait image, in which everything appears as designed. - sethfri
Maybe you're seeing something different than I am. I see no labels in your post. I see two images that look like a white rectangle with a square in the middle with a question mark in it (one rectangle is a little longer than the other). - rdelmar
The images aren't loading. We're getting 403 errors from Dropbox. - Brian Nickel♦
@BrianNickel Oh I'm so sorry! They're, understandably, showing up for me, so I didn't realize that was a problem. I've replaced the embedded images with links to them. Let me know if they're still not appearing! - sethfri

2 Answers

0
votes

You're not updating the width of cell when the the orientation changes. I assume all your sizes are being calculated around width = 320. You should set the width before doing the calculation:

CGRect frame = cell.contentView.frame;
frame.size.width = tableView.bounds.size.width;
cell.contentView.frame = frame;
0
votes

I think you need to put in a preferred maximum width for the textLabel. I don't know why the cell can't figure this out from the constraints and the width of the cell, but that seems to be the situation. One other thing I noticed was that if you dequeue a cell in heightForRowAtIndexPath to do the calculations, you get a subtle error -- the cell looks fine in portrait, and the first time you rotate to landscape, but if you rotate back to portrait and then back to landscape again, the first row (or sometimes the first several rows depending on the string length) is too tall. This only happened when I had enough rows that you couldn't see them all, and if you scrolled the first time you went to landscape, then the height was correct the second time you went to landscape, but not the third. This seems to have something to do with cell reuse. This can be fixed by dequeuing just one cell in viewDidLoad, and using that. You can also get the value you need to pass to preferredMaxLayoutWidth by getting the label's width there. One place I still needed a "magic" number was to add 2 to the height you return in heightForRowAtIndexPath. I don't know why this is, but I've noticed it with other simple cells with just one label also. Here is what worked for me (my RDCell was set up with the same constraints you posted in your question):

@interface TableController ()
@property (strong,nonatomic) NSArray *theData;
@property (strong,nonatomic) NSString *textString;
@property (nonatomic) CGFloat labelWidthAdjuster;
@property (strong,nonatomic) RDCell *measureCell;
@end

@implementation TableController 

- (void)viewDidLoad {
    [super viewDidLoad];
    self.measureCell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell"];
    self.labelWidthAdjuster = self.tableView.bounds.size.width - self.measureCell.postLabel.bounds.size.width;
    self.textString = @"A pretty long sentence that will make the label in the custom cell in the table view have to expand to several lines long";
    self.theData = @[@"One",@"Two",@"Three",@"Four",@"Five",@"Six",@"Seven",@"Eight",@"Nine"];
    [self.tableView reloadData];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.theData.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    self.measureCell.postLabel.preferredMaxLayoutWidth = self.tableView.bounds.size.width - self.labelWidthAdjuster;
    self.measureCell.postLabel.text = self.textString;
    return [self.measureCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 2;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    RDCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    cell.nameLabel.text = self.theData[indexPath.row];
    cell.postLabel.text = self.textString;
    return cell;
}