Cropping and placing image to achieve battery level effect - ios

I am trying to achieve the battery level effect by drawing on a plain battery image another cropped image based on current battery level.
At the moment I am able to change the colour of the empty battery image but I don't know how to crop, the new one, and place it on top of the original.
I've found some useful links on how to crop but, every single of them, is cropping from top-left corner of the image. I would like to have as a starting point the bottom-left and set the height according to current level.
In addition, it is possible to combine or place the cropped image on top of the original?
Here is my original image.
...and what I am trying to achieve
At the moment I am trying this code,
int width = emptyBm.size.width;
int height = fullBm.size.height * level / 100;
UIImage *image = [UIImage imageNamed:#"battery_icon"];
UIImage *image2 = [UIImage imageNamed:#"battery_icon_full"];
CGRect rect = CGRectMake(0, image.size.height-height, width, height);
CGImageRef imageRef = CGImageCreateWithImageInRect([image2 CGImage], rect);
CGImageRef actualMask = CGImageMaskCreate(CGImageGetWidth([image CGImage]),
CGImageGetHeight([image CGImage]),
CGImageGetBitsPerComponent([image CGImage]),
CGImageGetBitsPerPixel([image CGImage]),
CGImageGetBytesPerRow([image CGImage]),
CGImageGetDataProvider([image CGImage]), NULL, false);
CGImageRef masked = CGImageCreateWithMask(imageRef, actualMask);
batteryBackground.image = [UIImage imageWithCGImage:masked];
However, because I am using CGImageCreateWithImageInRect the cropped part is stretched

Use the gray battery image as mask. Create a green rectangle and apply the mask you created. Here is a link to iOS masking: link
EDIT:
I hope this works (not tested):
int width = emptyBm.size.width;
int height = fullBm.size.height * level / 100;
UIImage *image = [UIImage imageNamed:#"battery_icon"];
UIImage *image2 = [UIImage imageNamed:#"battery_icon_full"];
// For clarity, just pretend the mask method I gave link to you is defined in your class.
UIImage *maskedImage = [self maskImage:image2 withMask:image];
// I assume the two imageviews are already connected to outlets.
self.backgroundImageView = [[UIImageView alloc] initWithImage:image];
self.foregroundImageView = [[UIImageView alloc] initWithImage:maskedImage];
// Make it's image stick to bottom so it won't stretch.
self.foregroundImageView.contentMode = UIViewContentModeBottom;
// Then calculate the frame again to reflect changes of battery status.
CGRect rect = self.backgroundImageView.frame;
rect.size.height = height;
// Push the masked imageview a to bottom as amount of missing battery height.
rect.origin.y = rect.origin.y + self.backgroundImageView.frame.size.height - height;
self.foregroundImageView.frame = rect;

Related

How to crop square area of an image in iOS?

I need to crop and straighten a rotated square area from an UIImage and then store to a new UIImage object. The following picture will make you understand what I want to do. With the locations of point A, B and the width of the square known, how to crop the area out and make it straight? Could you please show me the example code to accomplish this in detail?
Swift or Objective-C is OK. Thanks in advance.
Use This Code For Cropping the Image
// Create the image from a png file
UIImage *image = [UIImage imageNamed:#"myImage.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
// Get size of current image
CGSize size = [image size];
// Frame location in view to show original image
[imageView setFrame:CGRectMake(0, 0, size.width, size.height)];
[[self view] addSubview:imageView];
// Create rectangle that represents a cropped image
// from the middle of the existing image
CGRect rect = CGRectMake(size.width / 4, size.height / 4 ,
(size.width / 2), (size.height / 2));
// Create bitmap image from original image data,
// using rectangle to specify desired crop area
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], rect);
UIImage *img = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
// Create and show the new image from bitmap data
imageView = [[UIImageView alloc] initWithImage:img];
[imageView setFrame:CGRectMake(0, 200, (size.width / 2), (size.height / 2))];
[[self view] addSubview:imageView];

CGImageCreateWithImageInRect not cropping properly

I have an image which I want to cut in half, so that it is only the bottom half of the image. I've been trying to do this by cropping the image, however it doesn't seem to be doing anything, the image is the same size, no matter what crop I apply to it. Is there something that I'm doing wrong in my implementation? My code is below.
NSString *imageString = [NSString stringWithFormat:#"grey_%#", imagePath];
greyImage = [UIImage imageNamed:imageString];
int width = greyImage.size.width;
int height = greyImage.size.height;
CGRect myRect = CGRectMake(0, 0, width, height / 2);
CGImageRef ref = CGImageCreateWithImageInRect([greyImage CGImage], myRect);
cropGrey = [UIImage imageWithCGImage:ref scale:greyImage.scale orientation:greyImage.imageOrientation];
CGImageRelease(ref);
[self addCreatedImage:greyImage toSubview:badgeView atX:30 atY:30 atWidth:badgeViewWidth - 60 atHeight:(badgeViewWidth - 60) / 2 isRounded:NO];
Whether I use greyImage, or cropGrey the image shows up exactly the same. I'm not sure why this is, any help would be appreciated. Thank you!

Crop a Portion of UIImage from Larger UIImage, and include non-image parts

I think I may have an odd request, however hopefully someone can help. I am using the well known UIScrollView + UIImageView to zoom into and out of an image, as well as pan. This works fine and dandy, but the current project we have needs to be able to crop the image, but also include the black bars on the sides if the image is smaller than the crop rectangle. See the images below.
We wish to capture everything inside of the blue box, including the white (which will be black, since opaque is set to YES).
This works great for images that are completely zoomed out (The white is just the UIImageView's extra space).
However the problem arises when we try to zoom into the image, and capture only that portion, plus the empty space.
This results in the following image
The problem we are seeing is we need to be able to create an image that is exactly what is in the Crop Rect, regardless if there is part of the image there or not. The other problem is we wish to have the ability to dynamically change the output resolution. The aspect ratio is 16:9, and for this example kMaxWidth = 1136 and kMaxHeight = 639, however in the future we may want to request a larger or smaller 16:9 resolution.
Below is the function I have so far:
- (UIImage *)createCroppedImageFromImage:(UIImage *)image {
CGSize newRect = CGSizeMake(kMaxWidth, kMaxHeight);
UIGraphicsBeginImageContextWithOptions(newRect, YES, 0.0);
// 0 is the edge of the screen, to help with zooming
CGFloat xDisplacement = ((abs(0 - imageView.frame.origin.x) * kMaxWidth) / (self.cropSize.width / self.scrollView.zoomScale) / self.scrollView.zoomScale);
CGFloat yDisplacement = ((abs(self.cropImageView.frame.origin.y - imageView.frame.origin.y) * kMaxHeight) / (self.cropSize.height / self.scrollView.zoomScale) / self.scrollView.zoomScale);
CGFloat newImageWidth = (self.image.size.width * kMaxWidth) / (self.cropSize.width / self.scrollView.zoomScale);
CGFloat newImageHeight = (self.image.size.height * kMaxHeight) / (self.cropSize.height / self.scrollView.zoomScale);
[image drawInRect:CGRectMake(xDisplacement, 0, newImageWidth, newImageHeight)];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return croppedImage;
}
Any help would be greatly appreciated.
I ended up just taking a screenshot, and cropping that. It seems to work well enough.
- (UIImage *)cropImage {
CGRect cropRect = self.cropOverlay.cropRect;
UIGraphicsBeginImageContext(self.view.frame.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *fullScreenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRef croppedImage = CGImageCreateWithImageInRect(fullScreenshot.CGImage, cropRect);
UIImage *crop = [[UIImage imageWithCGImage:croppedImage] resizedImage:self.outputSize interpolationQuality:kCGInterpolationHigh];
CGImageRelease(croppedImage);
return crop;
}
If using iOS 7, you would use drawViewHierarchyInRect:afterScreenUpdates:, instead of renderInContext:
I think the translated rect for the image view isn't calculated properly. Since UIImageView is the subview inside the UIScrollView, you should be able to calculate the visible rect by calling [scrollView convertRect:scrollView.bounds toView:imageView];. That will be the visible rect of your image view. All you need to now is crop it.
-(UIImage*)cropImage:(UIImage*)img inRect:(CGRect)rect{
CGImageRef cropped = CGImageCreateWithImageInRect(img.CGImage, rect);
UIImage *image = [UIImage imageWithCGImage:cropped];
CGImageRelease(cropped);
return image;
}
Edit: Yeah... I forgot to mention that cropping should be done in (0,1) coordinate space. I've modified the crop function for you, so it crops the image based on all parameters you provided, UIImageView inside UIScrollView and an image.
-(UIImage*)cropImage:(UIImage*)image inImageView:(UIImageView*)imageView scrollView:(UIScrollView*)scrollView{
// get visible rect from image scrollview
CGRect visibleRect = [scrollView convertRect:scrollView.bounds toView:imageView];
UIImage* rCroppedImage;
CALayer* maskLayer= [[CALayer alloc] init];
maskLayer.contents= (id)image.CGImage;
maskLayer.frame= CGRectMake(0, 0, visibleRect.size.width, visibleRect.size.height);
CGRect rect= CGRectMake(visibleRect.origin.x / image.size.width,
visibleRect.origin.y / image.size.height,
visibleRect.size.width / image.size.width,
visibleRect.size.height / image.size.height);
maskLayer.contentsRect= rect;
UIGraphicsBeginImageContext(visibleRect.size);
CGContextRef context= UIGraphicsGetCurrentContext();
[maskLayer renderInContext:context];
rCroppedImage= UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return rCroppedImage;
}

How to tile a UIImage vertically while only streching horizontally on iOS?

I have an image that can be stretched in the horizontal direction, but need to be tiled vertically (one on top of the other) to fill my view. How could I do this?
I know that an image can be made resizable by using the -(UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets method. So that works great for the horizontal direction but that method cannot be used for the vertical direction it really needs to be tiled, and not stretched in the vertical direction to work.
Now my idea of how this could work is to run a loop that creates a UIImageView with the horizontally stretched image, and simply adjust the frame.origin.y property, then add it as a subview each loop until I've gone past the height of the view. However this seems like an overly complex way of doing this and really not practical when the view need to be resized (on an iPad rotation for example)
Is there a simpler more efficient way of doing this on iOS 6.x?
I used this to tile an image horizontally in a UIImageView:
UIImage *image = [UIImage imageNamed:#"my_image"];
UIImage *tiledImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) resizingMode:UIImageResizingModeTile];
UIImageView *imageView = [[UIImageView alloc] initWithImage:tiledImage];
This assumes you set the frame of the UIImageView to a larger size than your image asset. If the height is taller than your image, it will also tile vertically.
Have you considered using UIColor's method colorWithPatternImage: to create a repeating pattern and just pass an image with the correct horizontal width?
Code Example:
// On your desired View Controller
- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *patternImage = [UIImage imageNamed:#"repeating_pattern"];
self.view.backgroundColor = [UIColor colorWithPatternImage:patternImage];
// ... do your other stuff here...
}
UIImage *image = [UIImage imageNamed:#"test.png"];
UIImage *resizableImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(0, image.size.width / 2, 0, image.size.width / 2)];
So I've figured out a solution for this. It is I think a little jacky but it works.
Basically what I've done was to create my stretchable image with the left and right caps specified. And then I initialize a UIImageView with it. I then adjust the frame of the image view to the desired width. This will appropriately resize the image that is contained within it.
Then finally I used a piece of code I found that creates a new image by vertically tiling the adjusted image that is now contained in the image view. Notice how instead of accessing the original UIImage I am using the UIImageView view's image property.
The last thing then is to set the new UIImage as the patterned background colour of the view
Here is the code:
// create a strechable image
UIImage *image = [[UIImage imageNamed:#"progressBackgroundTop#2x.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 60, 0, 60)];
// create the image view that will contain it
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
CGRect frame = imageView.frame;
frame.size.width = self.view.bounds.size.width;
imageView.frame = frame;
// create the tiled image
CGSize imageViewSize = imageView.bounds.size;
UIGraphicsBeginImageContext(imageViewSize);
CGContextRef imageContext = UIGraphicsGetCurrentContext();
CGContextDrawTiledImage(imageContext, (CGRect){ CGPointZero, imageViewSize }, imageView.image.CGImage);
UIImage *finishedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.view.backgroundColor = [UIColor colorWithPatternImage:finishedImage];

How to set MediaItemArtwork Image Fill in UITableViewCell in iOS?

I am trying to enter MPMediaItemArtwork Image into UITableView's cell's ImageView with following code.
MPMediaItemArtwork *artwork = [[[self.arrayOfAlbums objectAtIndex:indexPath.row] representativeItem]valueForProperty:MPMediaItemPropertyArtwork];
UIImage *artworkImage = [artwork imageWithSize: cell.imageView.bounds.size];
if (artworkImage)
{
cell.imageView.image = artworkImage;
}
else
{
cell.imageView.image = [UIImage imageNamed: #"noArtwork.png"];
}
It's okay when i insert Artwork image in UITableView's cell ImageView.
But when my artwork image is too small or big , it's happened like following pic.
Not in completely fill in cell's ImageView.
You see that? I want to set Fill with Stretch like iOS Music App
Here is Build-in App Artwork Image that set Completely fill with Stretch
I want to do like that.
So i used cell.imageView.contentMode = UIViewContentModeScaleAspectFill; however it's not effect.
So how can i do that?
Thanks you for your work.
Try this for calculating a new and fitting image;
Adjust newRect to the rect of your cell.
// scale and center the image
CGSize sourceImageSize = [artworkImage size];
// The rectangle of the new image
CGRect newRect;
newRect = CGRectMake(0, 0, 40, 33);
// Figure out a scaling ratio to make sure we maintain the same aspect ratio
float ratio = MAX(newRect.size.width / sourceImageSize.width, newRect.size.height / sourceImageSize.height);
UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 1.0);
// Center the image in the thumbnail rectangle
CGRect projectRect;
projectRect.size.width = ratio * sourceImageSize.width;
projectRect.size.height = ratio * sourceImageSize.height;
projectRect.origin.x = (newRect.size.width - projectRect.size.width) / 2.0;
projectRect.origin.y = (newRect.size.height - projectRect.size.height) / 2.0;
[sourceImage drawInRect:projectRect];
UIImage *sizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
cell.imageView.image = sizedImage;
The line
UIImage *artworkImage = [artwork imageWithSize: cell.imageView.bounds.size];
creates an image with the size of the cell. So, it's scaled in this line and the image won't be bigger than the cell and therefore it will not scale afterwards.
I would leave the image at it's original size or at a size 2x the cell size and leave the scaling up to contentMode.
CGSize newSize = CGSizeMake(artwork.bounds.size.width, artwork.bounds.size.height)
UIImage *artworkImage = [artwork imageWithSize: newSize];

Resources