I\'d like to append an NSTextAttachment
image to my attributed string and have it centered vertically.
I\'ve used the following code to create my string
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
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;
}
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
)
In my case calling sizeToFit() helped. In swift 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()
}
You can change the rect by subclassing NSTextAttachment
and overriding attachmentBoundsForTextContainer:proposedLineFragment:glyphPosition:characterIndex:
. Example:
- (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).
Try - [NSTextAttachment bounds]
. No subclassing required.
For context, I am rendering a UILabel
for 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.
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