35

I would like to know what to do to save 2 images into 1 image.

One of the photos can be moved, rotated and zoomed in/out...

I'm doing this, but it basically captures all the stuff on the screen including my buttons...

UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *savedImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Arnaud
  • 7,259
  • 10
  • 50
  • 71
Desmond
  • 5,001
  • 14
  • 56
  • 115

8 Answers8

69

You can create graphics context and draw both images in it. You'll get an image result from both your source images combined.

- (UIImage*)imageByCombiningImage:(UIImage*)firstImage withImage:(UIImage*)secondImage {
    UIImage *image = nil;

    CGSize newImageSize = CGSizeMake(MAX(firstImage.size.width, secondImage.size.width), MAX(firstImage.size.height, secondImage.size.height));
    if (UIGraphicsBeginImageContextWithOptions != NULL) {
        UIGraphicsBeginImageContextWithOptions(newImageSize, NO, [[UIScreen mainScreen] scale]);
    } else {
        UIGraphicsBeginImageContext(newImageSize); 
    }
    [firstImage drawAtPoint:CGPointMake(roundf((newImageSize.width-firstImage.size.width)/2), 
                                        roundf((newImageSize.height-firstImage.size.height)/2))]; 
    [secondImage drawAtPoint:CGPointMake(roundf((newImageSize.width-secondImage.size.width)/2), 
                                         roundf((newImageSize.height-secondImage.size.height)/2))]; 
    image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}
Maziyar
  • 1,913
  • 2
  • 18
  • 37
Vlad
  • 3,346
  • 2
  • 27
  • 39
  • 1
    Hi Vlad, Thanks for the code. it works, but is does not save any movement or rotation i edited. Also it save in the picture's aspect ratio. any suggestion ? – Desmond Feb 14 '12 at 01:33
  • glad it works. Not sure though what you're after? at you editing the images on runtime ? or just zoom, move and rotate? – Vlad Feb 15 '12 at 16:43
  • just zoom, move and rotate. then save for bumping – Desmond Feb 16 '12 at 00:47
  • 1
    This function fails when the two images have the same size (then MAX(first-width, second-width) results in 0; Also, the drawAtPoint functions seem to be borked. I had to exchange them with this: [firstImage drawAtPoint:CGPointMake(0,0)]; [secondImage drawAtPoint:CGPointMake(0, firstImage.size.height)]; This only works if the two images have the same width! – Swissdude Sep 24 '12 at 13:06
  • It will not fail, it will draw both images starting {0, 0}, overlapped. Coordinates are relative to the container view (ex: image inside imageView). – Vlad Sep 24 '12 at 15:06
  • hello! this great code helped me very much. But I'm not sure its really necessary to use roundf() function. It may cause to round error. Maybe not error exactly but unexpected result. – Alexey Voitenko Sep 12 '13 at 13:49
  • Here's a gist for the same logic in swift :) https://gist.github.com/A-Zak/3c38d3f83f911a25790f – Alex Zak Oct 19 '15 at 11:27
  • 1
    for me the image size increases 6 to 7 times. Any way to keep the size less with same quality? – Zaraki Sep 04 '16 at 15:43
55

Swift 4.2

let topImage = UIImage(named: "image1.png")
let bottomImage = UIImage(named: "image2.png")

let size = CGSize(width: topImage!.size.width, height: topImage!.size.height + bottomImage!.size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)

topImage!.draw(in: CGRect(x: 0, y: 0, width: size.width, height: topImage!.size.height))
bottomImage!.draw(in: CGRect(x: 0, y: topImage!.size.height, width: size.width, height: bottomImage!.size.height))

let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()

//set finalImage to IBOulet UIImageView
mergeImage.image = newImage

Objective-C

UIImage *image1 = [UIImage imageNamed:@"image1.png"];
UIImage *image2 = [UIImage imageNamed:@"image2.png"];

CGSize size = CGSizeMake(image1.size.width, image1.size.height + image2.size.height);

UIGraphicsBeginImageContext(size);

[image1 drawInRect:CGRectMake(0,0,size.width, image1.size.height)];
[image2 drawInRect:CGRectMake(0,image1.size.height,size.width, image2.size.height)];

UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

//set finalImage to IBOulet UIImageView
imageView.image = finalImage;
Himanshu padia
  • 7,428
  • 1
  • 47
  • 45
11
  1. Create a subview for adding images.
  2. Add all your images in that view instead of the main view.
  3. Let the buttons and other stuff stay on the main view.
  4. Render only the view with images in the bitmap context instead of the main view like you are doing right now.
Nishu_Priya
  • 1,251
  • 1
  • 10
  • 23
Swapnil Luktuke
  • 10,385
  • 2
  • 35
  • 58
10

Swift 3

In this example the frontImage is being drawn inside the other image using a insetBy of 20% margin.

The background image must be drawn before and then the front image in sequence.

I used this to place a "Play" icon image in front a video frame image inside a UIImageView like below:

enter image description here

Usage:

self.image = self.mergedImageWith(frontImage: UIImage.init(named: "play.png"), backgroundImage: UIImage.init(named: "backgroundImage.png")))

Method:

func mergedImageWith(frontImage:UIImage?, backgroundImage: UIImage?) -> UIImage{

    if (backgroundImage == nil) {
        return frontImage!
    }

    let size = self.frame.size
    UIGraphicsBeginImageContextWithOptions(size, false, 0.0)

    backgroundImage?.draw(in: CGRect.init(x: 0, y: 0, width: size.width, height: size.height))
    frontImage?.draw(in: CGRect.init(x: 0, y: 0, width: size.width, height: size.height).insetBy(dx: size.width * 0.2, dy: size.height * 0.2))

    let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return newImage
}
mourodrigo
  • 2,232
  • 28
  • 18
9

You can use this method, which is very dynamic and you can specify the starting position of the second image and total size of the image.

-(UIImage *) addImageToImage:(UIImage *)img withImage2:(UIImage *)img2 andRect:(CGRect)cropRect withImageWidth:(int) width     
{

    CGSize size = CGSizeMake(width,40);
    UIGraphicsBeginImageContext(size);

    CGPoint pointImg1 = CGPointMake(0,0);
    [img drawAtPoint:pointImg1];

     CGPoint pointImg2 = cropRect.origin;
     [img2 drawAtPoint: pointImg2];

     UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();
     return result;
}
P.J.Radadiya
  • 1,493
  • 1
  • 12
  • 21
Parvez Belim
  • 1,003
  • 13
  • 36
4

Here's a method for UIImage extension to combine multiple images:

class func combine(images: UIImage...) -> UIImage {
    var contextSize = CGSizeZero

    for image in images {
        contextSize.width = max(contextSize.width, image.size.width)
        contextSize.height = max(contextSize.height, image.size.height)
    }

    UIGraphicsBeginImageContextWithOptions(contextSize, false, UIScreen.mainScreen().scale)

    for image in images {
        let originX = (contextSize.width - image.size.width) / 2
        let originY = (contextSize.height - image.size.height) / 2

        image.drawInRect(CGRectMake(originX, originY, image.size.width, image.size.height))
    }

    let combinedImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return combinedImage
}

Example:

UIImage.combine(image1, image2)
Alexander Borisenko
  • 1,574
  • 1
  • 12
  • 16
2

Swift-3 (IOS10.3)

extension UIImage {
    func combineWith(image: UIImage) -> UIImage {
        let size = CGSize(width: self.size.width, height: self.size.height + image.size.height)
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)

        self.draw(in: CGRect(x:0 , y: 0, width: size.width, height: self.size.height))
        image.draw(in: CGRect(x: 0, y: self.size.height, width: size.width,  height: image.size.height))

        let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return newImage
    }
}

Usage

let image1 = UIImage(named: "image1.jpg")
let image2 = UIImage(named: "image2.jpg")

yourImageView.image = image1?.combineWith(image: image2)
ramchandra n
  • 1,957
  • 1
  • 15
  • 15
0

Based on Himanshu padia's response

If you want to "dynamically" combine more images (with the same aspect ratio) into one grid in Objective-C

I have used only two for this example on even/odd slots.

Formulas for equal inline and outline border:

// psx = (x - (n+1)*bs / n)
// psy = (y - (m+1)*bs / m)

Formulas for different inline and outline border

// psx = (x - (n-1)*bs + obs / n)
// psy = (y - (m-1)*bs + obs / m)

Explanation:

  • psx, psy - piece size X and y
  • x, y - original (piece) image size
  • n, m - pieces slots in grid
  • bs - border size
  • obs - outline border size

  • Why n+1 ? Because for three pieces, you need 4 borders |*|*|*|

  • Why n-1 ? Because same as above, but excluding the first and last border !*|*|*!

Code:

UIImage *image1 = [UIImage imageNamed:@"14x9blue"];
UIImage *image2 = [UIImage imageNamed:@"14x9red"];

// grid parameters
int k =0;
int m=3,n = 3;
int i=0, j=0;

CGFloat borderSize = 20.0f;
//  equal border inline and outline
// the 1 is a multiplier for easier and more dynamic sizes
// 0*borderSize is inline border only, 1 is equal, 2 is double, etc.
CGFloat outlineBorder = 1*borderSize;

CGSize size = CGSizeMake(self.gridImageView.image.size.width, self.gridImageView.image.size.height);
CGRect gridImage = CGRectMake(0,0, size.width, size.height);


// piece size
// border inline and outline    
// psx = (x - (n-1)*bs + obs / n)
// psy = (y - (m-1)*bs + obs / m)
CGFloat pieceSizeX = (size.width - (n-1)*borderSize - 2*outlineBorder) / n;
CGFloat pieceSizeY = (size.height - (m-1)*borderSize - 2*outlineBorder) / m;




UIGraphicsBeginImageContext(size);
// semi transparent fill
[[UIColor colorWithDisplayP3Red:240 green:250 blue:0 alpha:0.5] setFill];
UIRectFill(CGRectMake(0,0, size.width, size.height));

UIImage *currentImage;
for(i=0; i<m; i++) {
    for (j=0; j<n; j++) {
        if (k++%2) {
            currentImage = image1;
        } else {
            currentImage = image2;
        }
        // 10-60 , 70-120, 130-180
        [currentImage drawInRect:CGRectMake(
                outlineBorder + (i)*borderSize + i*pieceSizeX,
                outlineBorder + (j)*borderSize + j*pieceSizeY,
                pieceSizeX,
                pieceSizeY
        )
        ];
    }
}

UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

//set finalImage to IBOulet UIImageView
self.gridImageView.image = finalImage;

Grid with equal borders

Gabi Dj
  • 645
  • 6
  • 15