Draw UIImage with resizableEdgeInsets in center with stretched sides - uiview

I thought it was possible, since UIImageView does this just fine. Can I draw a UIImage in UIView's drawRect: method in a way that I center the image horizontally, and the sides of the UIImage are then stretched to fill the reminder of the UIView?
- (void)drawRect:(CGRect)rect {
[[[UIImage imageNamed:#"image"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2)] drawInRect:rect];
}
The whole image keeps stretching. For example, the image is 100 pixels wide. 90 of the pixels in the center cannot be stretched, but both sides have 5 pixels I would like to stretch on both sides to the egde of the rect.

That's not how resizableImageWithCapInsets: works, I'm afraid.
It's designed to keep the edges un-stretched and to tile the middle bit to fill the remaining space.
To achieve what I think you want, you will probably have to draw three images side-by-side.
Hope that helps.

Related

Resizing a UIView but drawing done by drawRect also changing size

I am testing a UIView using a UISlider as in the example images below:
I have a custom UIView with a yellow background that draws the gray square, the drawRect method is like so:
-(void)drawRect:(CGRect)rect{
NSLog(#"Draw rect called");
UIBezierPath* squarePath = [UIBezierPath bezierPathWithRect: CGRectMake(10, 10, 100, 100)];
[UIColor.grayColor setFill];
[squarePath fill];
}
And the method for my slide changing value:
- (IBAction)changeValue:(id)sender {
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, self.slider.value, self.slider.value);
self.tableView.transform = transform;
[self.tableView setNeedsDisplay];
}
I dont understand why the square is getting larger. I've noticed that drawRect is called every time the slider is moved. If this happens then why is the square size changing? Shouldn't it remain the same size and just the frame grow with the square in the top left corner?
My second question is, how would I change the code so just the frame grows and the drawing size stays the same? I ask this because actually I want the drawing size to change dynamically using my own code in drawRect.
Any pointers would be really appreciated! thanks!
The reason why the size of the square changes is because you've transformed it. Transformations don't just affect the frame of a view; they will affect the content. The square is getting drawn into its context at its constant size (100x100) and then the transform is stretching it before it gets rendered.
The reason why it's not expanding to the right and down is because by default the anchor point of a transform is the center of the bounds. Thus it'll scale from the center outwards. From the documentation:
The origin of the transform is the value of the center property ...
Transformations aren't intended to be used to simply scale the width and height of your frame. The frame property is for that. Simply store the view's frame in a variable, change its width and height, then set it back. In your drawRect: code you can check the dimensions of the rectangle that's given to you and make your square's width/height a percentage of that.

Transparent UILabel textColor on superview.superview (sort of)

I would like to achieve the same effect as in the new iTunes remote update for iOS 7 for my UILabel.
If you look at this screen:
(The one on the right) you'll notice that the UILabel text color is the background blurred album cover without the black mask.
At the moment I have transparent UILabel textColor (http://cl.ly/SInK), my code is similar to https://github.com/robinsenior/RSMaskedLabel
My first assumption is to have a view hierarchy like that
UIImageView (Light blurred image)
|
UIImageView/UIView (Dark blurred image or just a dark mask for the superview)
|
UILabel (And all my other view).
I would like the UILabel to have a transparent text color on the first UIImageView, ignoring the second one/Mask).
I can't wrap my head around a solution to achieve this effect.
Since iOS 8 Apple offer a new class, UIVibrancyEffect, which can be added to a UIVisualEffectView. This combined with a UIBlurEffect can achieve the exact same result as my screenshot above.
Source: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIVibrancyEffect/index.html
I don't think you're going to be able to directly do this with a UILabel. What I did was decompose it into several pieces:
How to draw only the background of a label. For this I cribbed code from CGPathRef from string to turn the string into a path. Then the subclassing UILabel and adding the following drawRect did the appropriate thing.
-(void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
// Convert the attributed text to a UIBezierPath
CGPathRef path = [self.attributedText createPath];
// Adjust the path bounds horizontally as requested by the view
CGRect bounds = [self textRectForBounds:rect limitedToNumberOfLines:self.numberOfLines];
CGAffineTransform xform = CGAffineTransformMakeTranslation(bounds.origin.x, bounds.origin.y);
// Adjust the path bounds vertically because it doesn't seem to be taken into account yet
bounds = CGPathGetBoundingBox(path);
xform = CGAffineTransformTranslate(xform, 0., (self.bounds.size.height - bounds.size.height) / 2.);
// Apply the transform to the path
path = CGPathCreateCopyByTransformingPath(path, &xform);
// Set colors, the fill color should be the background color because we're going
// to draw everything BUT the text, the text will be left clear.
CGContextSetFillColorWithColor(ctx, self.textColor.CGColor);
// Flip and offset things
CGContextScaleCTM(ctx, 1., -1.);
CGContextTranslateCTM(ctx, 0., 0. - self.bounds.size.height);
// Invert the path
CGContextAddRect(ctx, self.bounds);
CGContextAddPath(ctx, path);
CGContextDrawPath(ctx, kCGPathEOFill);
// Discard the path
CFRelease(path);
// Restore gstate
CGContextRestoreGState(ctx);
}
If you create a UILabel in interface builder, set the class, set the background color to clear and the foreground color to your proposed fill color, the background will show through where the text is.
To blur the body showing through the label, I dropped an FXBlurView (https://github.com/nicklockwood/FXBlurView) under the label with sizing constraints to keep them aligned.
You can see it all working here https://github.com/dwb357/TransparentLabel.
The color is not uniform. But it's actually "cut out" to let the blurred and tinted background shine through.
One way to do it is to construct a CGPath for the glyphs, clip the graphics context to them, then clear the context, leaving the area outside black and the area inside fhe glyths translucent.
An alternative approach is to create a pattern color from this large blurred tinted cover art and use this pattern color on a UILabel.

How to draw a UIBezierPath onto an image in iOS?

What's the easiest and least performance intensive way to achieve this?
Right now i have a UIBezierPath with over 60000 lines. I want to create an image from it that will later be moved around on-screen and stretched.
Just create a graphics context and draw your path into it, like this:
UIGraphicsBeginImageContextWithOptions(path.bounds.size, NO, 0.0); //size of the image, opaque, and scale (set to screen default with 0)
[path fill]; //or [path stroke]
UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
If you're planning on moving the image around, you'll need to draw it into your view, or preferably a UIImageView that's a subview of that view. This is faster than redrawing your view every time the user moves his finger.
Hope this helps!

How to smooth edges of drawn image?

I'm using this code to colorize some images of a UIButton subclass:
UIImage *img = [self imageForState:controlState];
// begin a new image context, to draw our colored image onto
UIGraphicsBeginImageContextWithOptions(img.size, NO, 0.0f);
// get a reference to that context we created
CGContextRef context = UIGraphicsGetCurrentContext();
// set the fill color
[self.buttonColor setFill];
CGContextSetAllowsAntialiasing(context, true);
CGContextSetShouldAntialias(context, true);
// translate/flip the graphics context (for transforming from CG* coords to UI* coords
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// set the blend mode to multiply, and the original image
CGContextSetBlendMode(context, kCGBlendModeScreen);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, rect, img.CGImage);
// set a mask that matches the shape of the image, then draw the colored image
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//return the colored image
[self setImage:coloredImg forState:controlState];
But the images come out with rough edges. I've tried using screen, lighten, and plusLighter blend modes, because some of the images have white parts that I want to stay white. The only part I want colorized is the black areas. I've attached the original button images, and after they've been colorized. I can't get the edges to look good. When I had them as white images that were colorized using multiply blend mode, it looked much better. But I want to use black so I can use one method for colorizing images with and without white in them. I tried with anti-aliasing, that didn't help either. It looks like it just isn't anti-aliasing it. I haven't worked with Core Graphics enough to know what's up with it.
EDIT
Here's what the original PNGs look like:
and here's what it should look like:
and here's what it does look like:
The size if different, but you can see the bad quality around the edges.
Maybe your original icons (PNGs?) are just "too sharp"? Could you show us? You just draw the image at its original size without resizing, so the problem could be right from the start.
I'm not sure what is what you are trying to accomplish here. Are you trying to round the edges of the images? If so, you are better of by changing the round corner property of the UIButton's layer. Since UIButton is a subclass of UIView, you can get its layer property and change the edge color and round its corner.

image manipulation using quartz2d

CGSize cgs = CGSizeMake(250.0, 300.0);
UIGraphicsBeginImageContext(cgs);
CGRect rectangle = CGRectMake(0,0,cgs.width,cgs.height);
UIImage *myImage = [UIImage imageNamed:#"BMW.jpg"];
[myImage drawInRect:rectangle];
[myImage release];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 5.0);
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextStrokeRect(context, rectangle);
UIImage *testImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[testImg drawAtPoint:CGPointMake(35, 10)];
i want to give gap between image and rectangle borders in which image is drawn, like 5 on left 5 on top and 5 on right but around 30 on bottom because its the space for text that user will write and user can also select that he/she want text on top so in that case top gap will be 30 and bottom gap will be 5. Here is the code it is inside subclass of uiview in drawrect method.
using this code border and image are tightly bound together, i am unable to give gap between border and image. Any idea how can i do that?
Thanks in advance.
First of all, you have a serious bug. This line is wrong:
[myImage release];
You don't own myImage, so you should not release it. You need to read “Memory-Management Rules” in Cocoa Core Competencies. Or you should turn on ARC (automatic reference counting), and then the compiler will take care of releasing things for you. Since ARC is supported since iOS 4.0, you should almost certainly be using it if you're developing a new app.
Regarding your image drawing, you just need to modify the rectangle in which you're drawing the image. For example, you can leave a 5 pixel border on top, left, and right, and a 30 point border on the bottom like this:
CGRect imageRect = CGRectInset(rectangle, 5, 5); // move 5 points in on all sides
imageRect.size.height -= 25; // make it another 25 points shorter
[myImage drawInRect:imageRect];
If you want the 30 point border on top, you also need to modify imageRect.origin.y.

Resources