我正在尝试创建一个动态大小的表格视图单元格。我已经阅读了每一个 SO 问题、网站、文章和示例 Github 项目,并且无法获得我想要的没有错误的布局(以当前的形式,没有错误,但最终结果如上图所示) .
我有一个包含多个部分的表格。第一部分有一个动态调整大小的单元格。我的目标是正确无误地显示此单元格。以下是细胞可能具有的两种不同的视觉状态:
这是单元格的所需外观,底部有一条消息:
这是完全没有消息的单元格的理想外观:
对于下面显示的代码,结果如下:
这是 TableViewController:
//
// The TableViewController
//
#import <Masonry.h>
#import "CustomCell.h"
#import "MyViewController.h"
@interface MyViewController()
@property (retain, nonatomic) CheckoutHeaderView *headerView;
@property (retain, nonatomic) CustomCell *customCell;
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView = [[UITableView alloc] init];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.allowsSelection = NO;
[self.tableView registerClass:[CustomCell class] forCellReuseIdentifier:@"customCell"];
[self.view addSubview:self.headerView];
[self.view addSubview:self.tableView];
[self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
}];
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.headerView.mas_bottom);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
make.bottom.equalTo(self.view);
}];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return self.customerCell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
static dispatch_once_t onceToken;
static CustomCell *customCell;
dispatch_once(&onceToken, ^{
customCell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"customCell"];
self.customCell = customerCell;
});
self.customCell.model = self.model;
return [self calculateHeightForConfiguredSizingCell:self.customCell];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
[sizingCell setNeedsUpdateConstraints];
[sizingCell updateConstraintsIfNeeded];
sizingCell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(self.tableView.bounds));
[sizingCell setNeedsLayout];
[sizingCell layoutIfNeeded];
CGFloat height = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
height += 1.0f;
return height;
}
@end
这是单元格类:
#import "CustomCell.h"
#import <Masonry.h>
#import "Label.h"
#import "Order.h"
#import "Helper.h"
#import "Theme.h"
@interface CustomCell()
@property (assign, nonatomic) BOOL didSetupConstraints;
@property (retain, nonatomic) Label *dateOneLabel;
@property (retain, nonatomic) Label *dateTwoToLabel;
@property (retain, nonatomic) Label *messageLabel;
@property (retain, nonatomic) Label *dateOneValue;
@property (retain, nonatomic) Label *dateTwoToValue;
@property (retain, nonatomic) Label *messageText;
@property (retain, nonatomic) NSMutableArray *messageConstraints;
@property (retain, nonatomic) MASConstraint *pinBottomOfDateTwoLabelToBottomOfContentViewConstraint;
@end
@implementation CustomCell
- (NSMutableArray *)messageConstraints {
return _messageConstraints ? _messageConstraints : [@[] mutableCopy];
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setup];
}
return self;
}
- (void)awakeFromNib {
[self setup];
}
- (void) setup {
self.didSetupConstraints = NO;
self.dateOneLabel = [UILabel new];
self.dateOneLabel.text = @"Date One";
self.dateTwoLabel = [UILabel new];
self.dateTwoLabel.text = @"Date Two";
self.messageLabel = [UILabel new];
self.messageLabel.text = @"Message";
self.dateOneValue = [UILabel new];
self.dateTwoToValue = [UILabel new];
// The actual message text label that spans numerous lines.
self.messageText = [UILabel new];
self.messageText.numberOfLines = 0;
self.messageText.adjustsFontSizeToWidth = NO;
[self.contentView addSubview:self.dateOneLabel];
[self.contentView addSubview:self.dateTwoToLabel];
[self.contentView addSubview:self.messageLabel];
[self.contentView addSubview:self.dateOneValue];
[self.contentView addSubview:self.dateTwoToValue];
[self.contentView addSubview:self.messageText];
}
- (void)layoutSubviews {
[super layoutSubviews];
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
self.messageText.preferredMaxLayoutWidth = CGRectGetWidth(self.messageText.frame);
}
- (void)updateConstraints {
if (!self.didSetupConstraints) {
__weak typeof (self.contentView) contentView = self.contentView;
// Topmost label, pinned to left side of cell.
[self.dateOneLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(contentView).with.offset(14);
make.right.lessThanOrEqualTo(self.dateOneValue.mas_left).with.offset(-20);
make.top.equalTo(contentView).with.offset(14);
}];
// Second label, pinned to left side of cell and below first label.
[self.dateTwoToLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.dateOneLabel);
make.top.equalTo(self.dateOneLabel.mas_bottom).with.offset(6);
make.right.lessThanOrEqualTo(self.dateTwoToValue.mas_left).with.offset(-20);
}];
// First date value, pinned to right of cell and baseline of its label.
[self.dateOneValue mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(contentView).with.offset(-14).priorityHigh();
make.baseline.equalTo(self.dateOneLabel);
}];
// Second date value, pinned to right of cell and baseline of its label.
[self.dateTwoToValue mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.dateOneValue);
make.baseline.equalTo(self.dateTwoToLabel);
}];
self.didSetupConstraints = YES;
}
[super updateConstraints];
}
- (void)uninstallMessageConstraints {
[self.pinBottomOfDateTwoLabelToBottomOfContentViewConstraint uninstall];
for (MASConstraint *constraint in self.messageConstraints) {
[constraint uninstall];
}
[self.contentView mas_remakeConstraints:^(MASConstraintMaker *make) {
self.pinBottomOfDateTwoLabelToBottomOfContentViewConstraint = make.bottom.equalTo(self.dateTwoToLabel).with.offset(14);
}];
}
- (void)installMessageConstraints {
__weak typeof (self.contentView) contentView = self.contentView;
[self.pinBottomOfDateTwoLabelToBottomOfContentViewConstraint uninstall];
// Below, add constraints of `self.messageConstraints` into an array so
// they can be removed later.
[self.messageConstraints addObjectsFromArray:[self.messageLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.dateOneLabel);
make.top.equalTo(self.dateTwoToLabel.mas_bottom).with.offset(6);
}]];
self.messageConstraints = [[self.messageText mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.messageLabel);
make.top.equalTo(self.messageLabel.mas_bottom).with.offset(6);
make.right.equalTo(contentView).with.offset(-14);
}] mutableCopy];
[contentView mas_makeConstraints:^(MASConstraintMaker *make) {
self.pinBottomOfDateTwoLabelToBottomOfContentViewConstraint = make.bottom.equalTo(self.messageText).with.offset(14);
}];
}
- (void)setModel:(MyModel *)model {
if (!model.message || model.message.length < 1) {
[self uninstallMessageConstraints];
self.messageText.text = @"";
[self.messageLabel removeFromSuperview];
[self.messageText removeFromSuperview];
} else {
self.messageText.text = model.message;
if (![self.contentView.subviews containsObject:self.messageLabel]) {
[self.contentView addSubview:self.messageLabel];
}
if (![self.contentView.subviews containsObject:self.messageText]) {
[self.contentView addSubview:self.messageText];
}
[self installMessageConstraints];
}
self.dateOneValue.text = model.dateOne;
self.dateTwoValue.text = model.dateTwo;
[self.contentView setNeedsDisplay];
[self.contentView setNeedsLayout];
}
@end
我已经对此进行了两天的修改,在某些时候,它看起来符合预期,但出现了自动布局错误。我不知道我的错误在哪里,所以我的一般问题是:我的代码有什么问题,需要改变什么才能产生正确的结果?
非常感谢。