Ios UILabel&x2B;自动布局=基线对齐中的错误

Ios UILabel&x2B;自动布局=基线对齐中的错误,ios,autolayout,nsattributedstring,Ios,Autolayout,Nsattributedstring,我在对齐两个标签时遇到问题。两个例子来说明这个问题 示例1(确定) 例2(挪威克朗) 在这两个示例中,布局约束如下所示 [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-bigMargin-[leftLabel]-bigMargin-[rightLabel]-bigMargin-|" options:NSLayoutFormatAlig

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

示例1(确定)

例2(挪威克朗)

在这两个示例中,布局约束如下所示

[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-bigMargin-[leftLabel]-bigMargin-[rightLabel]-bigMargin-|"
                                        options:NSLayoutFormatAlignAllBaseline
                                        metrics:metrics
                                          views:views];
问题在于正确的标签,当文本是属性文本时,它被绘制在下面一点,如图所示,对齐结果错误

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

编辑:

我已经创建了一个测试。这里的问题是,即使没有NSAttributedString,我也有这个问题!查看带有编号的标签,其与说明和金额不正确对齐

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

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

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

现在,看附件中的图片

在第一个示例中,我保留了示例项目的所有默认值。 您可以看到“日”标签和“描述”标签完全对齐:Autolayout将两个标签的边界对齐(仅为视图,其中包含其他私有子视图)。
但是,字体大小是不同的。日期标签是默认系统大小(17),用于您指定的描述20。
现在,如果两个标签对齐,文本在标签中垂直居中,字体大小不同,显然两个文本的基线不会对齐

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

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

  • 在所有标签中使用相同的字体(大小相同)
  • 如果要使用不同种类/大小的字体,必须更改标签的位置/大小,以使基线正确对齐
最后一点可以通过一些计算完成,您可以在这里找到一些示例:

编辑

好的,我举个例子。 第一件事:看来,在链接张贴,有一个错误<代码>上升+下降+1等于
线宽
,而不是
点大小
。我请作者改正它

因此,您可以将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;

}
//请注意:您可能需要一些错误检查
-(UIView*)基线布局视图
{
//创建视图如果不存在,则从rect zero开始
如果(!self.baselineView)
{
self.baselineView=[[UIView alloc]initWithFrame:CGRectZero];
self.baselineView.backgroundColor=[UIColor clearColor];
[self addSubview:self.baselineView];
}
//这是标签的总高度
浮动视图高度=self.bounds.size.height;
//计算文本上方的空间
float spaceovertext=(viewHeight-self.font.lineHeight)/2;
//这是我们要对齐的视图的高度
float baselineViewHeight=spaceovertext+self.font.ascender+1;

//如果是26.6545(例如),视图的高度为26.0。这不好,因此我们必须正确地对数字进行四舍五入(如果>0.5,则为上限值;如果我认为UIKit和核心文本布局有点不同,则为下限值……一个快速修复方法是在两个标签中都使用属性字符串,我想是的,我认为这是解决方案,但我更愿意使用这些方法来解决这个问题。这看起来像是UIKit错误。没有多少工作要做,但归档。不同大小的标签也有类似的问题。真的,我无法重现这个问题。两种情况下标签都正确对齐。你能发布更多的代码或示例项目吗?我会检查解决方法,但这看起来真的很奇怪。那么,自动布局的作用是什么?我必须计算字体方面的内容吗这看起来真的很尴尬。自动布局工作正常,因为视图的边界是对齐的。自动布局的目的是布局组件,在这种情况下,组件是UILabel,而不是UILabel的内容。此外,如果您希望有类似的成就将UILabel作为标准,您将如何实现它?既然UILabel支持多行,您将如何以“标准方式”将多行标签的基线与两种不同的字体大小对齐?也许我完全误解了什么是基线对齐,但我认为“NSLayoutFormatAlignAllBaseline–此选项根据视图的基线排列视图,对于基于文本的视图,基线是不向下的文本底部(如g、p、j等)。对于非文本视图,基线与底边相同“
NSLayoutFormatAlignAllBaseline
旨在与
viewForBaselineLayout
一起工作,并且应该完全按照您的要求进行操作。但是,不幸的是,它不是由UILabel实现的(它使用默认实现,返回self,而不是基线对齐的子视图).我不认为这是一个bug,但出于我在上一篇评论中解释的原因,这是预期的行为。好吧,我终于明白了…这完全令人失望。您认为避免这种情况的更好解决方案是什么?我正在考虑对
UILabel
进行子类化(我已经出于其他目的这样做了)要返回正确的
viewForBaselineLayout
,请考虑使用下降器和框架进行的计算。我只是在写我的想法,我必须测试是否可能…非常感谢您
- (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;
}
// 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;

}