Ширина ячейки UITableView на iOS 8 застряла на 320pt

В настоящее время я пытаюсь создать простой UITableView с пользовательскими ячейками без использования раскадровки.

Я получаю проблему на симуляторе iPhone 6, где табличное представление имеет ширину 375 (как и должно быть), но ячейки внутри получают ширину 320.

Число 320 нигде не встречается в проекте, так как я его не жестко кодирую. Когда я устанавливаю цвет фона ячейки, он расширяет всю ширину 375, но мне нужно выровнять изображение вправо, которое выравнивает только 320, как показано на фотографии ниже.

image of weird tableviewcell sizing

Я не уверен, что это потому, что я пропускаю ограничения или если есть ошибка. Любая помощь приветствуется, спасибо!

Код для настройки таблицы:

- (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;
}

Завершить 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 messages
    if ([[TBConfig userID] isEqualToString:self.message.user.userID]) {
        // Set avatar to left if it 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];
}

Ответы

Ответ 1

Вы печатаете размер ячейки до того, как она будет добавлена ​​на дисплей - до того, как она будет изменена. Кому вы представляете, будет ли размер ячейки? С чего, как вы думаете, должно получить ширину экрана таблицы? Он даже не знает, какой вид таблицы он будет передан.

Ячейкам будет присвоен соответствующий кадр при добавлении на дисплей.

EDIT: oh, и вы, вероятно, не хотите, чтобы cellIdentifier был static. Вероятно, вы хотели *const.

Ответ 2

Не знаю, нашли ли вы ответ. У меня возникла та же проблема, когда я пытался подклассифицировать UITableViewCell и добавлять пользовательские подпрограммы программно, не используя xib.

Наконец, решение для меня помогло использовать [[UIScreen mainScreen] applicationFrame] вместо self.frame при расчете кадров subviews.

Ответ 3

Правильный способ решить эту задачу - выполнить макет в методе layoutSubviews.

В вашем случае просто вызовите "populateSubviews" в методе "layoutSubviews", например:

- (void)layoutSubviews {

    [super layoutSubviews];
    [self populateSubviews];

}

... но прежде чем делать это, я бы рекомендовал, чтобы вы размещали контент в отдельном методе (т.е. вызывали label.text =...) и размещали все вызовы, влияющие на макет (т.е. label.frame =...) ниже [super layoutSubviews] в методе выше.

Это приведет к чему-то вроде:

- (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 ..
}

Ответ 4

После того, как вы установили кадр аватара в:

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

написать

self.avatar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;

Он должен закрепить изображение аватара на правом поле и оставить левое поле гибким.

Ответ 5

Выберите своего инспектора сотовой связи со списком таблиц и проверьте, не ошибочно ли вы подключили editAccessoryView

введите описание изображения здесь

Ответ 6

Хотя этот ответ может быть не таким простым, как вы ожидаете, UIKit будет доставлять из-за ошибки в UITableView - когда вы делаете что-то программно - вы должны заразиться руками. Любители Xib - будьте предупреждены - этот ответ не для вас. Вероятно, он просто работает. В более поздних версиях IOS позвольте надеяться, что эта проблема будет решена. Проблема заключается в том, что uitableview жестко кодирует размеры ячейки до 320. В одном проекте у меня было - я заставлял размер кадра исправлять это. Нотабене это имеет проблемы с контроллером splitview на 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);

}

Другая опция - лучше работать для меня - это переменная локальной ширины для ссылки. Я знаю, что этот код не идеален, но он работает последовательно.

@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

тогда у вас есть возможность сделать TBMessageViewCell подклассом этого AbstractCustomCell, который будет иметь эту переменную для вас. Вместо использования self.contentView.bounds.size.width/self.bounds.size.width просто используйте ширину.

Ответ 7

Я программирую макет без Storyboard и сталкиваюсь с той же проблемой, и мне нужно вернуть ячейку на cellForRowAt indexPath, которая в свою очередь еще не смогла получить размер контейнера.

И мне удастся его решить, переместив коды макета в делегат willDisplay cell.