NSString drawinrect not fills whole rect - iphone

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.

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.

UIEdgeInsets ignored on CGContextDrawImage within a UIGraphicsBeginImageContextWithOptions

I am struggling getting Image Insets to work when drawing to an off screen buffer.
Using the resizableImageWithCapInsets: on an UIImage directly setImage: into a button works fine for me:
UIImage * base = [UIImage imageNamed:#"button.png"];
UIImage * img = [base resizableImageWithCapInsets:UIEdgeInsetsMake(20,20,20,20)];
[self setImage:img forState:UIControlStateNormal];
As shown below (left is raw scaling, right is scaled with insets):
So the right one is fine - the lines top/botton/left/right are equally spaced. So far so good.
Now if I try the very same thing with an image that is drawn and then captured to an off screen buffer with:
UIImage * base = [UIImage imageNamed:#"button.png"];
base = [base resizableImageWithCapInsets:UIEdgeInsetsMake(20,20,20,20)];
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
ctx = UIGraphicsGetCurrentContext();
CGContextDrawImage(ctx, CGRectMake(0,0,self.bounds.size.width,
self.bounds.size.height), [base CGImage]);
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setImage:img forState:UIControlStateNormal];
I get below (again left is raw scaling, right is with insets):
.
So here it seems that the insets are ignored.
Anyone any suggestions as to what is going wrong here ? Fully functional example at http://people.apache.org/~dirkx/insetSample.zip and just the key code at http://pastebin.com/rm8h6YFV.
Any and all suggestions appreciated.
Dw
I would guess that stretchable UIImages are handled at a higher level than Quartz2D and so using CGContextDrawImage is not going to work.
You could try using -[UIImage drawInRect] instead. This will draw to the current UIGraphics context, which is your bitmap context that you create in your code snippet.
The relevant line is:
[base drawInRect:CGRectMake(0,0,self.bounds.size.width,
self.bounds.size.height)];

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

CGContextStrokeRect is drawing only a side of rect

I need to draw a rect filled with color and its border...
the rect is filled with color properly but the outside border is partially drawn, just the right side of the rect is drawn!
The generated UIImage is going to be used in a UITableViewCell's imageView.
- (UIImage *)legendItemWithColor:(UIColor *)color
{
UIGraphicsBeginImageContext(self.view.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect outside = CGRectMake(128, 128, 128, 128);
CGRect legend = CGRectInset(outside, 1, 1);
NSLog(#"Outside: %#", NSStringFromCGRect(outside));
NSLog(#"Legend: %#", NSStringFromCGRect(legend));
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, legend);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextStrokeRect(context, outside);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsPopContext();
return img;
}
The problem is when you pass self.view.frame.size to UIGraphicsBeginImageContext() and then use drawn rectangle in array, it is downscaled and the border is obfuscated. Try to pass only the size you need so, i.e. CGSizeMake(2*128+128+2,2*128+128+2). Then it displays ok

How to make an tiled background like on the web?

On the iPhone, how could I achieve the same tiled background effect?
For example, I have an pattern image which I want to repeat only horizontally. Would I have to go the route in -drawRect: by myself and loop over to draw the pattern, or is there a convenient method to do that?
You can set the backgroundColor of the UIView with a pattern using the following snippet:
UIImage *image = [UIImage imageNamed:#"pattern.png"];
myView.backgroundColor = [UIColor colorWithPatternImage:image];
The image will automatically be tiled for you to cover the area.
Claus
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
UIImage* img = [UIImage imageNamed:#"transparency.png"];
CGContextDrawTiledImage(currentContext, CGRectMake(0, 0, 12, 12), [img CGImage]);
}
In this example "transparency.png" is a 12x12 image.