Add transparent space around a UIImage
Asked Answered
T

5

42

Lets say we have an image of 600X400 pixel and we want to end up with an new image of 1000x1000 pixel which contains the initial image in the centre and transparent space around it. How can I achieve that in code?

enter image description here

Terraqueous answered 16/11, 2013 at 17:31 Comment(1)
Should not have been closed IMO. Question is well defined and is useful.Simasimah
B
24

You create a new image context that is 1000x1000, draw your old image in the middle, then get the new UIImage from the context.

// Setup a new context with the correct size
CGFloat width = 1000;
CGFloat height = 1000;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0.0);        
CGContextRef context = UIGraphicsGetCurrentContext();       
UIGraphicsPushContext(context);                             

// Now we can draw anything we want into this new context.
CGPoint origin = CGPointMake((width - oldImage.size.width) / 2.0f,
                            (height - oldImage.size.height) / 2.0f);
[oldImage drawAtPoint:origin];

// Clean up and get the new image.
UIGraphicsPopContext();                             
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Bridal answered 16/11, 2013 at 18:16 Comment(2)
Dear DrummerB! Thanks very much for posting a working solution. Only correctio was replace CGPoint origin = CGRectMake((width - oldImage.size.width) / 2.0f, (height - oldImage.size.height) / 2.0f); with CGPoint origin = CGPointMake((width - oldImage.size.width) / 2.0f, (height - oldImage.size.height) / 2.0f);Terraqueous
@Bridal don't you want the opaque argument in UIGraphicsBeginImageContextWithOptions to be NO since he wants transparent space?Fley
M
56

In Swift you can write an extension to UIImage that draws image with insets around it.

Swift 3:

import UIKit

extension UIImage {
    func imageWithInsets(insets: UIEdgeInsets) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(
            CGSize(width: self.size.width + insets.left + insets.right,
                   height: self.size.height + insets.top + insets.bottom), false, self.scale)
        let _ = UIGraphicsGetCurrentContext()
        let origin = CGPoint(x: insets.left, y: insets.top)
        self.draw(at: origin)
        let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return imageWithInsets
    }
}

OLD ANSWER:

import UIKit

extension UIImage {
    func imageWithInsets(insets: UIEdgeInsets) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(
            CGSizeMake(self.size.width + insets.left + insets.right,
                self.size.height + insets.top + insets.bottom), false, self.scale)
        let context = UIGraphicsGetCurrentContext()
        let origin = CGPoint(x: insets.left, y: insets.top)
        self.drawAtPoint(origin)
        let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return imageWithInsets
    }
}
Mccabe answered 6/7, 2015 at 8:20 Comment(3)
any idea why sometimes this might return nil?Bonnard
See developer.apple.com/reference/uikit/…: You should call this function only when a bitmap-based graphics context is the current graphics context. If the current context is nil or was not created by a call to UIGraphicsBeginImageContext(_:), this function returns nil.Mccabe
@Mccabe Just a minor suggestion: it should be return imageWithInsets?.withRenderingMode(renderingMode) in order to support template images as wellEversion
P
28

This is the solution in Swift 4 inspired by DrummerB answer:

import UIKit

extension UIImage {

    func addImagePadding(x: CGFloat, y: CGFloat) -> UIImage? {
        let width: CGFloat = size.width + x
        let height: CGFloat = size.height + y
        UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 0)
        let origin: CGPoint = CGPoint(x: (width - size.width) / 2, y: (height - size.height) / 2)
        draw(at: origin)
        let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return imageWithPadding
    }
}

How to apply:

let image = UIImage(named: "your-image")!
let imageView = UIImageView(image: image.addImagePadding(x: 50, y: 50))
imageView.backgroundColor = UIColor.gray
view.addSubview(imageView)

Features:

  • Simply pass padding values via parameters
  • Colored padding (by setting the UIGraphicsBeginImageContextWithOptions opaque parameter to false)
Porkpie answered 13/9, 2016 at 22:38 Comment(0)
B
24

You create a new image context that is 1000x1000, draw your old image in the middle, then get the new UIImage from the context.

// Setup a new context with the correct size
CGFloat width = 1000;
CGFloat height = 1000;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0.0);        
CGContextRef context = UIGraphicsGetCurrentContext();       
UIGraphicsPushContext(context);                             

// Now we can draw anything we want into this new context.
CGPoint origin = CGPointMake((width - oldImage.size.width) / 2.0f,
                            (height - oldImage.size.height) / 2.0f);
[oldImage drawAtPoint:origin];

// Clean up and get the new image.
UIGraphicsPopContext();                             
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Bridal answered 16/11, 2013 at 18:16 Comment(2)
Dear DrummerB! Thanks very much for posting a working solution. Only correctio was replace CGPoint origin = CGRectMake((width - oldImage.size.width) / 2.0f, (height - oldImage.size.height) / 2.0f); with CGPoint origin = CGPointMake((width - oldImage.size.width) / 2.0f, (height - oldImage.size.height) / 2.0f);Terraqueous
@Bridal don't you want the opaque argument in UIGraphicsBeginImageContextWithOptions to be NO since he wants transparent space?Fley
W
2

A fix for appsunited's answer with better naming convension. To not confuse it the function is mutating or not:

extension UIImage {
    func withPadding(_ padding: CGFloat) -> UIImage? {
        return withPadding(x: padding, y: padding)
    }

    func withPadding(x: CGFloat, y: CGFloat) -> UIImage? {
        let newWidth = size.width + 2 * x
        let newHeight = size.height + 2 * y
        let newSize = CGSize(width: newWidth, height: newHeight)
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
        let origin = CGPoint(x: (newWidth - size.width) / 2, y: (newHeight - size.height) / 2)
        draw(at: origin)
        let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return imageWithPadding
    }
}
Wrathful answered 10/12, 2019 at 13:3 Comment(0)
R
1

Make a category on UIImage and try this:

+ (UIImage *)imageWithInsets:(CGRect)insetRect image:(UIImage *)image {
    CGRect newRect = CGRectMake(0.0, 0.0, insetRect.origin.x+insetRect.size.width+image.size.width, insetRect.origin.y+insetRect.size.height+image.size.height);
    // Setup a new context with the correct size
    UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    UIGraphicsPushContext(context);

    // Now we can draw anything we want into this new context.
    CGPoint origin = CGPointMake(insetRect.origin.x, insetRect.origin.y);
    [image drawAtPoint:origin];

    // Clean up and get the new image.
    UIGraphicsPopContext();
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
Rockfish answered 30/10, 2015 at 3:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.