Home > Net >  Draw UIImage in Rectangle with background color if it doesn't fit in a rectangle?
Draw UIImage in Rectangle with background color if it doesn't fit in a rectangle?

Time:04-26

I'm new to this topic so I want to ask how I can achieve this following behavior: I would like to create a image with a specific size. If the original Image doesn't fit in a square I would like to fill the sides with a color, so I want to draw that image in a rectangle with a color and want to store it in a new UIImage variable.

To resize a Image I found something like this. But how can I fit the image in a Rectangle with a specific color and create a new Image that can be stored?

UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
return UIGraphicsGetImageFromCurrentImageContext()

CodePudding user response:

One possible way would be to create an extension method to UIImage as you started in your sample code.

Then create a new image of the desired size and fill the background color with UIRectFill.

The next step would be to calculate the new size so that the image content is scaled to fit into the image by taking the aspect ratio into account:

let scale = min(size.width / originalSize.width, size.height / originalSize.height)
let newSize = CGSize(width: originalSize.width * scale, height: originalSize.height * scale)
let origin = CGPoint(x: (size.width - newSize.width) / 2, y: (size.height - newSize.height) / 2)

Then you basically just need to draw the image into the rectangle that results from the origin and the actual size of the image inside the background area.

Completely, the extension method could then look something like this:

func image(size: CGSize, background: UIColor) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
    defer { UIGraphicsEndImageContext() }
    background.setFill()
    let completeRect = CGRect(origin: .zero, size: size)
    UIRectFill(completeRect)

    let originalSize = self.size
    let scale = min(size.width / originalSize.width, size.height / originalSize.height)
    let newSize = CGSize(width: originalSize.width * scale, height: originalSize.height * scale)
    let origin = CGPoint(x: (size.width - newSize.width) / 2, y: (size.height - newSize.height) / 2)
    let imageRect = CGRect(origin: origin, size: newSize)
    draw(in: imageRect, blendMode: .normal, alpha: 1.0)
    return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
}

Self-Contained Complete Example

Finally a self-contained complete example for testing:

import UIKit

class ViewController: UIViewController {
    
    private let image1 = UIImage(named: "country")
    private let image2 = UIImage(named: "regensburg")
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let imageView1 = createImageView()
        imageView1.image = image1?.image(size: CGSize(width: 200, height: 200), background: .blue)

        let imageView2 = createImageView()
        imageView2.image = image2?.image(size: CGSize(width: 200, height: 200), background: .blue)

        NSLayoutConstraint.activate([
            imageView1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24),
            imageView1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24),
            imageView1.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24),
            imageView2.topAnchor.constraint(equalTo: imageView1.bottomAnchor, constant: 24),
            imageView2.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24),
            imageView2.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24),
            imageView2.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24),
            imageView2.widthAnchor.constraint(equalTo: imageView1.widthAnchor),
            imageView2.heightAnchor.constraint(equalTo: imageView1.heightAnchor)
        ])
    }

    private func createImageView() -> UIImageView {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        imageView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(imageView)
        return imageView
    }

}


extension UIImage {

    func image(size: CGSize, background: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, self.scale)
        defer { UIGraphicsEndImageContext() }
        background.setFill()
        let completeRect = CGRect(origin: .zero, size: size)
        UIRectFill(completeRect)

        let originalSize = self.size
        let scale = min(size.width / originalSize.width, size.height / originalSize.height)
        let newSize = CGSize(width: originalSize.width * scale, height: originalSize.height * scale)
        let origin = CGPoint(x: (size.width - newSize.width) / 2, y: (size.height - newSize.height) / 2)
        let imageRect = CGRect(origin: origin, size: newSize)
        draw(in: imageRect, blendMode: .normal, alpha: 1.0)
        return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
    }

}

The output of the above code is then:

demo

The specified actual image size is square. Accordingly, there are corresponding vertical or horizontal stripes at the edges of the image in the selected background color (blue).

  • Related