18

我在对齐两个标签时遇到问题。两个例子来说明问题

示例 1(确定)

[leftLabel setText:@"03"];
[rightLabel setText:@"Description3"];

在此处输入图像描述

示例 2(挪威克朗)

[leftLabel setText:@"03"];
[rightLabel setAttributedText:[[NSAttributedString alloc] initWithString:@"Description3"]];

在此处输入图像描述

在这两个示例中,布局约束都是这样

[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-bigMargin-[leftLabel]-bigMargin-[rightLabel]-bigMargin-|"
                                        options:NSLayoutFormatAlignAllBaseline
                                        metrics:metrics
                                          views:views];

问题是正确的标签,当文本是一个属性时,它被绘制在下面一点,如图所示,并且对齐结果错误。

为什么?我可以使用UIlabel这两种方法来解决这个问题吗?

编辑:

在 GitHub 中创建了一个项目,并对此进行了测试。这里的问题是,即使没有 NSAttributdString,我也遇到了问题!看标签上的数字,是不是与描述和金额正确对齐。

在此处输入图像描述

我在这里粘贴了单元格的代码,但必须在项目中看到整个场景。

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        
        UIView *contentView = [self contentView];
        
        [contentView setBackgroundColor:[UIColor clearColor]];
        
        dayLabel_ = [[UILabel alloc] initWithFrame:CGRectZero];
        [dayLabel_ setTranslatesAutoresizingMaskIntoConstraints:NO];
        [contentView addSubview:dayLabel_];
        
        monthLabel_ = [[UILabel alloc] initWithFrame:CGRectZero];
        [monthLabel_ setTranslatesAutoresizingMaskIntoConstraints:NO];
        [monthLabel_ setFont:[UIFont boldSystemFontOfSize:13.0f]];
        [contentView addSubview:monthLabel_];
        
        descriptionLabel_ = [[UILabel alloc] initWithFrame:CGRectZero];
        [descriptionLabel_ setTranslatesAutoresizingMaskIntoConstraints:NO];
        [descriptionLabel_ setFont:[UIFont systemFontOfSize:20.0f]];
        [contentView addSubview:descriptionLabel_];
        
        conceptLabel_ = [[UILabel alloc] initWithFrame:CGRectZero];
        [conceptLabel_ setTranslatesAutoresizingMaskIntoConstraints:NO];
        [conceptLabel_ setLineBreakMode:NSLineBreakByTruncatingTail];
        [conceptLabel_ setFont:[UIFont systemFontOfSize:12.0f]];
        [contentView addSubview:conceptLabel_];
        
        amountLabel_ = [[UILabel alloc] initWithFrame:CGRectZero];
        [amountLabel_ setTranslatesAutoresizingMaskIntoConstraints:NO];
        [contentView addSubview:amountLabel_];
        
        // Constraints
        
        NSDictionary *views = NSDictionaryOfVariableBindings(contentView, dayLabel_, monthLabel_, descriptionLabel_, conceptLabel_, amountLabel_);
        NSDictionary *metrics = @{ @"bigMargin" : @12 };
        
        [descriptionLabel_ setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
        [conceptLabel_ setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
                
        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-bigMargin-[dayLabel_][monthLabel_]"
                                                                            options:NSLayoutFormatAlignAllLeading
                                                                            metrics:metrics
                                                                              views:views]];
        
        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-bigMargin-[descriptionLabel_][conceptLabel_]"
                                                                            options:NSLayoutFormatAlignAllLeading
                                                                            metrics:metrics
                                                                              views:views]];
        
        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-bigMargin-[dayLabel_]-bigMargin-[descriptionLabel_]-(>=bigMargin)-[amountLabel_]-bigMargin-|"
                                                                            options:NSLayoutFormatAlignAllBaseline
                                                                            metrics:metrics
                                                                              views:views]];
        
        [contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-bigMargin-[monthLabel_]-bigMargin-[conceptLabel_]-bigMargin-|"
                                                                            options:NSLayoutFormatAlignAllBaseline
                                                                            metrics:metrics
                                                                              views:views]];
    }
    
    return self;
}
4

1 回答 1

12

好的,通过最后一个示例,问题更清楚了。您必须知道的第一件事:UILabel 中的文本默认情况下在标签的中心垂直对齐。
AFAIK 您无法更改垂直对齐以使文本与基线对齐。

现在,看看附加的图像。

比较

在第一个示例中,我保留了示例项目的所有默认值。您可以看到日期标签和描述标签完美对齐:自动布局对齐两个标签的边界(即简单的视图,内部包含其他私有子视图)。
但是,字体大小不同。对于 day label 是默认的系统大小(17),对于你指定的 description 20。
现在,如果两个标签对齐,则文本在标签中垂直居中,字体大小不同,显然是基线两个文本不会对齐。

在第二个示例中,我使用了相同的字体大小,您可以看到对齐方式是正确的。

因此,可能的解决方案有两种:

  • 在所有标签中使用相同的字体(具有相同的大小)
  • 如果要使用不同种类/大小的字体,则必须更改标签的位置/大小以使基线正确对齐

最后一点可以通过一些计算来完成,您可以在此处找到一些示例:链接

编辑

好的,我发布一个示例。第一件事:似乎在发布的链接中存在错误。ascender + descender + 1等于lineHeight,而不是pointSize。我要求作者更正它。

因此,您可以继承 UILabel,覆盖 viewForBaselineLayout 并执行类似的操作。您必须添加一个baselineView实例变量并将其添加为 的子视图UILabel,因为 AutoLayout 希望视图对齐为标签的子视图。

// please note: you may need some error checking
- (UIView *)viewForBaselineLayout
{
    // create the view if not exists, start with rect zero
    if (!self.baselineView)
    {
        self.baselineView = [[UIView alloc] initWithFrame:CGRectZero];
        self.baselineView.backgroundColor = [UIColor clearColor];
        [self addSubview:self.baselineView];
    }

    // this is the total height of the label
    float viewHeight = self.bounds.size.height;

    // calculate the space that is above the text
    float spaceAboveText = (viewHeight - self.font.lineHeight) / 2;

    // this is the height of the view we want to align to
    float baselineViewHeight = spaceAboveText + self.font.ascender + 1;

    // if you have 26.6545 (for example), the view takes 26.0 for the height. This is not good, so we have to round the number correctly (to the upper value if >.5 and to the lower if <.5)
    int integerBaseline = (int)(baselineViewHeight + 0.5f);

    // update the frame of the view
    self.baselineView.frame = CGRectMake(0, 0, self.bounds.size.width, (float)integerBaseline);


    return self.baselineView;

}

使用这种方法,您必须处理几件事情:

  • 标签在调用 autolayout 时必须有大小,viewForBaselineLayout之后其高度不得改变。因此,您必须在布局过程发生之前使标签适合其内容大小。
  • 你已经根据上面的建议调整了你的约束(现在你正在修复,例如,dayLabel 和 monthLabel,但你不能因为 dayLabel 现在必须“浮动”)。

我为您的项目附加了一些用于调试的更新和颜色,仅使用 dayLabel 和 descriptionLabel(我删除了其他)和更新的约束。

错误的布局已更新

于 2013-09-03T08:53:48.303 回答