1

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!

Community
  • 1
  • 1
sethfri
  • 1,307
  • 1
  • 15
  • 31
  • What padding? Your pictures aren't very informative since you can't see the labels. – rdelmar Dec 01 '13 at 04:48
  • @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 Dec 01 '13 at 05:47
  • 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 Dec 01 '13 at 06:04
  • The images aren't loading. We're getting 403 errors from Dropbox. – Brian Nickel Dec 01 '13 at 06:09
  • @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 Dec 01 '13 at 06:15
  • @rdelmar Wanted to make sure you were notified too that the images should now be there. – sethfri Dec 01 '13 at 06:15
  • The links are good. I see the same thing when I test your code, except that I have to add 2 to height to get it to work in portrait. – rdelmar Dec 01 '13 at 06:16
  • @rdelmar In addition to the 5 (kImageViewPadding) or instead of? – sethfri Dec 01 '13 at 06:18
  • Instead of. But in my tests, the image view size never comes into play anyway. – rdelmar Dec 01 '13 at 06:21
  • @rdelmar Right, that shouldn't be the issue. I was just using the same padding from the Name Label constraint (Top Space to: Superview Equals: 5). – sethfri Dec 01 '13 at 06:22
  • Have you tried what I suggested in my answer? – rdelmar Dec 01 '13 at 06:52

2 Answers2

0

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;
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • This seems to work! I hate to rely on these magic numbers though, especially since I have so little understanding of why this is required to work. Do you have any suggestions for a more dynamic approach, in case the layout were to later change slightly? – sethfri Dec 01 '13 at 06:58
  • @sethfri, well, I wouldn't call it a magic number, since it's based on the constraints you set up in IB, but no, I don't know how to do it without that number. I'm checking out some other possibilities now. – rdelmar Dec 01 '13 at 07:02
  • What I mean by "magic number" is it's a static number, whereas something like `tableView.bounds.size.width` is more dynamic (than, say, 320). – sethfri Dec 01 '13 at 07:07
  • @sethfri, I've updated my answer to get rid of that "magic" number -- the 105 anyway, but the 2 still remains. I've also explained a subtle error that I noticed when dequeuing the cell in heightForRowAtIndexPath. – rdelmar Dec 01 '13 at 16:39
0

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;
Brian Nickel
  • 26,890
  • 5
  • 80
  • 110
  • Are you talking about including this in `tableView:heightForRowAtIndexPath:`? If so, this solution doesn't work. Nowhere in the code is an explicit width of 320 being used. – sethfri Dec 01 '13 at 06:31
  • The 320 width would be coming from the initial size taken from the xib. If you resize the xib to 480, you should see it look good in landscape but wrong in portrait. I updated my answer because you are pulling from contentView, not cell itself. – Brian Nickel Dec 01 '13 at 06:40
  • Including this code results in the same issue, regardless of whether or not you're using `contentView` or the cell itself. – sethfri Dec 01 '13 at 06:43