Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSAttributedString: Fit image to container

I have a NSAttributedString which is made from HTML and it displays some images. The problem is that the images are bigger than the container and I wonder how to fit them in it.

Thanks for your help

like image 753
Florentin Avatar asked Mar 03 '26 09:03

Florentin


1 Answers

I finally found how to do that:

content.enumerateAttribute(NSAttachmentAttributeName, inRange: NSMakeRange(0, content.length), options: NSAttributedStringEnumerationOptions(0)) { (value, range, stop) -> Void in
    if let attachement = value as? NSTextAttachment {
        let image = attachement.imageForBounds(attachement.bounds, textContainer: NSTextContainer(), characterIndex: range.location)
        let screenSize: CGRect = UIScreen.mainScreen().bounds
        if image.size.width > screenSize.width-2 {
            let newImage = image.resizeImage((screenSize.width-2)/image.size.width)
            let newAttribut = NSTextAttachment()
            newAttribut.image = newImage
            content.addAttribute(NSAttachmentAttributeName, value: newAttribut, range: range)
        }
    }
}

The function resizeImage() is defined like that:

extension UIImage {
    func resizeImage(scale: CGFloat) -> UIImage {
        let newSize = CGSizeMake(self.size.width*scale, self.size.height*scale)
        let rect = CGRectMake(0, 0, newSize.width, newSize.height)

        UIGraphicsBeginImageContext(newSize)
        self.drawInRect(rect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }
}

Swift 3 version

text.enumerateAttribute(NSAttachmentAttributeName, in: NSMakeRange(0, text.length), options: .init(rawValue: 0), using: { (value, range, stop) in
    if let attachement = value as? NSTextAttachment {
        let image = attachement.image(forBounds: attachement.bounds, textContainer: NSTextContainer(), characterIndex: range.location)!
        let screenSize: CGRect = UIScreen.main.bounds
        if image.size.width > screenSize.width-20 {
            let newImage = image.resizeImage(scale: (screenSize.width-2)/image.size.width)
            let newAttribut = NSTextAttachment()
            newAttribut.image = newImage
            text.addAttribute(NSAttachmentAttributeName, value: newAttribut, range: range)
        }
    }
})

The function resizeImage() is defined like that:

func resizeImage(scale: CGFloat) -> UIImage {
    let newSize = CGSize(width: self.size.width*scale, height: self.size.height*scale)
    let rect = CGRect(origin: CGPoint.zero, size: newSize)

    UIGraphicsBeginImageContext(newSize)
    self.draw(in: rect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage!
}

Swift 4 version + Extension and customisable max width for image

extension NSAttributedString {
    func attributedStringWithResizedImages(with maxWidth: CGFloat) -> NSAttributedString {
        let text = NSMutableAttributedString(attributedString: self)
        text.enumerateAttribute(NSAttributedStringKey.attachment, in: NSMakeRange(0, text.length), options: .init(rawValue: 0), using: { (value, range, stop) in
            if let attachement = value as? NSTextAttachment {
                let image = attachement.image(forBounds: attachement.bounds, textContainer: NSTextContainer(), characterIndex: range.location)!
                if image.size.width > maxWidth {
                    let newImage = image.resizeImage(scale: maxWidth/image.size.width)
                    let newAttribut = NSTextAttachment()
                    newAttribut.image = newImage
                    text.addAttribute(NSAttributedStringKey.attachment, value: newAttribut, range: range)
                }
            }
        })
        return text
    }
}

extension UIImage {
    func resizeImage(scale: CGFloat) -> UIImage {
        let newSize = CGSize(width: self.size.width*scale, height: self.size.height*scale)
        let rect = CGRect(origin: CGPoint.zero, size: newSize)

        UIGraphicsBeginImageContext(newSize)
        self.draw(in: rect)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}
like image 150
Florentin Avatar answered Mar 06 '26 00:03

Florentin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!