8

I want to center image to center Y position of first line of text of my UILabel. I use masonry to set Auto Layout constraints like that:

 [_haveReadIndicatorImgView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.equalTo(self.contentView).offset(SMALL_OFFSET);
        make.height.width.equalTo(@(8));
    }];

    [_topTxtlbl mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(_haveReadIndicatorImgView.mas_right).offset(TINY_OFFSET);
        make.top.equalTo(_haveReadIndicatorImgView.mas_top);
        make.right.equalTo(self.arrowImgView.mas_left).offset(-SMALL_OFFSET);
        make.bottom.equalTo(_dateTxtLbl.mas_top).offset(-SMALL_OFFSET);
    }];

It should be pretty strightforward. I simply attach top of UIImageView to top of my Label.

But take a look at screen.

enter image description here

Top edges of UIImageView (gray dot) and label are equal, but how to make UIImageView to be centered to first line of text like that?

enter image description here

Thanks.

Evgeniy Kleban
  • 6,794
  • 13
  • 54
  • 107
  • 2
    It may be easier to use `NSAttributedString`, with `NSParagraphStyle`, with a `firstLineHeadIndent` set to 0, and a `headIndent` set to 10 (for the rest of the text), and use either a `NSTextAttachment` (image for the bullet, which width will the the `headIndent` value) or simply directly the bullet point character `•` (or another one in the character list). – Larme Jun 13 '17 at 08:54
  • @Larme can you provide example? – Evgeniy Kleban Jun 13 '17 at 09:44
  • `NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:@"• MyLongTextHere"]; NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; [style setHeadIndent:15]; [style setFirstLineHeadIndent:0]; [attr addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, [attr length])];`? – Larme Jun 13 '17 at 09:50
  • @Larme thank you, but i achieve point with different approach, will post as an answer. – Evgeniy Kleban Jun 13 '17 at 10:29

5 Answers5

13

Actually there is a way of doing this! If you use plain old AutoLayout this can be done with the following snippet:

// Aligns the icon to the center of a capital letter in the first line
let offset = label.font.capHeight / 2.0

// Aligns the icon to the center of the whole line, which is different
// than above. Especially with big fonts this makes a visible difference.
let offset = (label.font.ascender + label.font.descender) / 2.0

let constraints: [NSLayoutConstraint] = [
  imageView.centerYAnchor.constraint(equalTo: label.firstBaselineAnchor, constant: -offset),
  imageView.trailingAnchor.constraint(equalTo: label.leadingAnchor, constant: -10)
]
NSLayoutConstraint.activate(constraints)

The first constraint will display your icon at the Y center of the first line of your label. The second one puts your icon left of the label and creates a 10pt space between them.

Hope this helps!

blackjacx
  • 9,011
  • 7
  • 45
  • 56
1

You derive the middle of the first line by using the lineHeight for the font of your label.

let lineHeight = ceil(multiLineLabel.lineHeight)
let center = lineHeight / 2

Now that you have the center, you can center the haveReadIndicatorImgView's centerYAnchor to the top of your label with a constant: center

runmad
  • 14,846
  • 9
  • 99
  • 140
  • As I am concerned, `label.font.lineHeight` is 16.003, and `label.sizeThatFits(_:)` is 18. We can see that `lineHeight` is not accurate enough. – DawnSong Oct 24 '20 at 10:24
  • Oh no, my question is caused by label's `paragraphStyle.minimumLineHeight = 18.0`, which is really confusing. Your method is correct. – DawnSong Oct 26 '20 at 01:39
1

I solved this recently by adding a hidden single line label in exactly the same location and font as the multiline one, without a bottom constraint.

Then you can simply align the icon image .centerY to the hidden label's .centerY.

multi line label icon alignment

Wez
  • 10,555
  • 5
  • 49
  • 63
  • 2
    This is one of the most stable solutions. It works well with custom line heights, line spacings, baseline offsets etc. Just don't forget to apply the same font, attributes and set number of lines = 1 to this hidden label – Alexander Kulabukhov Feb 15 '22 at 10:08
1

I have done this differently.

  1. At first i align my imageView with label by FirstBaseLine.

  2. And then i took an outlet of that LayoutConstraint

  3. I have calculated an offset like below:

    let offset = (label.font.capHeight + imageView.frame.size.height) / 2 //your bulleted image

  4. I have discarded that offset from FirstBaseLine constant

    firstBaseLineConstraintWithLabel.constant -= offset

Here is the output enter image description here

elk_cloner
  • 2,049
  • 1
  • 12
  • 13
0

I achieve following by 2 steps:

1) Calculate expected height of label line of text with specific font:

+(CGSize)getSimpleSizeBasedOnFont:(CGFloat)font{

    UILabel *lbl = [UILabel new];
    lbl.text = @"Simple text";
    lbl.font = [UIFont systemFontOfSize:font];

    return [lbl.text sizeWithFont:lbl.font
                                constrainedToSize:lbl.frame.size
                                    lineBreakMode:NSLineBreakByWordWrapping];
}
  1. Then i add constraints to center Y of UIImage View with offset equal to 50% of that height:

CGFloat lblOffs = [Helper getSimpleSizeBasedOnFont:14].height; [_haveReadIndicatorImgView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(_topTxtlbl.mas_top).offset(lblOffs/2); make.left.equalTo(self.contentView).offset(SMALL_OFFSET); make.height.width.equalTo(@(8)); }];

Evgeniy Kleban
  • 6,794
  • 13
  • 54
  • 107