How to Resize an Image or Done as a Nsattributedstring Nstextattachment (Or Set Its Initital Size)

how to resize an image or done as a NSAttributedString NSTextAttachment (or set its initital size)

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.

Swift iOS -How to Set NSTextAttachment Content Mode to Aspect Fit?

I followed @Maciej Swic's answer

Resize NSTextAttachment Image

For some reason I couldn't extend the NSTextAttachment class so I added it to the bottom of the class I was using it in. I removed the bounds property that I used in my question and used his function instead. It's on #4, the second line:

class MyController: UIViewController{

override func viewDidLoad() {
super.viewDidLoad()

// #1. Define dict attribute for string
let bold17 = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: 17)]

// #2. Create "hello" string and add the dict attribute to it
let helloStr = NSAttributedString(string: "Hello\n\n", attributes: bold17)

// #3. Create NSTextAttachment
let textAttachment = NSTextAttachment()

// #4. Add image to the textAttachment then set it's bounds
textAttachment.image = UIImage(named: "world_PDF")
textAttachment.setImageHeight(height: 200) // <----HIS ANSWER HERE

// #5. Set image as NSAttributedString
let worldImage = NSAttributedString(attachment: textAttachment)

// #6. Create NSMutableString to
let mutableAttributedString = NSMutableAttributedString()

// #7. Append the "hello" string and the "world" image to each other using the mutableAttributedString object
mutableAttributedString.append(helloStr)
mutableAttributedString.append(worldImage)

// #8. Set the mutableAttributedString to the textView then center it
textView.attributedText = mutableAttributedString
textView.textAlignment = .center

}
}

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

Resize an image that is used in NSAttributedString

Oh right, so NSTextAttachment doesn't have image views... ah... Right well then another idea comes to mind. How about reszing the UIImage data and then passing that image to the NSTextAttachment, like in this post:

https://stackoverflow.com/a/2658801/4657588

Make use of this method to resize the image accordingly:

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

Center NSTextAttachment image next to single line UILabel

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

NSTextAttachment images are not dynamic (light/dark mode)

I think that's unfortunately the normal behavior, but I considered it as a forgotten feature non-dev by Apple.

The only way I got currently, is to listen to func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) to detect the mode change.

Then, you can either reconstruct your NSAttributedString, or enumerate over it and update it when needed.

With a enumeration, ie update only what's needed, and not regenerate the whole NSAttributedString:

In your initial attachment creation:

let attachment = NSTextAttachment(image: asset.image(with: traitCollection))
let attachmentCharacter = NSAttributedString(attachment: attachment)

Side note:
I used asset.image(with: traitCollection) instead of image1, else when starting with dark mode, your image will be of light mode instead. So this should set the correct image.

Then, I'd update it with:

func switchAttachment(for attr: NSAttributedString?) -> NSAttributedString? {
guard let attr = attr else { return nil }
let mutable = NSMutableAttributedString(attributedString: attr)
mutable.enumerateAttribute(.attachment, in: NSRange(location: 0, length: mutable.length), options: []) { attachment, range, stop in
guard let attachment = attachment as? NSTextAttachment else { return }
guard let asset = attachment.image?.imageAsset else { return }
attachment.image = asset.image(with: .current)
mutable.replaceCharacters(in: range, with: NSAttributedString(attachment: attachment))
}
return mutable
}

And update when:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
label.attributedText = switchAttachment(for: label.attributedText)
}


Related Topics



Leave a reply



Submit