Font smoothing/anti-aliasing when using Quartz2d on iOS? - ios

I have a custom UIView that generates some kind of diagram that needs a height notated with a number.
All drawing goes ok, it antialiases correctly when creating circles etc. with quartz2d.
However, when I want to draw a NSString to the context, the font-smoothing/anti-aliasing is messed up. Disabling anti-aliasing does result in a thinner text, so the switch does work, however, the anti-aliased text is awfull. It doesn't look as crisp at all as the normal text rendered in iOS.
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetShouldAntialias(context, YES);
CGContextSetShouldSmoothFonts(context, YES);
NSString *stringValue = [NSString stringWithFormat:#"%d", input.height];
UIFont *font = [UIFont systemFontOfSize:textsize];
[stringValue drawAtPoint:CGPointMake(x, y) withFont:font];
I am out of clues on how to solve this font rendering issue. Any ideas?

If you are willing to forego the anti-aliasing on the text you could save the context with anti-aliasing turned on.
GCContextSaveGState(context);
Turn anti-aliasing off and render the text:
GCContextSetShouldAntialias(context, NO);
NSString *stringValue = [NSString stringWithFormat:#"%d", input.height];
UIFont *font = [UIFont systemFontOfSize:textSize];
[stringValue drawAtPoint:CGPointMake(x, y) withFont:font];
Then restore the graphics context back to it's previous space. (with anti-aliasing turned on) to render the remaining parts of the drawing.
CGContextRestoreGState(context);

My solution to this problem was to just add several UILabel programmatically with the right coordinates. These UILabel instances rendered the text perfectly, so this solved my problem.

Related

CoreText Attributed String Height Calculation Inaccurate

CoreText isn't giving the correct height of the attributed string (its short by a line or more). I have seen a lot of posts on SO about this but unable to understand or find a solution. Can somebody explain how Core Text height calculation works? Here's an example code I wrote showing inaccurate height calculation.
Context
I have a collection view where the cell's height is determined by the content inside it.
I am displaying paragraphs of text in the cells. I would like to save some performance by doing the height calculation using core text. I have seen that with core text's height calculation I could save ~300ms.
Code
// Height Calculation
+ (CGFloat)getHeight
{
NSString *text = #"The Apple HIG recommends to use a common color for links and buttons and we did just that. By using the same color throughout the app we trained the user to always associate blue to a link.The Apple HIG recommends to use a common color for links and buttons and we did just that.By using the same color throughout the app we trained the user to always associate blue to a link.";
NSAttributedString *attrStr = [self attributedString:text withLinespacing:3 withLineBreakMode:NSLineBreakByWordWrapping];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrStr));
CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter,
CFRangeMake(0, attrStr.length),
NULL,
CGSizeMake(320, 9999),
NULL);
return suggestedSize.height;
}
// Load the same text when Cell is about to display
- (void)loadData
{
NSString *text = #"The Apple HIG recommends to use a common color for links and buttons and we did just that.By using the same color throughout the app we trained the user to always associate blue to a link.The Apple HIG recommends to use a common color for links and buttons and we did just that.By using the same color throughout the app we trained the user to always associate blue to a link.";
NSAttributedString *attrStr = [[self class] attributedString:text withLinespacing:3 withLineBreakMode:NSLineBreakByWordWrapping];
// UILabel element
self.textLabel.attributedText = attrStr;
self.layer.borderColor = [UIColor blueColor].CGColor;
self.layer.borderWidth = 1.0f;
}
// Generate attributed string with leading, font and linebreak
+ (NSAttributedString *)attributedString:(NSString *)string
withLinespacing:(CGFloat)linespacing
withLineBreakMode:(NSLineBreakMode)lineBreakMode
{
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:string];
NSInteger strLength = [string length];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineSpacing = linespacing;
style.lineBreakMode = lineBreakMode;
[attrStr addAttributes:#{NSParagraphStyleAttributeName: style,
NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue" size:15]} range:NSMakeRange(0, strLength)];
return attrStr;
}
The above code uses core text to calculate the height and UILabel to display the text. The UILabel has 3 constraints to the cell {Top:17, Leading:13px, Trailing:13px}
CTFramesetterSuggestFrameSizeWithConstraints is known to be buggy, returning incorrect height values. The missing line bug you experience is very common, and there are no good solutions that I know of, only ugly workarounds which never give 100% accurate results.
For iOS7 and above, I recommend moving to TextKit. Somehow the calculations performed there internally do work correctly, while being based on Core Text also. Using NSLayoutManager's usedRectForTextContainer: returns a correct result.
You can see a more complete answer here. While not exactly 100% on topic, there is some discussion about the bugginess of Core Text calculations.

Put text in UIView programmatically

Within my app, I use drawRect to draw some text within a UIImage. It writes multiple things in multiple places.. Later, I try to erase some of the text by using
CGContextSetBlendMode(context, kCGBlendModeClear);
CGPoint daPoint = CGPointMake(second.x + 20, second.y + 20);
NSDictionary *textAttributes = #{ NSFontAttributeName: [UIFont boldSystemFontOfSize:25.0],
NSForegroundColorAttributeName: [UIColor clearColor] };
[textString drawAtPoint:daPoint withAttributes:textAttributes];
This works almost perfectly, except there is a small thin stroke left over from the text. I use the same code to draw the text, as I do to erase, except when i'm drawing I use kCGBlendModeNormal. How would I get rid of it completely? Can I draw a box and fill it using kCGBlendModeClear? This is what it looks like currently before erasing:
After erasing:
I would get the bounding rect of the text and then call CGContextClearRect and then fill with the background color if you think that "erasing" the text is really necessary. If you simply "redraw" your rect, that might be another way to solve this problem.
Here's how to get that bounding box for clearing:
CGSize textSize = [textString sizeWithAttributes:textAttributes];
CGRect textFrame = CGRectMake(daPoint.x, daPoint.y, textSize.width, textSize.height);
Hope this helps!

landscape rotation text in iOS

The project I'm working on in Xcode is done in landscape.
I may be doing something wrong, but I've had to rotate every image 270 degrees before adding them to my file.
NSString *strFromInt = [NSString stringWithFormat:#"%d",score];
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(gc, 1, 1, 1, 1);
NSString *str = (#"Score ");
UIFont *uif = [UIFont systemFontOfSize:40];
[strFromInt drawAtPoint:CGPointMake(150, 10) withAttributes:#{NSFontAttributeName:uif}];
[str drawAtPoint:CGPointMake(10, 10) withAttributes:#{NSFontAttributeName:uif}];
When I try including a score (in the above code), the text runs along the wall vertically.
Any way to fix this (preferably in a method that doesn't make me have to rotate all 47 of my images again)?
I suspect the issue is that you are using CoreGrphics to place everything on the screen. Using UILabel's etc. would do this for you as these are self contained views with logic behind them as apposed to to instruction the OS to draw the letters for you.
Is there a reason you can't use UIlabel ?
You can use UILable with clearColor. This will resolve your problem.

Setting up Text Alignment (CTTextAligment) on NSAttributedString - ipad

I'm trying to draw a custom string with color, font, size and alignment.
I got everything working with an NSMutableAttributedString before, but it looks like Text Aligment only works with Paragraph alignement which only works with non mutable version of NSString.
So, I had to change my previous code to this :
//Note : _name variables are provided by my GUI for text, size and font name.
//Create the String ColorRef
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
const CGFloat myColor[] = {_color.r/255.0, _color.g/255.0, _color.b/255.0, 1.0f};
CGColorRef colorRef = CGColorCreate(rgb, myColor);
//Setup paragraph Alignment Ref
CTTextAlignment theAlignment = kCTCenterTextAlignment;
CFIndex theNumberOfSettings = 1;
CTParagraphStyleSetting theSettings[1] = {{ kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &theAlignment }};
CTParagraphStyleRef theParagraphRef = CTParagraphStyleCreate(theSettings, theNumberOfSettings);
//Prep Font
NSDictionary *fontAttributes = [NSDictionary dictionaryWithObjectsAndKeys: _fontName, (NSString *)kCTFontFamilyNameAttribute,
[NSNumber numberWithFloat:_fontSize], (NSString *)kCTFontSizeAttribute,
nil];
CTFontRef font = [self newFontWithAttributes:fontAttributes];
//Prepare String Attributes
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: (id)font, (NSString *)kCTFontAttributeName,
[NSNumber numberWithFloat:_fontSize], (NSString *)kCTFontSizeAttribute,
(id)theParagraphRef, (NSString*)kCTParagraphStyleAttributeName,
colorRef, (NSString *)kCTForegroundColorAttributeName, nil];
//Create the Attributed String
NSAttributedString *myString = [[NSAttributedString alloc] initWithString:_textString
attributes: attributes];
But the Text Aligment still doesn't work. Everything else is fine but text remains aligned on the Left.
Why ?
EDIT :
Each of my strings are created inside a class that is a subclass of CATextLayer. Each of those TextLayers are then added to a CALayer as sublayers. On updates I apply trasformation matrix on the sublayers and use setNeedsDisplay. This is how I display the text on screen. Maybe There's a reason here why the CTParagraphStyleRef set is not working ?
I have no clues why the ParagraphStyle that I've set is not working, But I've found a solution that's working for me, so I'm posting it in case someone encounter similar problems :
My class is subclassing CATextLayer, which I think would've been important to mention in my question (my bad, I'll edit it).
Inside my CATextLayer class, I create the string using the code
shown in my question.
Then I use the self.alignmentMode = kCAAlignmentCenter; to align
the text the way I want.
Each string is then added to a CALAyer for display
I've also found this very good guide on AttributedStrings that helped me improving my code and finding this solution.

iOS light linen background?

I really want to know if there's a way to get iOS's light linen background. Here's what it looks like:
Is there a way to access this by merely using the built-in SDK? Or do I have to find an image like this somewhere?
EDIT: I didn't want to use private APIs, so here's what I did. I grabbed the image this way:
CGRect rect = CGRectMake(0.0f, 0.0f, 640.0f, 960.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [[UIColor underPageBackgroundColor] CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *imagepath = [documentsDirectory stringByAppendingPathComponent:#"underPageBackground.png"];
[UIImagePNGRepresentation(image) writeToFile:imagepath atomically:YES];
And here is the resulting png in retina resolution: (click on thumbnail to open)
Hope this is useful to someone. :)
Use [UIColor underPageBackgroundColor]. And here is a link with useful information and samples.
I'm not sure it is exactly the one (seems darker to me) but, you can choose the "Scroll View Textured Background Color" in Interface Builder. To do so, when selection a color choose the dropbox to the right instead of the color box on the left.
I believe you should be able to find this background in the SDK(it should be the default one for this kind of "flick page up" function) or on google (try looking for UIStockImageUnderPageBackground.png).
Otherwise - it looks like a pattern. What you could do is to import the attached screenshot, then cut a bit without the shadows and fill a blank canvas with it, so that the sides match forming the original pattern.
This isn't really a solution to get the actual image but you can easily just make the texture. I just pulled up a tutorial on how to make it in photoshop. iOS Linen tute here
This way you can make it whatever color you want!

Resources