Is this possible to add bevel text effect as the image shown here in an app?
I can use images to show beveled text but right now I need to do this programmatically.
You can draw text with help of
+(UIImage*) drawText:(NSString*) text
inImage:(UIImage*) image
atPoint:(CGPoint) point
{
UIFont *font = [UIFont boldSystemFontOfSize:12];
UIGraphicsBeginImageContext(image.size);
[image drawInRect:CGRectMake(0,0,image.size.width,image.size.height)];
CGRect rect = CGRectMake(point.x, point.y, image.size.width, image.size.height);
[[UIColor whiteColor] set];
[text drawInRect:CGRectIntegral(rect) withFont:font];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
but make sure you add shadow properly. If needed add more than one text with different shadow.
Related
I'm writing text onto an image like so:
-(UIImage *)addLabelToImage:(UIImage *)img
{
UIFont *font = [UIFont boldSystemFontOfSize:20];
UIGraphicsBeginImageContext(img.size);
[img drawInRect:CGRectMake(0, 0,img.size.width,img.size.height)];
CGRect rect = CGRectMake(60, 550, img.size.width, img.size.height);
[[UIColor whiteColor] set];
[[NSString stringWithFormat:#"%#, %#", [[NSUserDefaults standardUserDefaults] valueForKey:#"whatever"], [[NSUserDefaults standardUserDefaults] valueForKey:#"whatever2"]] drawInRect:CGRectIntegral(rect) withFont:font];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
How would I add a drop shadow to this text? I know I can just redraw the text in black shifted over a few pixels, but is there an easier way?
Cheers,
George
Before you draw your text, you can set a shadow like this:
//...
CGContextRef c = UIGraphicsGetCurrentContext();
CGSize offset = CGSizeMake(0, 1);
CGFloat blur = 2.0;
UIColor *shadowColor = [UIColor blackColor];
CGContextSetShadowWithColor(c, offset, blur, [shadowColor CGColor]);
//draw your text ...
Adjust the offset, blur and shadowColor parameters as needed to achieve the effect you want. For a sharp shadow (like on a UILabel), set blur to 0.
Can I know how to have the emboss effect as the text "Reminders" as shown on the picture?
It looks like the text are embedded?
Thanks
UPDATE FOR iOS 7.0
In iOS 7.0, Apple added a new attribute, NSTextEffectAttributeName, for attributed strings. If your deployment target is iOS 7.0 or later, you can set this attribute to NSTextEffectLetterpressStyle to draw an attributed string in an embossed style.
ORIGINAL
I can't say for certain how Apple draws the embossed text. It looks to me like they fill the string glyphs with a reddish color, then apply a shadow around the interior edges of the glyphs, and also apply a very faint shadow along the top outside edges of the glyphs. I tried it out and here's what it looks like:
On top is my rendering. Below that is a simple UILabel with shadow as Chris suggested in his answer. I put a screen shot of the Reminders app in the background.
Here's my code.
First, you need a function that creates an image mask of your string. You'll use the mask to draw the string itself, and then to draw a shadow that only appears around the inside edges of the string. This image just has an alpha channel and no RGB channels.
- (UIImage *)maskWithString:(NSString *)string font:(UIFont *)font size:(CGSize)size
{
CGRect rect = { CGPointZero, size };
CGFloat scale = [UIScreen mainScreen].scale;
CGColorSpaceRef grayscale = CGColorSpaceCreateDeviceGray();
CGContextRef gc = CGBitmapContextCreate(NULL, size.width * scale, size.height * scale, 8, size.width * scale, grayscale, kCGImageAlphaOnly);
CGContextScaleCTM(gc, scale, scale);
CGColorSpaceRelease(grayscale);
UIGraphicsPushContext(gc); {
[[UIColor whiteColor] setFill];
[string drawInRect:rect withFont:font];
} UIGraphicsPopContext();
CGImageRef cgImage = CGBitmapContextCreateImage(gc);
CGContextRelease(gc);
UIImage *image = [UIImage imageWithCGImage:cgImage scale:scale orientation:UIImageOrientationDownMirrored];
CGImageRelease(cgImage);
return image;
}
Second, you need a function that inverts that mask. You'll use this to make CoreGraphics draw a shadow around the inside edges of the string. This needs to be a full RGBA image. (iOS doesn't seem to support grayscale+alpha images.)
- (UIImage *)invertedMaskWithMask:(UIImage *)mask
{
CGRect rect = { CGPointZero, mask.size };
UIGraphicsBeginImageContextWithOptions(rect.size, NO, mask.scale); {
[[UIColor blackColor] setFill];
UIRectFill(rect);
CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage);
CGContextClearRect(UIGraphicsGetCurrentContext(), rect);
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
You can use those in a function that draws the string in red and applies a shadow to its interior edges.
-(UIImage *)imageWithInteriorShadowAndString:(NSString *)string font:(UIFont *)font textColor:(UIColor *)textColor size:(CGSize)size
{
CGRect rect = { CGPointZero, size };
UIImage *mask = [self maskWithString:string font:font size:rect.size];
UIImage *invertedMask = [self invertedMaskWithMask:mask];
UIImage *image;
UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale); {
CGContextRef gc = UIGraphicsGetCurrentContext();
// Clip to the mask that only allows drawing inside the string's image.
CGContextClipToMask(gc, rect, mask.CGImage);
// We apply the mask twice because we're going to draw through it twice.
// Only applying it once would make the edges too sharp.
CGContextClipToMask(gc, rect, mask.CGImage);
mask = nil; // done with mask; let ARC free it
// Draw the red text.
[textColor setFill];
CGContextFillRect(gc, rect);
// Draw the interior shadow.
CGContextSetShadowWithColor(gc, CGSizeZero, 1.6, [UIColor colorWithWhite:.3 alpha:1].CGColor);
[invertedMask drawAtPoint:CGPointZero];
invertedMask = nil; // done with invertedMask; let ARC free it
image = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
return image;
}
Next you need a function that takes an image and returns a copy with a faint upward shadow.
- (UIImage *)imageWithUpwardShadowAndImage:(UIImage *)image
{
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); {
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, -1), 1, [UIColor colorWithWhite:0 alpha:.15].CGColor);
[image drawAtPoint:CGPointZero];
}
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
Finally, you can combine those functions to create an embossed image of your string. I put my final image into a UIImageView for easy testing.
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect rect = self.imageView.bounds;
NSString *string = #"Reminders";
UIFont *font = [UIFont systemFontOfSize:33];
UIImage *interiorShadowImage = [self imageWithInteriorShadowAndString:string
font:font
textColor:[UIColor colorWithHue:0 saturation:.9 brightness:.7 alpha:1]
size:rect.size];
UIImage *finalImage = [self imageWithUpwardShadowAndImage:interiorShadowImage];
self.imageView.image = finalImage;
}
That just looks like a shadow around the text. You set it in IB in the same area where you set the text color - pick an appropriate color for the shadow and set how you want the shadow offset (in the example you posted, it looks like they set the shadow color to the same color as the text and offset it 0 horizontal and -1 vertical (which means one pixel up).
In code, the properties are set like this (assuming you have already set up a UILabel named, appropriately, "label":
label.shadowColor = [UIColor blackColor]; // Choose your color here - in the example
// posted, they probably chose a similar color
// to the text color and then set the alpha
// down around 0.7 or so so the shadow would be
// faint.
label.shadowOffset = CGSizeMake(0,-1); // First parameter is horizontal, second is vertical
You can configure your effect on the basis of this example
I am wanting to draw an NSString and a border onto a UIImage that I already have. I found a method that will draw an NSString as a UIImage, but I need it to draw on an image that I provide.
-(UIImage *)imageFromText:(NSString *)text
{
// set the font type and size
UIFont *font = [UIFont systemFontOfSize:20.0];
CGSize size = [text sizeWithFont:font];
// check if UIGraphicsBeginImageContextWithOptions is available (iOS is 4.0+)
if (UIGraphicsBeginImageContextWithOptions != NULL)
UIGraphicsBeginImageContextWithOptions(size,NO,0.0);
else
// iOS is < 4.0
UIGraphicsBeginImageContext(size);
// optional: add a shadow, to avoid clipping the shadow you should make the context size bigger
//
// CGContextRef ctx = UIGraphicsGetCurrentContext();
// CGContextSetShadowWithColor(ctx, CGSizeMake(1.0, 1.0), 5.0, [[UIColor grayColor] CGColor]);
// draw in context, you can use also drawInRect:withFont:
[text drawAtPoint:CGPointMake(0.0, 0.0) withFont:font];
// transfer image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
How would I modify this method to provide my own background image, as well as adding a border?
If you are displaying the UIImage in a UIImageView you can set the UIImageView.layer.delegate and use something like:
- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CGContextSetFillColorWithColor(ctx, [[UIColor darkTextColor] CGColor]);
UIGraphicsPushContext(ctx);
[word drawAtPoint:CGPointMake(30.0f, 30.0f)
forWidth:200.0f
withFont:[UIFont boldSystemFontOfSize:32]
lineBreakMode:UILineBreakModeClip];
UIGraphicsPopContext();
}
Code from Add text to CALayer
The border is easy, just use the CALayer properties:
imageview.layer.borderColor = [UIColor blackColor].CGColor;
imageview.sublayer.borderWidth = 2.0;
You are looking for CALayers.
Here is very good tutorial how to create and use them.
So basically you will add new CALayer with image as a background and then draw on it text.
http://www.raywenderlich.com/2502/introduction-to-calayers-tutorial
Use this function to Draw NSString and border on UIImage
For border check CGContextSetRGBStrokeColor
-(UIImage *)imageFromText:(NSString *)text
{
// set the font type and size
UIFont *font = [UIFont systemFontOfSize:20.0];
CGSize size = [text sizeWithFont:font];
// check if UIGraphicsBeginImageContextWithOptions is available (iOS is 4.0+)
if (UIGraphicsBeginImageContextWithOptions != NULL)
UIGraphicsBeginImageContextWithOptions(size,NO,0.0);
else
// iOS is < 4.0
UIGraphicsBeginImageContext(size);
// optional: add a shadow, to avoid clipping the shadow you should make the context size bigger
//
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetShadowWithColor(ctx, CGSizeMake(1.0, 1.0), 5.0, [[UIColor brownColor] CGColor]);
// draw in context, you can use also drawInRect:withFont:
[text drawAtPoint:CGPointMake(0.0, 0.0) withFont:font];
//CGImageRef cimg = UIGraphicsGetCurrentContext();
// transfer image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
[image drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0];
//CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx, 2.0, 3.5, 5.0, 1.0);
CGContextStrokeRect(ctx, rect);
UIImage *testImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return testImg;
}
I'm having a problem with NSString drawInRect method.
I have a very large text to be painted over a 768x1024 size Rect and I obtain this:
As you can see the only text line that reaches the whole screen is the last one, I don't know why other lines don't.
This is my code:
//The drawing rectangle
CGRect textRect = CGRectMake(1, 4, 768, 1024);
[[UIColor blackColor] set];
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
//texto_completo is a very large text String
[texto_completo drawInRect:textRect withFont:font lineBreakMode:UILineBreakModeCharacterWrap alignment:UITextAlignmentLeft];
CGImageRef cgImage = CGBitmapContextCreateImage(context);
UIImage *img_con_texto = [[UIImage alloc] initWithCGImage:cgImage];
UIGraphicsPopContext();
CGContextRelease(context);
CGImageRelease(cgImage);
Could you help me understand this behaviour?
It looks like the APple algorithm is over aggressive.
The best solution is to set the alignment to UITextAlignmentCenter, that is the only way top get the characters flush right. In my test the lime breaks were the same as with alignment UITextAlignmentLeft.
is it possible to create such a UILabel with inner and outer shadow?
alt text http://dl.getdropbox.com/u/80699/Bildschirmfoto%202010-07-12%20um%2021.28.57.png
i only know shadowColor and shadowOffset
zoomed:
alt text http://dl.getdropbox.com/u/80699/Bildschirmfoto%202010-07-12%20um%2021.39.56.png
thanks!
The answer by dmaclach is only suitable for shapes that can easily be inverted. My solution is a custom view that works with any shape and also text. It requires iOS 4 and is resolution independent.
First, a graphical explanation of what the code does. The shape here is a circle.
The code draws text with a white dropshadow. If it's not required, the code could be refactored further, because the dropshadow needs to be masked differently. If you need it on an older version of iOS, you would have to replace the block and use an (annoying) CGBitmapContext.
- (UIImage*)blackSquareOfSize:(CGSize)size {
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
[[UIColor blackColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height));
UIImage *blackSquare = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return blackSquare;
}
- (CGImageRef)createMaskWithSize:(CGSize)size shape:(void (^)(void))block {
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
block();
CGImageRef shape = [UIGraphicsGetImageFromCurrentImageContext() CGImage];
UIGraphicsEndImageContext();
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(shape),
CGImageGetHeight(shape),
CGImageGetBitsPerComponent(shape),
CGImageGetBitsPerPixel(shape),
CGImageGetBytesPerRow(shape),
CGImageGetDataProvider(shape), NULL, false);
return mask;
}
- (void)drawRect:(CGRect)rect {
UIFont *font = [UIFont fontWithName:#"HelveticaNeue-Bold" size:40.0f];
CGSize fontSize = [text_ sizeWithFont:font];
CGImageRef mask = [self createMaskWithSize:rect.size shape:^{
[[UIColor blackColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
[[UIColor whiteColor] setFill];
// custom shape goes here
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];
}];
CGImageRef cutoutRef = CGImageCreateWithMask([self blackSquareOfSize:rect.size].CGImage, mask);
CGImageRelease(mask);
UIImage *cutout = [UIImage imageWithCGImage:cutoutRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
CGImageRelease(cutoutRef);
CGImageRef shadedMask = [self createMaskWithSize:rect.size shape:^{
[[UIColor whiteColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 1), 1.0f, [[UIColor colorWithWhite:0.0 alpha:0.5] CGColor]);
[cutout drawAtPoint:CGPointZero];
}];
// create negative image
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
[[UIColor blackColor] setFill];
// custom shape goes here
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];
UIImage *negative = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRef innerShadowRef = CGImageCreateWithMask(negative.CGImage, shadedMask);
CGImageRelease(shadedMask);
UIImage *innerShadow = [UIImage imageWithCGImage:innerShadowRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
CGImageRelease(innerShadowRef);
// draw actual image
[[UIColor whiteColor] setFill];
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -0.5) withFont:font];
[[UIColor colorWithWhite:0.76 alpha:1.0] setFill];
[text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];
// finally apply shadow
[innerShadow drawAtPoint:CGPointZero];
}
Although Steve's answer does work, it didn't scale to my particular case and so I ended up creating an inner shadow by applying a shadow to a CGPath after clipping my context:
CGContextRef context = UIGraphicsGetCurrentContext();
// clip context so shadow only shows on the inside
CGPathRef roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:4].CGPath;
CGContextAddPath(context, roundedRect);
CGContextClip(context);
CGContextAddPath(context, roundedRect);
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 0), 3, [UIColor colorWithWhite:0 alpha:1].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:0 alpha:1].CGColor);
CGContextStrokePath(context);
Which results in:
You can change the shadow offset and blur to change the shadow style. If you need it to be darker, you can also successively add and then stroke the same CGPath, which will make the shadows pile up.
I use FXLabel for inner shadow, look here https://github.com/nicklockwood/FXLabel.
Yes, I have a blog post about it here. The short answer is to borrow the JTAInnerShadowLayer from here, subclass UILabel, and change its layerclass by overriding + (Class)layerClass so that it returns JTAInnerShadowLayer class like this:
+ (Class)layerClass
{
return [JTAInnerShadowLayer class];
}
Then in the init method for the layer set up the JTAInnerShadowLayer something like this:
[self setBackgroundColor:[UIColor clearColor]];
JTAInnerShadowLayer *innerShadow = (JTAInnerShadowLayer *)[self layer];
[innerShadow setClipForAnyAlpha:YES];
[innerShadow setOutsideShadowSize:CGSizeMake(0.0, 1.0) radius:1.0];
[innerShadow setInsideShadowSize:CGSizeMake (0.0, 4.0) radius:6.0];
/* Uncomment this to make the label also draw the text (won't work well
with black text!*/
//[innerShadow drawOriginalImage];
(or just borrow the JTAIndentLabel class).
You will have to do your own custom drawing to get an inner shadow. There's no way currently to do it with a standard UILabel.
Here's a reasonable explanation on doing inner shadow with Quartz.
Inner shadow in Core Graphics
Try PaintCode trial version! For beginners, you'll learn how to create custom drawings. It's very nice! :3 You can use it for practice or for learning, but don't use it all the time, you should also learn how to create drawings on your own.
Disclaimer: I'm not an endorser, I was just amazed by my discovery. ;)