WatchKit: How can I implement a text shadow? - swift

In my Apple Watch app, I'm trying to place text over the top of an image. I've got a group with a background image and label positioned correctly. But depending on the image, my white text can sometimes be unreadable. Dark text would have the same problem or be even worse.
Is there a way to have black text just under my white text and slightly offset?

Unfortunately, the current version of WatchKit doesn't provide a way to "stack" or layer WKInterfaceLabel controls. So, the idea of including black text under the white text using a second label won't work.
You might consider rendering your drop-shadowed text as a UIImage in your Watch extension and using the image with a WKInterfaceImage control. Something like:
- (UIImage *)imageWithText:(NSString *)text shadowBlurRadius:(CGFloat)shadowBlurRadius {
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowOffset = CGSizeZero;
shadow.shadowBlurRadius = shadowBlurRadius;
shadow.shadowColor = [UIColor blackColor];
NSDictionary *attributes = #{ NSForegroundColorAttributeName : [UIColor whiteColor],
NSFontAttributeName : [UIFont boldSystemFontOfSize:36.0f],
NSShadowAttributeName : shadow };
CGSize size = [text sizeWithAttributes:attributes];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width + (2.0f * shadowBlurRadius),
size.height + (2.0f * shadowBlurRadius)),
NO,
2.0f);
[text drawAtPoint:CGPointMake(shadowBlurRadius, shadowBlurRadius)
withAttributes:attributes];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

Related

How do I add beveled text effect in an iOS app?

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.

Drawing text in UITableViewCell

I used the following method to draw text in my custome cell, it's working fine but I just found that part of the text missing(Not showing all text in the cell):
- (void)drawContentView:(CGRect)rect {
UIColor * textColor = [UIColor blackColor];
if (self.selected || self.highlighted){
textColor = [UIColor whiteColor];
}
else
{
[[UIColor whiteColor] set];
UIRectFill(self.bounds);
}
[textColor set];
UIFont * textFont = [UIFont systemFontOfSize:16];
CGSize textSize = [text sizeWithFont:textFont constrainedToSize:rect.size];
[text drawInRect:CGRectMake(self.frame.size.width-(textSize.width+2 ) ,
(rect.size.height / 2) - (textSize.height / 2),
textSize.width, textSize.height) withFont:textFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentRight];
}
Thanks, please advice.
Try to make the space for drawing bigger than the textSize.width, textSize.heigh
UIFont * textFont = [UIFont systemFontOfSize:16];
CGSize sizeMe = CGSizeMake(300, rect.size.height*1.5);
CGSize textSize = [text sizeWithFont:textFont constrainedToSize:sizeMe];
[text drawInRect:CGRectMake(self.frame.size.width-(textSize.width+10 ) ,
(rect.size.height / 2) - (textSize.height / 2),
textSize.width, textSize.height+(textSize.height*1.5)) withFont:textFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentRight];
Perhaps you should change the line that determines text size to:
CGSize textSize = [text sizeWithFont:textFont constrainedToSize:rect.size lineBreakMode: NSLineBreakByWordWrapping];
You did not consider the lineBreakMode in sizeWithFont. You may want to use – sizeWithFont:constrainedToSize:lineBreakMode: instead for determine the actual size.
BTW, when your constraint is exactly the rect.size then the return value will never be larger than the actual rect.size. That may be what you want. Whenever I deal with strings of variable length in custom cells, I like to know whether the string fits in or not and may enlarge the individual cell (which requieres some more work of course).
If you do that then set the size.height of the constraints size to something very high. 10000.0f or so.
You are creating a custom cell .What is the need for drawing the text into it?That is the purpose of Label just add as the subview and use it.
drawInRect is a coregraphics drawing method
as per docs
Draws the string in the current graphics context using the specified
bounding rectangle and font. This method draws as much of the string
as possible using the given font and constraints. This method uses the
UILineBreakModeWordWrap line break mode and the UITextAlignmentLeft
alignment.
I dont see you get the context before drawing .Hence it doesnot have a valid context.and hence not drawing

iOS - How to achieve emboss effect for the text on UILabel?

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

Adding text over an image on Xcode

I want to make an iphone application similar to some greeting cards application, where I could write text over some pre prepared background images(cards).
How can I write this text?
How to save the background image+the text on one image file ?
Thanks.
Here is a method that burns a string into an image. You can tweak the font size and other parameters to configure it to your liking.
/* Creates an image with a home-grown graphics context, burns the supplied string into it. */
- (UIImage *)burnTextIntoImage:(NSString *)text :(UIImage *)img {
UIGraphicsBeginImageContext(img.size);
CGRect aRectangle = CGRectMake(0,0, img.size.width, img.size.height);
[img drawInRect:aRectangle];
[[UIColor redColor] set]; // set text color
NSInteger fontSize = 14;
if ( [text length] > 200 ) {
fontSize = 10;
}
UIFont *font = [UIFont boldSystemFontOfSize: fontSize]; // set text font
[ text drawInRect : aRectangle // render the text
withFont : font
lineBreakMode : UILineBreakModeTailTruncation // clip overflow from end of last line
alignment : UITextAlignmentCenter ];
UIImage *theImage=UIGraphicsGetImageFromCurrentImageContext(); // extract the image
UIGraphicsEndImageContext(); // clean up the context.
return theImage;
}
Thanks Rayfleck! It worked.
To add optional compatibility with retina displays (fixes choppy letters during '#2x' version of image scale up):
replace:
UIGraphicsBeginImageContext(img.size);
with conditional:
if (UIGraphicsBeginImageContextWithOptions != NULL)
UIGraphicsBeginImageContextWithOptions(img.size,NO,0.0);
else
UIGraphicsBeginImageContext(img.size);
Update for ios7...
/* Creates an image with a home-grown graphics context, burns the supplied string into it. */
- (UIImage *)burnTextIntoImage:(NSString *)text :(UIImage *)img {
UIGraphicsBeginImageContext(img.size);
CGRect aRectangle = CGRectMake(0,0, img.size.width, img.size.height);
[img drawInRect:aRectangle];
[[UIColor redColor] set]; // set text color
NSInteger fontSize = 14;
if ( [text length] > 200 ) {
fontSize = 10;
}
UIFont *font = [UIFont fontWithName:#"Courier" size:fontSize];
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
paragraphStyle.alignment = NSTextAlignmentRight;
NSDictionary *attributes = #{ NSFontAttributeName: font,
NSParagraphStyleAttributeName: paragraphStyle,
NSForegroundColorAttributeName: [UIColor whiteColor]};
[text drawInRect:aRectangle withAttributes:attributes];
UIImage *theImage=UIGraphicsGetImageFromCurrentImageContext(); // extract the image
UIGraphicsEndImageContext(); // clean up the context.
return theImage;
}
This approach takes in account the screen's scale and it's super intuitive
UIImage * img = ...
UIImageView * iV = [[UIImageView alloc] initWithImage:img];
UILabel * l = [[UILabel alloc] initWithFrame:iV.bounds];
l.textAlignment = ...;
l.adjustsFontSizeToFitWidth = YES;
l.textColor = ...;
l.font = ...;
l.text = ...;
[iV addSubview:l];
UIGraphicsBeginImageContextWithOptions(iV.bounds.size, NO, 0);
[iV.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage

How to get a color image in iPhone sdk

I want something as follows
UIImage *solid = [UIImage imageWithColor:[UIColor darkGrayColor]];
to create an image with respect to some color.
how to do it in iPhone sdk.
You can draw the color into a CGContext and then capture an image from it:
- (UIImage *)imageWithColor:(UIColor *)color andSize:(CGSize)size {
//Create a context of the appropriate size
UIGraphicsBeginImageContext(size);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
//Build a rect of appropriate size at origin 0,0
CGRect fillRect = CGRectMake(0,0,size.width,size.height);
//Set the fill color
CGContextSetFillColorWithColor(currentContext, color.CGColor);
//Fill the color
CGContextFillRect(currentContext, fillRect);
//Snap the picture and close the context
UIImage *retval = UIGraphicsGetImageFromCurrentImageContext(void);
UIGraphicsEndImageContext();
return retval;
}
If you're just trying to create a solid rectangle of colour why not just do something like
UIView *solid = [[UIView alloc] initWithFrame:someFrame];
solid.backgroundColor = [UIColor greyColor];
And then add the view to whatever subview you want to show the solid colour.
(That is, of course only if that's what you're trying to achieve. Maybe you aren't)