以编程方式使用Autolayout的自定义UITableViewCell的布局

时间:2015-03-25 13:11:55

标签: ios objective-c uitableview autolayout

我有一个自定义的UITableViewCell在iPhone 5s上运行良好,但是由于这个预计开始,我们现在必须更新它以支持iPhone 6和iPhone 6 Plus。

我在.xib文件中使用Autolayout解决了大部分升级问题。但是这个Cell没有.xib所以我必须以编程方式完成所有操作。为清楚起见,我暂时在我有问题的标签上画了一个边框

这就是它在iPhone 5S上的外观

label with good positioning on iPhone 5s

这就是它在iPhone 6上的外观

problematic label positioning on iPhone 6

这些表格视图是可扩展/可折叠的。在屏幕截图中,您会看到它已展开。

这是相关代码:

#import "LotsDetailTableViewCell.h"
#import "MSCellAccessory.h"


@interface LotsDetailTableViewCell ()

@property (nonatomic, strong) NSArray *data;
@property (nonatomic, strong) UIView *detailView;

@end

const float LABEL_HEIGHT = 22.0f;

@implementation LotsDetailTableViewCell

- (id)initWithData:(NSArray *)data reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    if (self) {
        self.data = data;
        [self setupDetailView];
        self.textLabel.textColor = RGB(39, 143, 191);
        self.textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:16.0];

        self.autoresizingMask = UIViewAutoresizingFlexibleHeight;
        self.clipsToBounds = true;

    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];

    self.textLabel.frame = CGRectMake(self.textLabel.frame.origin.x,
                                      self.textLabel.frame.origin.y,
                                      self.textLabel.frame.size.width,
                                      43.5f);
   self.accessoryView.frame = CGRectMake(self.accessoryView.frame.origin.x, 6, 32, 32);
}

#pragma mark Detail View

- (void)setupDetailView
{
    self.detailView = [[UIView alloc] init];

    UILabel *previousLabel;

    for (NSObject *detail in self.data) {
        CGFloat frameY;
        NSString *keyText;
        NSString *valueText;

        if([detail isKindOfClass:[NSString class]]){
            keyText = @"";
            valueText = (NSString *)detail;
        } else if([detail isKindOfClass:[NSDictionary class]]){
            keyText = ((NSDictionary *)detail)[@"key"];
            valueText = [(NSDictionary *)detail objectForKey:@"value"];
        }

        if (previousLabel) {
            frameY = previousLabel.frame.origin.y+previousLabel.frame.size.height;
        } else {
            frameY = self.frame.origin.y+44.0f+[self.data indexOfObject:detail]*LABEL_HEIGHT;
        }

        UILabel *keyLabel = [[UILabel alloc] initWithFrame:
                             CGRectMake(self.frame.size.width*.05, frameY, self.frame.size.width*.45, [self heightForValueLabel:valueText])
                             ];

        UILabel *valueLabel = [[UILabel alloc] initWithFrame:
                           CGRectMake(self.frame.size.width*.5, frameY, self.frame.size.width*.45, [self heightForValueLabel:valueText])
                           ];

        valueLabel.lineBreakMode = NSLineBreakByWordWrapping;
        valueLabel.numberOfLines = 0;
        keyLabel.lineBreakMode = NSLineBreakByWordWrapping;
        keyLabel.numberOfLines = 0;
        valueLabel.textAlignment = NSTextAlignmentRight;
        keyLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0];
        valueLabel.font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:12.0];
        keyLabel.textColor = RGB(119, 119, 119);
        valueLabel.textColor = RGB(39, 143, 191);
        keyLabel.text = keyText;
        valueLabel.text = valueText;

        valueLabel.layer.borderColor = [RGB(178, 178, 178) CGColor];
        valueLabel.layer.borderWidth = 1.0f;

        [self.detailView addSubview:keyLabel];
        [self.detailView addSubview:valueLabel];


        previousLabel = valueLabel;
    }

    [self addSubview:self.detailView];
}

- (CGFloat)heightForValueLabel:(NSString *)text
{
    return [[self class] heightForValueLabel:text width:self.frame.size.width*.5];
}

+ (CGFloat)heightForValueLabel:(NSString *)text width:(CGFloat)width
{
    CGSize constrainedSize = CGSizeMake(width, CGFLOAT_MAX);

    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [UIFont fontWithName:@"HelveticaNeue-Bold" size:12.0], NSFontAttributeName,
                                          nil];

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:text attributes:attributesDictionary];

    CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];

    return requiredHeight.size.height+5.0;
}

+ (CGFloat)heightForData:(NSArray*)data width:(CGFloat)width
{
    CGFloat height = 0.0;

    for (NSObject *detail in data) {
        NSString *valueText;

        if([detail isKindOfClass:[NSString class]]){
            valueText = (NSString *)detail;
        } else if([detail isKindOfClass:[NSDictionary class]]){
            valueText = ((NSDictionary *)detail)[@"value"];
        }

        height = height + [self heightForValueLabel:valueText width:width];
    }

    return height;
}

@end

大多数相关绘图都是在setupDetailView方法中执行的。

我想要的是什么:我希望标签与tableviewcell的右侧对齐,以充分利用可用空间。

我尝试了什么:我已尝试使用以下协议在NSLayoutConstraint中以编程方式添加setupDetailView

NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:valueLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:1.0];

[valueLabel addConstraint:constraint];

[self needsUpdateConstraints];

但这会导致运行时崩溃。

如果没有Autolayout我的问题很容易解决,我并不反对。

修改:如果我尝试添加约束,这就是它的爆炸方式:

2015-03-25 14:19:04.047 App[35277:10763464] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7faf5049a630 UILabel:0x7faf50499a50'8050'.trailing == LotsDetailTableViewCell:0x7faf50494840'LotDetailInfoCell'.right + 1>
    When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
2015-03-25 14:19:04.048 App[35277:10763464] View hierarchy unprepared for constraint.
    Constraint: <NSLayoutConstraint:0x7faf5049a630 UILabel:0x7faf50499a50'8050'.trailing == LotsDetailTableViewCell:0x7faf50494840'LotDetailInfoCell'.right + 1>
    Container hierarchy: 
<LotsDetailTableViewCell: 0x7faf50494840; baseClass = UITableViewCell; frame = (0 0; 320 44); layer = <CALayer: 0x7faf50494c50>>
   | <UITableViewCellContentView: 0x7faf5049c070; frame = (0 0; 320 44); gestureRecognizers = <NSArray: 0x7faf504a2530>; layer = <CALayer: 0x7faf50455b60>>
    View not found in container hierarchy: <UILabel: 0x7faf50499a50; frame = (156 44; 144 19.304); text = '8050'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7faf50499870>>
    That view's superview: NO SUPERVIEW
2015-03-25 14:19:04.058 App[35277:10763464] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view.  Does the constraint reference something from outside the subtree of the view?  That's illegal. constraint:<NSLayoutConstraint:0x7faf5049a630 UILabel:0x7faf50499a50'8050'.trailing == LotsDetailTableViewCell:0x7faf50494840'LotDetailInfoCell'.right + 1> view:<LotsDetailTableViewCell: 0x7faf50494840; baseClass = UITableViewCell; frame = (0 0; 320 44); layer = <CALayer: 0x7faf50494c50>>'

2 个答案:

答案 0 :(得分:1)

将leftConstarint添加到您的标签后,将其添加为subView。

[_label setTranslatesAutoresizingMaskIntoConstraints:NO];


NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:_label
                                                                  attribute:NSLayoutAttributeLeft
                                                                  relatedBy:NSLayoutRelationEqual
                                                                     toItem:[_label superview]
                                                                  attribute:NSLayoutAttributeLeft
                                                                 multiplier:1.0
                                                                   constant:10];

[_label addConstraint:leftConstraint];

[_ label superview]必须是你的[自我视图]。 //可能。 希望能帮助到你。 :)

答案 1 :(得分:0)

在子视图和superview之间添加约束时,必须将它们添加到superview:

NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:valueLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:1.0];

[self addConstraint:constraint];

[self needsUpdateConstraints];