Ios 将NSTextAttachment图像居中放置在单行UILabel旁边

Ios 将NSTextAttachment图像居中放置在单行UILabel旁边,ios,objective-c,swift,uilabel,nstextattachment,Ios,Objective C,Swift,Uilabel,Nstextattachment,我想在属性字符串中附加一个NSTextAttachment图像,并使其垂直居中 我使用以下代码创建了我的字符串: NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:DDLocalizedString(@"title.upcomingHotspots") attributes:attrs]; NSTextAttachment *attachment = [[NSTextAttachm

我想在属性字符串中附加一个
NSTextAttachment
图像,并使其垂直居中

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

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];
但是,图像似乎与单元格的
文本标签的顶部对齐


如何更改绘制附件的rect?

您可以通过子类化
NSTextAttachment
并重写
attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex:
来更改rect。例如:

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

这不是一个完美的解决方案。你必须“用眼睛”找出Y原点,如果你改变字体或图标大小,你可能会想要改变Y原点。但是我找不到更好的方法,除非把图标放在一个单独的图像视图中(这有它自己的缺点)。

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

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

请使用-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;
}

@Travis认为偏移量是字体下降量是正确的。如果还需要缩放图像,则需要使用NSTextAttachment的子类。下面是代码,它的灵感来自。我也把它贴成了一个网站

那么:

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);

无需子类化

我找到了一个完美的解决方案,但对我来说很有吸引力,不过你必须自己尝试一下(可能常数取决于设备的分辨率,或者其他;)


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

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
}
y计算如下图所示

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

如果你想把它放在整个标签的中间,请使用这个y值:

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

您可以使用字体的大写字母高度

目标-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)
附件图像在文本的基线上呈现。 它的y轴是反向的,就像核心图形坐标系一样。 如果要向上移动图像,请将
bounds.origin.y
设置为正值

图像应与文本的高度垂直居中对齐。 因此,我们需要将
bounds.origin.y
设置为
(capHeight-imageHeight)/2

为了避免图像上出现锯齿状的效果,我们应该将y的分数部分圆化。但是字体和图像通常很小,即使1px的差异也会使图像看起来不对齐。所以我在除法之前应用了round函数。它使y值的分数部分变为.0或.5

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


我们可以在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
    }
}
然后,您可以拨打电话,发送图像名称和字体:

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

然后将imageAttachment附加到attributedString中,在我的例子中,调用sizeToFit()有帮助。 在swift 5.1中

在自定义标签内:

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


不知道为什么他们否决了这一点,这对我很有帮助,+1Y-origin是字体的下降者。请参阅下面我的答案。Travis的答案是一个没有子类化的更干净的解决方案。有关更多详细信息,请查看@mg han的答案(我认为它应该是问题的选定答案)。只要您不需要缩放图像,此选项就有效。对于Swift 3.0:
attachment.bounds=CGRect(x:0.0,y:self.font.downer,宽度:attachment.image!.size.width,高度:attachment.image!.size.height)
太棒了,谢谢!我不知道UIFont的
下降器
属性!如何呈现标签用作附件图像?
-lineFrag.size.height/5.0
不正确。相反,它是字体下降器。比使用self.font.downer(在运行iOS 8的iPhone 4s模拟器上默认值为~4)效果更好-10看起来是默认字体样式/大小的更好近似值。感谢您发布这篇文章,它让我找到了一个非常好的方法。我注意到您在y坐标计算中添加了一个有点神奇的2。以下是我在y坐标计算中使用的方法:下降器+(abs(下降器)+capHeight)/2-iconHeight/2为什么Y来源为+2?@WilliamLeGate我真的不知道,只是尝试了一下,它适用于我测试的所有字体大小(我需要的字体大小)…好该死…这个答案太棒了。我有一个分类类,可以将NSString与UIImage一起使用,反之亦然。享受吧。这个插图值得一个+1!谢谢分享。
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
    }
}
let imageAttachment = NSTextAttachment.getCenteredImageAttachment(with: imageName,
                                                                   and: youLabel?.font)
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()
}