Reputation: 19969
I have a NSAttributedString to which I am adding a NSTextAttachment. The image is 50w by 50h but I'd like it to scale down to reflect the line height of the attributed string. I thought this would be done automatically but I guess not. I have looked at the UImage class reference but this image doesn't seem to be set in a UIImageView so no access to a frame property. Here's a screenshot of what I currently have:
In an ideal world, I would also like to implement a way to scale up the image based upon user input (such as increasing the font size). Any ideas on how to achieve this?
thx
here's how I'm creating it:
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
textAttachment.image = [UIImage imageNamed:@"note-small.png"];
NSLog(@"here is the scale: %f", textAttachment.image.scale);
NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment];
[headerAS replaceCharactersInRange:NSMakeRange([headerAS length], 0) withString:@" "];
[headerAS replaceCharactersInRange:NSMakeRange([headerAS length], 0) withAttributedString:attrStringWithImage];
Upvotes: 37
Views: 28756
Reputation: 673
Thanks @Dung Nguyen, for his answer. I found that his solution works well on iOS devices, but not working on macOS when trying to update an attachment in a large NSAttributedString. So I searched for my own solution for that. Here it is
let newSize: NSSize = \\ the new size for the image attachment
let originalAttachment: NSTextAttachment = \\ wherever the attachment comes from
let range: NSRange = \\ the range of the original attachment in a wholeAttributedString object which is an NSMutableAttributedString
if originalAttachment.fileWrapper != nil,
originalAttachment.fileWrapper!.isRegularFile {
if let contents = originalAttachment.fileWrapper!.regularFileContents {
let newAttachment = NSTextAttachment()
newAttachment.image = NSImage(data: contents)
#if os(iOS)
newAttachment.fileType = originalAttachment.fileType
newAttachment.contents = originalAttachment.contents
newAttachment.fileWrapper = originalAttachment.fileWrapper
#endif
newAttachment.bounds = CGRect(x: originalAttachment.bounds.origin.x,
y: originalAttachment.bounds.origin.y,
width: newSize.width, height: newSize.height)
let newAttachmentString = NSAttributedString(attachment: newAttachment)
wholeAttributedString.replaceCharacters(in: range, with: newAttachmentString)
}
}
Also, on different OS, the image size or bounds an image attachment returns can be different. In order to extract the correct image size, I wrote the following extensions:
extension FileWrapper {
func imageSize() -> CPSize? {
if self.isRegularFile {
if let contents = self.regularFileContents {
if let image: CPImage = CPImage(data: contents) {
#if os(iOS)
return image.size
#elseif os(OSX)
if let rep = image.representations.first {
return NSMakeSize(CGFloat(rep.pixelsWide),
CGFloat(rep.pixelsHigh))
}
#endif
}
}
}
return nil
}
}
extension NSTextAttachment {
func imageSize() -> CPSize? {
var imageSize: CPSize?
if self.fileType != nil &&
AttachmentFileType(rawValue: self.fileType!) != nil {
if self.bounds.size.width > 0 {
imageSize = self.bounds.size
} else if self.image?.size != nil {
imageSize = self.image!.size
} else if let fileWrapper = self.fileWrapper {
if let imageSizeFromFileWrapper = fileWrapper.imageSize() {
imageSize = imageSizeFromFileWrapper
}
}
}
return imageSize
}
}
#if os(iOS)
import UIKit
public typealias CPSize = CGSize
#elseif os(OSX)
import Cocoa
public typealias CPSize = NSSize
#endif
#if os(iOS)
import UIKit
public typealias CPImage = UIImage
#elseif os(OSX)
import AppKit
public typealias CPImage = NSImage
#endif
enum AttachmentFileType: String {
case jpeg = "public.jpeg"
case jpg = "public.jpg"
case png = "public.png"
case gif = "public.gif"
}
Upvotes: 1
Reputation: 915
You should set bounds form attachment to resize image like this:
attachment.bounds = CGRectMake(0, 0, yourImgWidth, yourImgHeight)
Upvotes: 87
Reputation: 1664
Swift4 if you want a attributed string along with the image or a icon
here you can do something like this.
func AttributedTextwithImgaeSuffixAndPrefix(AttributeImage1 : UIImage , AttributedText : String ,AttributeImage2 : UIImage, LabelBound : UILabel) -> NSMutableAttributedString
{
let fullString = NSMutableAttributedString(string: " ")
let image1Attachment = NSTextAttachment()
image1Attachment.bounds = CGRect(x: 0, y: ((LabelBound.font.capHeight) - AttributeImage1.size.height).rounded() / 2, width: AttributeImage1.size.width, height: AttributeImage1.size.height)
image1Attachment.image = AttributeImage1
let image1String = NSAttributedString(attachment: image1Attachment)
let image2Attachment = NSTextAttachment()
image2Attachment.bounds = CGRect(x: 0, y: ((LabelBound.font.capHeight) - AttributeImage2.size.height).rounded() / 2, width: AttributeImage2.size.width, height: AttributeImage2.size.height)
image2Attachment.image = AttributeImage2
let image2String = NSAttributedString(attachment: image2Attachment)
fullString.append(image1String)
fullString.append(NSAttributedString(string: AttributedText))
fullString.append(image2String)
return fullString
}
you can use this code as mentioned below:
self.lblMid.attributedText = AttributedTextwithImgaeSuffixAndPrefix(AttributeImage1: #imageLiteral(resourceName: "Left") , AttributedText: " Payment Details ", AttributeImage2: #imageLiteral(resourceName: "RIght") , LabelBound: self.lblMid)
here you can add images you can replace it with your own:
Upvotes: 3
Reputation: 11359
If you need to resize a bunch of NSTextAttachment
images while keeping their aspect ratio i've written a handy extension: http://hack.swic.name/convenient-nstextattachment-image-resizing
extension NSTextAttachment {
func setImageHeight(height: CGFloat) {
guard let image = image else { return }
let ratio = image.size.width / image.size.height
bounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: ratio * height, height: height)
}
}
Example usage:
let textAttachment = NSTextAttachment()
textAttachment.image = UIImage(named: "Image")
textAttachment.setImageHeight(16) // Whatever you need to match your font
let imageString = NSAttributedString(attachment: textAttachment)
yourAttributedString.appendAttributedString(imageString)
Upvotes: 35
Reputation: 141
NSTextAtatchment is just a holder for a UIImage so scale the image when it needs scaling and recreate the text attachment or set it's image. You'll need to force a nslayout update if you change the image on an existing text attachment.
Upvotes: 0
Reputation: 119031
Look at subclassing NSTextAttachment
and implementing the NSTextAttachmentContainer
methods to return different sizes based on the text container supplied. By default, NSTextAttachment
just returns the size of the image it is provided with.
Upvotes: 16