在单行 UILabel 旁边居中 NSTextAttachment 图像
Posted
技术标签:
【中文标题】在单行 UILabel 旁边居中 NSTextAttachment 图像【英文标题】:Center NSTextAttachment image next to single line UILabel 【发布时间】:2014-11-24 04:40:03 【问题描述】:我想将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];
但是,图像似乎与单元格顶部的 textLabel
对齐。
如何更改绘制附件的矩形?
【问题讨论】:
我有一个类别类,用于将 NSString 与 UIImage 结合使用,反之亦然。 github.com/Pradeepkn/TextWithImage享受。 【参考方案1】:您可以使用字体的 capHeight。
Objective-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];
斯威夫特
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
设置为正值。
图像应与文本的 capHeight 垂直居中对齐。
所以我们需要将bounds.origin.y
设置为(capHeight - imageHeight)/2
。
为了避免对图像产生一些锯齿状的影响,我们应该对 y 的小数部分进行四舍五入。但是字体和图像通常很小,即使是 1px 的差异也会使图像看起来像未对齐。所以我在划分之前应用了round函数。它将 y 值的小数部分设为 .0 或 .5
在您的情况下,图像高度大于字体的 capHeight。但是你可以用同样的方法。偏移 y 值将为负数。并且会从基线的下方开始布局。
【讨论】:
仅插图就值得+1!感谢分享。【参考方案2】:试试- [NSTextAttachment bounds]
。不需要子类化。
对于上下文,我正在渲染一个UILabel
用作附件图像,然后像这样设置边界:
attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height)
和标签图像中的文本基线和属性字符串中的文本根据需要排列。
【讨论】:
只要您不需要缩放图像,它就可以工作。 对于 Swift 3.0:attachment.bounds = CGRect(x: 0.0, y: self.font.descender, width: attachment.image!.size.width, height: attachment.image!.size.height)
太棒了,谢谢!不知道 UIFont 的 descender
属性!
如何渲染标签作为附件图片?【参考方案3】:
您可以通过继承NSTextAttachment
并覆盖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;
这不是一个完美的解决方案。您必须“通过眼睛”找出 Y 原点,如果您更改字体或图标大小,您可能需要更改 Y 原点。但是我找不到更好的方法,除了将图标放在单独的图像视图中(这有其自身的缺点)。
【讨论】:
不知道为什么他们投了反对票,这对我很有帮助 +1 Y 原点是字体下降器。请参阅下面的答案。 Travis 的答案是没有子类化的更清洁的解决方案。 欲了解更多详情,请查看@mg-han 的答案***.com/a/45161058/280503(我认为这应该是问题的选定答案。)【参考方案4】:我找到了一个完美的解决方案,虽然对我来说就像一个魅力,但是你必须自己尝试一下(可能常数取决于设备的分辨率,也可能是其他因素;)
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
应该有效并且不应该以任何方式模糊(感谢CGRectIntegral
)
【讨论】:
感谢您发布此内容,它使我找到了一个很好的方法。我注意到您在 y 坐标计算中添加了一个有点神奇的 2。 这是我用于 y 计算的内容:descender + (abs(descender) + capHeight)/2 - iconHeight/2 为什么 Y 原点 +2? @WilliamLeGate 我真的不知道,只是试了一下,它适用于我测试过的所有字体大小(我需要的那些).. 该死的......这个答案太棒了。【参考方案5】:怎么样:
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);
不需要子类化
【讨论】:
比使用 self.font.descender 效果更好(在运行 ios 8 的 iPhone 4s 模拟器上默认值为 ~4)。 -10 看起来是默认字体样式/大小的更好近似值。【参考方案6】:@Travis 是正确的,偏移量是字体下降器。如果还需要缩放图像,则需要使用 NSTextAttachment 的子类。下面是受this article启发的代码。我还将其发布为gist。
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)
如下使用:
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
【讨论】:
【参考方案7】:如果你有一个非常大的上升点并且想像我一样将图像居中(大写高度的中心)试试这个
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)
【讨论】:
【参考方案8】:我们可以在 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 附加到属性字符串
【讨论】:
【参考方案9】:就我而言,调用 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()
【讨论】:
【参考方案10】:请使用 -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;
【讨论】:
-lineFrag.size.height/5.0
不正确。相反,它是字体下降器。以上是关于在单行 UILabel 旁边居中 NSTextAttachment 图像的主要内容,如果未能解决你的问题,请参考以下文章