ios 单行 UILabel 旁边的中心 NSTextAttachment 图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26105803/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:54:20  来源:igfitidea点击:

Center NSTextAttachment image next to single line UILabel

iosobjective-cswiftuilabelnstextattachment

提问by Sean Danzeiser

I'd like to append an NSTextAttachmentimage to my attributed string and have it centered vertically.

我想将NSTextAttachment图像附加到我的属性字符串并使其垂直居中。

I've used the following code to create my string:

我使用以下代码来创建我的字符串:

NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:DDLocalizedString(@"title.upcomingHotspots") attributes:attrs];
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
attachment.image = [[UIImage imageNamed:@"help.png"] imageScaledToFitSize:CGSizeMake(14.f, 14.f)];
cell.textLabel.attributedText = [str copy];

However, the image appears to align to the top of the cell's textLabel.

但是,图像似乎与单元格的顶部对齐textLabel

text attachment offset problem screenshot

文字附件偏移问题截图

How can I change the rect in which the attachment is drawn?

如何更改绘制附件的矩形?

采纳答案by rob mayoff

You can change the rect by subclassing NSTextAttachmentand overriding attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex:. Example:

您可以通过子类化NSTextAttachment和覆盖来更改 rect attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex:。例子:

- (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex {
    CGRect bounds;
    bounds.origin = CGPointMake(0, -5);
    bounds.size = self.image.size;
    return bounds;
}

It's not a perfect solution. You have to figure out the Y-origin “by eye” and if you change the font or the icon size, you'll probably want to change the Y-origin. But I couldn't find a better way, except by putting the icon in a separate image view (which has its own disadvantages).

这不是一个完美的解决方案。您必须“通过眼睛”找出 Y 原点,如果您更改字体或图标大小,您可能想要更改 Y 原点。但是我找不到更好的方法,除了将图标放在单独的图像视图中(这有其自身的缺点)。

回答by MG Han

You can use the capHeight of the font.

您可以使用字体的 capHeight。

Objective-C

目标-C

NSTextAttachment *icon = [[NSTextAttachment alloc] init];
UIImage *iconImage = [UIImage imageNamed:@"icon.png"];
[icon setBounds:CGRectMake(0, roundf(titleFont.capHeight - iconImage.size.height)/2.f, iconImage.size.width, iconImage.size.height)];
[icon setImage:iconImage];
NSAttributedString *iconString = [NSAttributedString attributedStringWithAttachment:icon];
[titleText appendAttributedString:iconString];

Swift

迅速

let iconImage = UIImage(named: "icon.png")!
var icon = NSTextAttachment()
icon.bounds = CGRect(x: 0, y: (titleFont.capHeight - iconImage.size.height).rounded() / 2, width: iconImage.size.width, height: iconImage.size.height)
icon.image = iconImage
let iconString = NSAttributedString(attachment: icon)
titleText.append(iconString)

The attachment image is rendered on the baseline of the text. And the y axis of it is reversed like the core graphics coordinate system. If you want to move the image upward, set the bounds.origin.yto positive.

附件图像呈现在文本的基线上。并且它的 y 轴像核心图形坐标系一样反转。如果要向上移动图像,请将 设置bounds.origin.y为正。

The image should be aligned vertically center with the capHeight of the text. So we need to set the bounds.origin.yto (capHeight - imageHeight)/2.

图像应与文本的 capHeight 垂直居中对齐。所以我们需要设置bounds.origin.y(capHeight - imageHeight)/2.

Avoiding some jagged effect on the image, we should round the fraction part of the y. But fonts and images are usually small, even 1px difference makes the image looks like misaligned. So I applied the round function before dividing. It makes the fraction part of the y value to .0 or .5

避免对图像产生一些锯齿效果,我们应该四舍五入 y 的小数部分。但是字体和图像通常很小,即使是 1px 的差异也会使图像看起来没有对齐。所以我在划分之前应用了round函数。它使 y 值的小数部分变为 .0 或 0.5

In your case, the image height is larger than the capHeight of the font. But you can use the same way. The offset y value will be negative. And it will be laid out from the below of the baseline.

在您的情况下,图像高度大于字体的 capHeight。但是你可以用同样的方法。偏移 y 值将为负。它将从基线的下方布置。

enter image description here

在此处输入图片说明

回答by Travis

Try - [NSTextAttachment bounds]. No subclassing required.

试试- [NSTextAttachment bounds]。不需要子类化。

For context, I am rendering a UILabelfor use as the attachment image, then setting the bounds like so: attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height)and baselines of text within label image and text in attributed string line up as desired.

对于上下文,我渲染一个UILabel用作附件图像,然后像这样设置边界: attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height)标签图像中的文本基线和属性字符串中的文本根据需要排列。

回答by borchero

I found a perfect solution to this, works like a charm for me though, however you have to try it out yourself (probably the constant depends on the resolution of the device and maybe whatever ;)

我找到了一个完美的解决方案,虽然对我来说就像一个魅力,但是你必须自己尝试一下(可能常数取决于设备的分辨率,也许其他什么;)

func textAttachment(fontSize: CGFloat) -> NSTextAttachment {
    let font = UIFont.systemFontOfSize(fontSize) //set accordingly to your font, you might pass it in the function
    let textAttachment = NSTextAttachment()
    let image = //some image
    textAttachment.image = image
    let mid = font.descender + font.capHeight
    textAttachment.bounds = CGRectIntegral(CGRect(x: 0, y: font.descender - image.size.height / 2 + mid + 2, width: image.size.width, height: image.size.height))
    return textAttachment
}

Should work and shouldn't be blurry in any way (thanks to CGRectIntegral)

应该工作并且不应该以任何方式模糊(感谢CGRectIntegral

回答by Jakub Truhlá?

What about:

关于什么:

CGFloat offsetY = -10.0;

NSTextAttachment *attachment = [NSTextAttachment new];
attachment.image = image;
attachment.bounds = CGRectMake(0.0, 
                               offsetY, 
                               attachment.image.size.width, 
                               attachment.image.size.height);

No subclassing needed

不需要子类化

回答by phatmann

@Travis is correct that the offset is the font descender. If you also need to scale the image, you will need to use a subclass of NSTextAttachment. Below is the code, which was inspired by this article. I also posted it as a gist.

@Travis 是正确的,偏移量是字体下降器。如果您还需要缩放图像,则需要使用 NSTextAttachment 的子类。以下是受本文启发的代码。我也将其作为要点发布。

import UIKit

class ImageAttachment: NSTextAttachment {
    var verticalOffset: CGFloat = 0.0

    // To vertically center the image, pass in the font descender as the vertical offset.
    // We cannot get this info from the text container since it is sometimes nil when `attachmentBoundsForTextContainer`
    // is called.

    convenience init(_ image: UIImage, verticalOffset: CGFloat = 0.0) {
        self.init()
        self.image = image
        self.verticalOffset = verticalOffset
    }

    override func attachmentBoundsForTextContainer(textContainer: NSTextContainer, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
        let height = lineFrag.size.height
        var scale: CGFloat = 1.0;
        let imageSize = image!.size

        if (height < imageSize.height) {
            scale = height / imageSize.height
        }

        return CGRect(x: 0, y: verticalOffset, width: imageSize.width * scale, height: imageSize.height * scale)
    }
}

Use as follows:

使用方法如下:

var text = NSMutableAttributedString(string: "My Text")
let image = UIImage(named: "my-image")!
let imageAttachment = ImageAttachment(image, verticalOffset: myLabel.font.descender)
text.appendAttributedString(NSAttributedString(attachment: imageAttachment))
myLabel.attributedText = text

回答by IndyZa

If you have a very large ascendent and want to center the image (center of the cap height) like me try this

如果你有一个非常大的上升点并且想要像我一样将图像居中(大写高度的中心)试试这个

let attachment: NSTextAttachment = NSTextAttachment()
attachment.image = image
if let image = attachment.image{
    let y = -(font.ascender-font.capHeight/2-image.size.height/2)
    attachment.bounds = CGRect(x: 0, y: y, width: image.size.width, height: image.size.height).integral
}

The y calculation is as the picture below

y计算如下图

enter image description here

在此处输入图片说明

Note that the y value is 0 because we want the image to shift down from the origin

请注意,y 值为 0,因为我们希望图像从原点向下移动

If you want it to be in the middle of the whole label.Use this y value:

如果你想让它在整个标签的中间。使用这个 y 值:

let y = -((font.ascender-font.descender)/2-image.size.height/2)

回答by Oscar

We can make an extension in swift 4 that generates an attachment with a centered image like this one:

我们可以在 swift 4 中进行扩展,以生成带有居中图像的附件,如下所示:

extension NSTextAttachment {
    static func getCenteredImageAttachment(with imageName: String, and 
    font: UIFont?) -> NSTextAttachment? {
        let imageAttachment = NSTextAttachment()
    guard let image = UIImage(named: imageName),
        let font = font else { return nil }

    imageAttachment.bounds = CGRect(x: 0, y: (font.capHeight - image.size.height).rounded() / 2, width: image.size.width, height: image.size.height)
    imageAttachment.image = image
    return imageAttachment
    }
}

Then you can make the call sending the name of the image and the font:

然后你可以打电话发送图像的名称和字体:

let imageAttachment = NSTextAttachment.getCenteredImageAttachment(with: imageName,
                                                                   and: youLabel?.font)

And then append the imageAttachment to the attributedString

然后将 imageAttachment 附加到 attributesString

回答by Reimond Hill

In my case calling sizeToFit() helped. In swift 5.1

在我的情况下,调用 sizeToFit() 有帮助。在快速 5.1

Inside your custom label:

在您的自定义标签内:

func updateUI(text: String?) {
    guard let text = text else {
        attributedText = nil
        return
    }

    let attributedString = NSMutableAttributedString(string:"")

    let textAttachment = NSTextAttachment ()
    textAttachment.image = image

    let sizeSide: CGFloat = 8
    let iconsSize = CGRect(x: CGFloat(0),
                           y: (font.capHeight - sizeSide) / 2,
                           width: sizeSide,
                           height: sizeSide)
    textAttachment.bounds = iconsSize

    attributedString.append(NSAttributedString(attachment: textAttachment))
    attributedString.append(NSMutableAttributedString(string: text))
    attributedText = attributedString

    sizeToFit()
}

回答by Harish Kumar Kailas

Please use -lineFrag.size.height/5.0 for the bounds height. This exactly centres the image and aligned with text for all the size of fonts

请使用 -lineFrag.size.height/5.0 作为边界高度。这恰好使图像居中并与所有字体大小的文本对齐

override func attachmentBoundsForTextContainer(textContainer: NSTextContainer, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect
{
    var bounds:CGRect = CGRectZero

    bounds.size = self.image?.size as CGSize!
    bounds.origin = CGPointMake(0, -lineFrag.size.height/5.0);

    return bounds;
}