Home » Ios » UITableView cell width on iOS 8 stuck at 320pt

UITableView cell width on iOS 8 stuck at 320pt

Posted by: admin February 24, 2018 Leave a comment

Questions:

I am currently trying to create a simple UITableView with custom cells without using storyboard.

I’m getting an issue on the iPhone 6 simulator where the table view has a width of 375 (as it should), but the cells inside are getting a width of 320.

The number 320 is nowhere to be found in the project as I am not hard coding it. When I am setting the background colour of the cell, it extends the full width of 375, but I need to align an image to the right, which only aligns 320 across as shown in the photo below.

image of weird tableviewcell sizing

I’m not sure if it’s because I’m missing constraints or if there’s a bug. Any help is appreciated, thanks!

Code to set up table:

- (TBMessageViewCell *)getMessageCellforTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath
{

    static NSString *cellIdentifier = @"MessageCell";
    TBMessageViewCell *cell = (TBMessageViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil) {
        cell = [[TBMessageViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
        [cell createSubviews];
    }

     // Set the new message and refresh
    [cell setMessage:self.viewModel.messages[indexPath.row]];
    [cell populateSubviews];
    cell.backgroundColor = [UIColor blueColor];

    NSLog(@"cell Width: %f", cell.contentView.frame.size.width);

    return cell;
}

Complete TBMessageViewCell:

@implementation TBMessageViewCell

const CGFloat MARGIN = 10.0f;
const CGFloat AVATAR_SIZE = 40.0f;

-(id)initWithStyle:(UITableViewCellStyle *)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if(self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]){
    }

// Sets background and selected background color
self.backgroundColor = [UIColor clearColor];
UIView *selectionColor = [[UIView alloc] init];
selectionColor.backgroundColor = [UIColor clearColor];
self.selectedBackgroundView = selectionColor;

return self;
}

- (void)populateSubviews
{
    // Set the message body
    [self.messageBodyLabel setText:self.message.body];
    [self.messageBodyLabel setTextAlignment:NSTextAlignmentRight];
    CGRect bodyFrame = CGRectMake(MARGIN, MARGIN, self.frame.size.width - (AVATAR_SIZE + (MARGIN * 3)), self.frame.size.height);
    // Calculates the expected frame size based on the font and dimensions of the label
    // FLT_MAX simply means no constraint in height
    CGSize maximumLabelSize = CGSizeMake(bodyFrame.size.width, FLT_MAX);
    CGRect textRect = [self.message.body boundingRectWithSize:maximumLabelSize
    options:NSStringDrawingUsesLineFragmentOrigin
    attributes:@{NSFontAttributeName:self.messageBodyLabel.font}
    context:nil];
    bodyFrame.size.height = textRect.size.height;

    // Setup the new avatar frame (Right aligned)
    CGRect avatarFrame = CGRectMake(bodyFrame.size.width + (MARGIN * 2), MARGIN, AVATAR_SIZE, AVATAR_SIZE);

    // Align to the LEFT side for current user's messages
    if ([[TBConfig userID] isEqualToString:self.message.user.userID]) {
        // Set avatar to left if it's me
        avatarFrame.origin.x = MARGIN;
        bodyFrame.origin.x = AVATAR_SIZE + (MARGIN * 2);
        [self.messageBodyLabel setTextAlignment:NSTextAlignmentLeft];
    }

    self.avatar.frame = avatarFrame;
    self.avatar.layer.cornerRadius = self.avatar.frame.size.width/2;
    self.messageBodyLabel.frame = bodyFrame;

    // Set the new cell height on the main Cell
    CGFloat cellHeight = MAX(bodyFrame.size.height, self.frame.size.height) + MARGIN;
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, cellHeight);

    // Set the new Profile avatar
    if (![self.avatar.profileID isEqualToString:self.message.user.facebookID]) {
        [self.avatar setProfileID:nil];
        [self.avatar setProfileID:self.message.user.facebookID];
    }
}

- (void)createSubviews
{
    self.messageBodyLabel = [[UILabel alloc] init];
    self.messageBodyLabel.textColor = [UIColor whiteColor];
    self.messageBodyLabel.lineBreakMode = NSLineBreakByWordWrapping;
    self.messageBodyLabel.numberOfLines = 0;
    [self addSubview:self.messageBodyLabel];

    // Creates the avatar
    self.avatar = [[FBProfilePictureView alloc] init];
    [self.avatar setPictureCropping:FBProfilePictureCroppingSquare];
    [self addSubview:self.avatar];
}
Answers:

You’re printing the size of the cell before it has been added to the display — before it has been sized. Who are you imagining will have sized the cell? Where do you think ot is supposed to get the table view width from? It doesn’t even know which table view it’s going to be handed to yet.

The cells will be given an appropriate frame when added to the display.

EDIT: oh, and you probably don’t want that cellIdentifier to be static. You probably wanted *const.

Questions:
Answers:

Don’t know if you have found the answer. I faced the same problem when I was trying to subclass UITableViewCell and add custom subviews programmatically without using xib.

Finally the solution worked for me is to use [[UIScreen mainScreen] applicationFrame] instead of self.frame when calculating subviews’ frames.

Questions:
Answers:

The proper way to solve this is to perform your layout in the layoutSubviews method.

In your case, simply call “populateSubviews” within “layoutSubviews” method, like this:

- (void)layoutSubviews {

    [super layoutSubviews];
    [self populateSubviews];

}

… but before doing this I would recommend that you do content population in a separate method (ie, calls to label.text = …), and place all layout-affecting calls (ie, label.frame = …) below [super layoutSubviews] in the method above.

That would result in something like:

- (void)layoutSubviews {

    [super layoutSubviews];

    CGRect bodyFrame = CGRectMake(MARGIN, MARGIN, self.frame.size.width - (AVATAR_SIZE + (MARGIN * 3)), self.frame.size.height);

    // Calculates the expected frame size based on the font and dimensions of the label
    // FLT_MAX simply means no constraint in height
    CGSize maximumLabelSize = CGSizeMake(bodyFrame.size.width, FLT_MAX);
    CGRect textRect = [self.message.body boundingRectWithSize:maximumLabelSize
    options:NSStringDrawingUsesLineFragmentOrigin
    attributes:@{NSFontAttributeName:self.messageBodyLabel.font}
    context:nil];

    bodyFrame.size.height = textRect.size.height;

    // .. the rest of your layout code here ..

}

- (void)populateSubviews {

    [self.messageBodyLabel setText:self.message.body];

    // .. the rest of your code here ..
}

Questions:
Answers:

After you set your avatar frame in:

self.avatar.frame = avatarFrame;
self.avatar.layer.cornerRadius = self.avatar.frame.size.width/2;
self.messageBodyLabel.frame = bodyFrame;

write

self.avatar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;

It should hook your avatar image to the right margin and leave the left margin as flexible.

Questions:
Answers:

Select your table view cell’s connection inspector and check if you haven’t connected editingAccessoryView by mistake

enter image description here

Questions:
Answers:

While this answer may not be as straightforward as you’d expect UIKit to deliver, due to a bug in UITableView – when doing things programmatically – you gotta get your hands dirty. Xib lovers – be warned – this answer isn’t for you. It’s probably just working.
In later IOS versions, let’s hope this problem get resolved. The problem is uitableview is hardcoding the dimensions of the cell to 320. In one project I had – I was forcing the frame size to to fix this. N.B. this has problems with splitview controller on iPad.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
 NSLog(@"cell frame width:%f",cell.frame.size.width); // you should see <- 320! WTH

// you can crudely correct this here
cell.frame = CGRectMake(0,0,[[UIScreen mainScreen] applicationFrame].size.width,CELL_HEIGHT);

}

Another option – that’s working better for me is a local width variable to reference. I know this code is not ideal – but it works consistently.

@interface AbtractCustomCell : UITableViewCell {
    float width;
}
@end


@implementation AbtractCustomCell
    - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
        self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
        if (self) {
            //optimise speed
            // [self setOpaque:YES];
            if ([SDiPhoneVersion deviceSize] == iPhone47inch) {
                width = 375;
            }
            else if ([SDiPhoneVersion deviceSize] == iPhone47inch) {
                width = 414;
            }
             else {
                width = 320;         // hardcode iphone 4/5 /ipad2
            }
        }
        return self;
    }

@end

then you have the option to make TBMessageViewCell a subclass of this AbstractCustomCell which will have this variable there for you. Instead of using self.contentView.bounds.size.width / self.bounds.size.width just use width.

Questions:
Answers:

I’m programmatically layout without Storyboard and facing same issue, and I need to return the cell upon cellForRowAt indexPath which in turn it still didnt manage to get the container size yet.

And I manage to solve it by move the layout codes into willDisplay cell delegate.