How do I join x number of UIImages together? - iphone

I'm looking to stitch some images together, with the images always being added to the bottom of the previous image.
e.g. I have images A,B and C. I would like them to appear on top of one another like:
A
B
C
What's the best way to do this?
Thanks!

I did this in the end:
+ (UIImage *)joinImages:(UIImage *)im1 secondImage:(UIImage *)im2 thirdImage:(UIImage *)im3
{
//Joins 3 UIImages together, stitching them vertically
CGSize size = CGSizeMake(320, 480);
UIGraphicsBeginImageContext(size);
CGPoint image1Point = CGPointMake(0, 0);
[im1 drawAtPoint:image1Point];
CGPoint image2Point = CGPointMake(0, im1.size.height);
[im2 drawAtPoint:image2Point];
CGPoint image3Point = CGPointMake(0, im1.size.height +im2.size.height);
[im3 drawAtPoint:image3Point];
UIImage* finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}

Create a UIView, then add multiple UIImageView instances as subviews. Set the image for each subview, and you're all set. If you have a fixed set of images (or at least a fixed number), you can do all of the layout in InterfaceBuilder. Otherwise, it's just a matter of creating a new UIImageView, then calling addSubView: on the parent view.
http://developer.apple.com/iPhone/library/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/addSubview:
If the set of images will be taller than the screen, you'll want to use a UITableView.

Related

divide image into two parts using divider

I'm working on one app where I need to divide a image into two part using a red line.
left part for labels
right part for prices
Question 1.
How can I draw a red line on image?
Question 2.
How can I divide image to two parts using red line ?( red line position is not fixed. user can move the position wherever it want)
Question 3.
How can I get line current position and how can I use that position two divide image
Thanks in advance
I would approach this in somewhat the same manner as koray was suggesting:
1) I am assuming that your above image/view is going to be managed by a view controller, which I will call ImageSeperatorViewController from here on.
Inside of ImageSeperatorViewController, insert koray's code in the -(void) viewDidLoad{} method. Make sure you change the imageToSplit variable to be an UIImageView instead of a plain UIView.
2) Next, I assume that you know how to detect user gestures. You will detect these gestures, and determine if the user has selected the view (i.e. bar in koray's code). Once you have determined if the user has selected bar, just update its origin's X position with the touch position.
CGRect barFrame = bar.frame;
barFrame.origin.x = *X location of the users touch*
bar.frame = barFrame;
3) For cropping, I would not use github.com/bilalmughal/NLImageCropper, it will not do what you need to do.
Try this on for size:
Header:
#interface UIImage (ImageDivider)
- (UIImage*)imageWithDividerAt:(CGFloat)position width:(CGFloat)width color:(UIColor*)color;
- (UIImage*)imageWithDividerAt:(CGFloat)position patternImage:(UIImage*)patternImage;
- (NSArray*)imagesBySlicingAt:(CGFloat)position;
#end
Implementation:
#implementation UIImage (ImageDivider)
- (UIImage*)imageWithDividerAt:(CGFloat)position patternImage:(UIImage*)patternImage
{
//pattern image
UIColor *patternColor = [UIColor colorWithPatternImage:patternImage];
CGFloat width = patternImage.size.width;
//set up context
UIGraphicsBeginImageContext(self.size);
CGContextRef context = UIGraphicsGetCurrentContext();
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//set the fill color from the pattern image color
CGContextSetFillColorWithColor(context, patternColor.CGColor);
//this is your divider's area
CGRect dividerRect = CGRectMake(position - (width / 2.0f), 0, width, self.size.height);
//the joy of image color patterns being based on 0,0 origin! must set phase
CGContextSetPatternPhase(context, CGSizeMake(dividerRect.origin.x, 0));
//fill the divider rect with the repeating pattern from the image
CGContextFillRect(context, dividerRect);
//get your new image and viola!
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage*)imageWithDividerAt:(CGFloat)position width:(CGFloat)width color:(UIColor *)color
{
//set up context
UIGraphicsBeginImageContext(self.size);
CGContextRef context = UIGraphicsGetCurrentContext();
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//set the fill color for your divider
CGContextSetFillColorWithColor(context, color.CGColor);
//this is your divider's area
CGRect dividerRect = CGRectMake(position - (width / 2.0f), 0, width, self.size.height);
//fill the divider's rect with the provided color
CGContextFillRect(context, dividerRect);
//get your new image and viola!
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (NSArray*)imagesBySlicingAt:(CGFloat)position
{
NSMutableArray *slices = [NSMutableArray array];
//first image
{
//context!
UIGraphicsBeginImageContext(CGSizeMake(position, self.size.height));
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//get your new image and viola!
[slices addObject:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
}
//second
{
//context!
UIGraphicsBeginImageContext(CGSizeMake(self.size.width - position, self.size.height));
//draw the existing image into the context
[self drawAtPoint:CGPointMake(-position, 0)];
//get your new image and viola!
[slices addObject:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
}
return slices;
}
The concept is simple - you want an image with the divider drawn over it. You could just overlay a view, or override drawRect:, or any number of any solutions. I'd rather give you this category. It just uses some quick Core Graphics calls to generate an image with your desired divider, be it pattern image or color, at the specified position. If you want support for horizontal dividers as well, it is rather trivial to modify this as such. Bonus: You can use a tiled image as your divider!
Now to answer your primary question. Using the category is rather self explanatory - just call one of the two methods on your source background to generate one with the divider, and then apply that image rather than the original source image.
Now, the second question is simple - when the divider has been moved, regenerate the image based on the new divider position. This is actually a relatively inefficient way of doing it, but this ought to be lightweight enough for your purposes as well as only being an issue when moving the divider. Premature optimization is just as much a sin.
Third question is also simple - call imagesBySlicingAt: - it will return an array of two images, as generated by slicing through the image at the provided position. Use them as you wish.
This code has been tested to be functional. I strongly suggest that you fiddle around with it, not for any purpose of utility, but to better understand the mechanisms used so that next time, you can be on the answering side of things
For Crop you can try this,
UIImage *image = [UIImage imageNamed:#"yourImage.png"];
CGImageRef tmpImgRef = image.CGImage;
CGImageRef topImgRef = CGImageCreateWithImageInRect(tmpImgRef, CGRectMake(0, 0, image.size.width, image.size.height / 2.0));
UIImage *topImage = [UIImage imageWithCGImage:topImgRef];
CGImageRelease(topImgRef);
CGImageRef bottomImgRef = CGImageCreateWithImageInRect(tmpImgRef, CGRectMake(0, image.size.height / 2.0, image.size.width, image.size.height / 2.0));
UIImage *bottomImage = [UIImage imageWithCGImage:bottomImgRef];
CGImageRelease(bottomImgRef);
hope this can help you, :)
if you want to draw a line you could just use a UIView with red background and make the height the size of your image and the width around 5 pixels.
UIView *imageToSplit; //the image im trying to split using a red bar
CGRect i = imageToSplit.frame;
int x = i.origin.x + i.size.width/2;
int y = i.origin.y;
int width = 5;
int height = i.size.height;
UIView *bar = [[[UIView alloc] initWithFrame:CGRectMake(x, y, width, height)] autorelease];
bar.backgroundColor = [UIColor redColor];
[self.view addSubview:bar];

Rendering views, scaled #2x, renderInContext, iPhone

I have a view (called outPutView) that contains graphics, like uIImageViews and labels. I need to render an image of the outPutView and it's sub-views. I am using renderInContext:UIGraphicsGetCurrentContext() to do so. Works fine, except that I need to scale the views. I am using a transform on the outPutView. This successfully scales the view and it's sub-views, but the transform does not render. While the views are scaled onscreen. the final render displays the vies at their original size, while the render context is at the target size (here #2x iPhone view size).
Thanks for reading!!
[outPutView setTransform:CGAffineTransformMake(2, 0, 0, 2, 0, 0)];
CGSize renderSize = CGSizeMake(self.view.bounds.size.width*2, self.view.bounds.size.height*2);
UIGraphicsBeginImageContext(renderSize);
[[outPutView layer] renderInContext:UIGraphicsGetCurrentContext()];
renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I've just made this work, although in the opposite direction (scaling down). Here's a summary of the relevant code:
// destination size is half of self (self is a UIView)
float rescale = 0.5;
CGSize resize = CGSizeMake(self.width * rescale, self.height * rescale);
// make the destination context
UIGraphicsBeginImageContextWithOptions(resize, YES, 0);
// apply the scale to dest context
CGContextScaleCTM(UIGraphicsGetCurrentContext(), rescale, rescale);
// render self into dest context
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
// grab the resulting UIImage
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
To scale up instead of down, it should be fine to have rescale = 2.
I solved this by re-ordering my views. Actually adding another view between the output view: the view that the rendered context is taken from, and the view that is scaled via transform. It worked, but I have no idea why at this point. Any thoughts on this would be appreciated. Thanks for reading.

Draw image constrain aspects

I have an imageview, the image within this imageview is for you to choose from the photoroll. I also have a button, when you click this button, there's an image added to a view with the addSubview code. This piece of image is draggable, resizeable and rotatable.
One problem, when I finish the image I use the method drawInRect. This draws all the layers onto eachother and creates an image. However the layers are on the wrong place and are the wrong size. It's also never rotated. I don't know how to fix this, the piece of code is beneath this text. Is it possible to keep the original image size and still have the layers drawn on the same place I drag them onto the imageview, if not how do I create a new size for this and have the result I want. And how do I draw the image rotated.
UIGraphicsBeginImageContext(imageView2.image.size);
// Draw image1
[imageView2.image drawInRect:CGRectMake(0, 0, imageView2.image.size.width, imageView2.image.size.height)];
// Draw image2
for(UIImageView *viewsSub in [self.imageViewer subviews])
{
[viewsSub.image drawInRect:CGRectMake(viewsSub.frame.origin.x, viewsSub.frame.origin.y, viewsSub.frame.size.width, viewsSub.frame.size.height)];
}
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
pld.imageChosen2 = resultingImage;
UIGraphicsEndImageContext();
So you want something like taking a "screenshot" of your actual imageview (with subviews included), don't you?
I used this piece of code to do something similar, but don't know if would work for you.
- (UIImage *)screenshot {
CGFloat scale = [UIScreen screenScale];
if(scale > 1.5) {
UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, scale);
} else {
UIGraphicsBeginImageContext(self.frame.size);
}
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenshot;
}
You should add this method inside your imageview (the one that contains all the subviews you're adding).

How to render UIView into 2 parts

I am trying to develop transition effect, when one view splits into 2 parts with upper part animated upwards and lower part animated downwards to revel the view behind it.
I am using UIView and UIImageView approach to accomplish that:
// 1. Make a screenshot:
UIGraphicsBeginImageContext(parentView.frame.size);
[parentView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// 2. Calculate rectangles for top and bottom part:
CGRect rectTop, rectBottom;
CGFloat W = rectBig.size.width;
CGFloat H = rectBig.size.height;
rectTop = CGRectMake(0, 0, W, y_cutoff);
rectBottom = CGRectMake(0, y_cutoff, W, H - y_cutoff);
// 3. Create top and bottom images:
CGImageRef imageRefTop = CGImageCreateWithImageInRect([screenshot CGImage], rectTop);
CGImageRef imageRefBottom = CGImageCreateWithImageInRect([screenshot CGImage], rectBottom);
UIImage *imageTop = [UIImage imageWithCGImage:imageRefTop];
UIImage *imageBottom = [UIImage imageWithCGImage:imageRefBottom];
// 4. Assign images to image views:
imageViewTop.image = imageTop;
imageViewBottom.image = imageBottom;
// 5. Animate image views:
[UIView beginAnimation:nil context:NULL];
.... animation code here
[UIView commitAnimations];
This code however, is extremely slow on the device and I am sure there is a more efficient way to implement such a transition. Most probably using CALayers, etc..
Can you point me to the right direction?
This is not animation. Your drawing code is slow. If you profile, you will see that renderInContext: (btw. always executed on the main thread) and CGImageCreateWithImageInRect are the limiting factors. What your view (the one you want to split) is about? Is it an option to create two views instead of one from start?
The animation itself shouldn't be slow. All you really need to be doing is changing the Y position of the two views for the animation.
Why don't you try putting static images into the UIImageViews and seeing how the animation performs then?
If the lag is happening when you are creating the images, maybe you should consider moving that code to a separate thread so that the main thread doesn't freeze up. When the images are done being created, notify the main thread, put them in the UIImageViews, and perform the animation.

How to I rotate UIImageView by 90 degrees inside a UIScrollView with correct image size and scrolling?

I have an image inside an UIImageView which is within a UIScrollView. What I want to do is rotate this image 90 degrees so that it is in landscape by default, and set the initial zoom of the image so that the entire image fits into the scrollview and then allow it to be zoomed up to 100% and back down to minimum zoom again.
This is what I have so far:
self.imageView.transform = CGAffineTransformMakeRotation(-M_PI/2);
float minimumScale = scrollView.frame.size.width / self.imageView.frame.size.width;
scrollView.minimumZoomScale = minimumScale;
scrollView.zoomScale = minimumScale;
scrollView.contentSize = CGSizeMake(self.imageView.frame.size.height,self.imageView.frame.size.width);
The problem is that if I set the transform, nothing shows up in the scrollview. However if I commented out the transform, everything works except the image is not in the landscape orientation that I want it to be!
If I apply the transform and remove the code that sets the minimumZoomScale and zoomScale properties, then the image shows up in the correct orientation, however with the incorrect zoomScale and seems like the contentSize property isn't set correctly either - since the doesn't scroll to the edge of the image in the left/right direction, however does top and bottom but much over the edge.
NB: image is being loaded from a URL
Maybe rotating the image itself fits your needs:
UIImage* rotateUIImage(const UIImage* src, float angleDegrees) {
UIView* rotatedViewBox = [[UIView alloc] initWithFrame: CGRectMake(0, 0, src.size.width, src.size.height)];
float angleRadians = angleDegrees * ((float)M_PI / 180.0f);
CGAffineTransform t = CGAffineTransformMakeRotation(angleRadians);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
[rotatedViewBox release];
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
CGContextRotateCTM(bitmap, angleRadians);
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-src.size.width / 2, -src.size.height / 2, src.size.width, src.size.height), [src CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
I believe the easiest way (and thread safe too) is to do:
//assume that the image is loaded in landscape mode from disk
UIImage * LandscapeImage = [UIImage imageNamed: imgname];
UIImage * PortraitImage = [[UIImage alloc] initWithCGImage: LandscapeImage.CGImage
scale: 1.0
orientation: UIImageOrientationLeft];
Any calculations that you do based on the imageView's frame should probably be done before you apply any transformations to it. But I would actually suggest doing those calculations based on the size of the UIImage, not the UIImageView. Then set both the UIImageView's frame and the UIScrollView's contentSize based on that.
Max's suggestion is a good one, although with a larger image it could be a performance killer. Are you displaying this image from your app's resources? If so, why not just rotate the images before you even build the app?
There's a much easier solution that is also faster, just do this:
- (void) imageRotateTapped:(id)sender
{
[UIView animateWithDuration:0.33f animations:^()
{
self.imageView.transform = CGAffineTransformMakeRotation(RADIANS(self.rotateDegrees += 90.0f));
self.imageView.frame = self.imageView.superview.bounds; // change this to whatever rect you want
}];
}
When the user is done, you will need to actually create a new rotated image, but that is very easy to do.
I was using the accepted answer for a while until we noticed that non-square rotations based on images taken directly from the camera seemed stretched (they were rotated as desired, just the frame width/height wasn't adjusted).
Great explanation/post here from Trevor: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
In the end, it was a very simple import of Trevor's code which uses categories to add a resizedImage:interpoationQuality method to UIImage. So yeah, user beware, if it still works for you, great. But if it doesn't, I'd take a look at the library instead.