我想将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顶部对齐。
如何更改绘制附件的矩形?
答案 0 :(得分:126)
您可以使用字体的capHeight。
<强>目标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的差异也会使图像看起来像是不对齐的。所以我在分割前应用了圆函数。它使y值的分数部分为.0或.5
在您的情况下,图像高度大于字体的capHeight。但你可以用同样的方式。偏移y值将为负。它将从基线的下方进行布局。
答案 1 :(得分:98)
试试- [NSTextAttachment bounds]
。不需要子类化。
对于上下文,我渲染UILabel
用作附件图像,然后像这样设置边界:
attachment.bounds = CGRectMake(0, self.font.descender, attachment.image.size.width, attachment.image.size.height)
和标签图片中的文字基线以及归因字符串中的文字根据需要排列。
答案 2 :(得分:58)
我找到了一个完美的解决方案,对我来说就像一个魅力,但是你必须自己尝试一下(可能常数取决于设备的分辨率,也许是其他的;)
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
)
答案 3 :(得分:51)
您可以通过继承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原点。但我无法找到更好的方法,除非将图标放在单独的图像视图中(这有其自身的缺点)。
答案 4 :(得分:10)
@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
答案 5 :(得分:8)
如果你有一个非常大的ascendent并希望像我一样将图像居中(帽子高度的中心),试试这个
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)
答案 6 :(得分:4)
我们可以在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
答案 7 :(得分:1)
就我而言,调用sizeToFit()有所帮助。 快速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()
}
答案 8 :(得分:0)
请使用-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;
}