Setting up Text Alignment (CTTextAligment) on NSAttributedString - ipad - 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.

Related

is there any way to make TextFlied input like google wallet input in ios ? please see attached photo for your understanding what i need

I want same textfield or any control in iOS Objective-C, like this image
google wallet input entry.
Yes totally possible Use NSAttributed String here is the link NSAttributedString
Create 2 NSDictionary one for smaller fontSize and one for Bigger fontSize.
in the small font size Attributed Dictionary the size for the NSBaselineOffsetAttributeName is used to move the small font up or down change the value to see the effect.
// I have added the attribute for font color as well so it should look the same as picture above
NSDictionary *smallFontSize = #{
NSFontAttributeName : [UIFont systemFontOfSize:10],
NSBaselineOffsetAttributeName : [[NSNumber alloc] initWithInt:10],
NSForegroundColorAttributeName : [UIColor grayColor]
};
Second dictionary is very simple it only contains the font Size
NSDictionary *bigFontSize = #{
NSFontAttributeName : [UIFont systemFontOfSize:25]
};
Declare NSMutableAttributedString not AttributedString pass in the samllFontSize
// I have directly passed the $ sign but you will pass it as a property
NSMutableAttributedString *mutableAttriString = [[NSMutableAttributedString alloc] initWithString:#"$" attributes:smallFontSize];
Now create two new AttributedStrings these 2 can be attributedString because we are not manipulating them
// You will be passing the amount and decimals as properties
NSAttributedString *amount = [[NSAttributedString alloc] initWithString:#"10" attributes:bigFontSize];
NSAttributedString *decimalAmount = [[NSAttributedString alloc] initWithString:#"00" attributes:smallFontSize];
Now try to append both amount and decimalAmount to the mutableAttributedString that was the reason we made the first one mutable so we can append to it later
[mutableAttriString appendAttributedString:amount];
[mutableAttriString appendAttributedString:decimalAmount];
Each label has a property called text and another one called attributedText add it to the attributedText property
self.priceLabel.attributedText = mutableAttriString
Here is the screenshot of the label change the font and colours to your liking.

sizeWithAttributes() not giving me the right height after several break lines

Im using sizeWithAttributes() to get the size of a string with \n's in it. It works up to a number of 5 \n's and then it starts to return half a line too low on the height, so the last row gets cut in half (---).
Are there any other attributes than Font which will help me in my situation?
Code:
str = "text\ntext\ntext\ntext\ntext"
label = CATextLayer()
label. ...
let textSize = str.uppercaseString.sizeWithAttributes([NSFontAttributeName:UIFont(name: label.font as String, size: label.fontSize)!])
First, label.font might be not a string:
The font to use, currently may be either a CTFontRef, a CGFontRef, or a string naming the font
So you'd better perform some checks if you're not setting it in your code (I'll write my example in ObjC, but it should be pretty simple to translate it to Swift):
NSString* fontName;
CFTypeRef font = label.font;
if (CFGetTypeID(font) == CFStringGetTypeID())
{
fontName = (__bridge NSString *)(font);
}
else if (CFGetTypeID(font) == CGFontGetTypeID())
{
fontName = CFBridgingRelease(CGFontCopyFullName((CGFontRef)font));
}
else if (CFGetTypeID(font) == CTFontGetTypeID())
{
fontName = CFBridgingRelease(CTFontCopyFullName((CTFontRef)font));
}
Second, CATextLayer (unlike UILabel or string drawing functions) seems to use line height multiple different from 1 when drawing string content, so you need to use paragraph style to measure it correctly:
NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
style.lineHeightMultiple = 1.05;
NSDictionary* attributes = #{
NSFontAttributeName: [UIFont fontWithName:fontName size:label.fontSize],
NSParagraphStyleAttributeName: style
};
CGSize textSize = [str sizeWithAttributes:attributes];
Multiplier 1.05 was actually chosen after some experiments, and it works pretty good for different font sizes from 8 to 72.

use coretext ,when draw images and in ios6 , i get CTFrame empty values

CTFrame: visible string range = (0, 0){type = mutable-small, count = 0, values = ()}
i find this problem only happens in ios6,and only drawing images.final,i find this code maybe the problem at :
if (imagename) {
//render empty space for drawing the image in the text //1
CTRunDelegateCallbacks callbacks;
callbacks.version = kCTRunDelegateVersion1;
callbacks.getAscent = ascentCallback;
callbacks.getDescent = descentCallback;
callbacks.getWidth = widthCallback;
callbacks.dealloc = deallocCallback;
NSDictionary *imgAttr = #{#"width": [NSNumber numberWithFloat:self.customFaceSize.width],
#"height": [NSNumber numberWithFloat:self.customFaceSize.height],
#"descent" : [NSNumber numberWithFloat:0],
};
CFRetain((__bridge CFTypeRef)imgAttr);
CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (__bridge void *)(imgAttr));
NSDictionary *attrDictionaryDelegate = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)delegate,(NSString *)kCTRunDelegateAttributeName,
imagename,iCKeyFaceName,
[NSNumber numberWithLong:styleRange.location],iCkeyFaceLocation,
nil];
[*attributedString addAttributes:attrDictionaryDelegate range:styleRange];
[*attributedString addAttributes:[self attributesWithNude:nude] range:styleRange];
}else{
[*attributedString addAttributes:[self attributesWithNude:nude] range:styleRange];
}
how can i do? thanks
I fix this issue,i found the rect for path is something difference in ios6.
CGRect drawingRect = self.bounds;
CGPathAddRect(path, NULL, drawingRect);
CTFrameRef textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);
drawingRect.size.height is less than which in ios7 and upper
so.i fix it by add little pixel for drawingRect.size.height in ios6,and it work.
hope,will help you.
drawingRect.size.height += 2;
i am chinese,and i solved this problem.maybe this is not u'r problem.
but,i think i should make a record. and , my english is just so so !!!
ok,final i found what's the problem.Here is the step:
1.i found "CGPathAddRect" method, different rect draws different
2.so,i think CTRunDelegateRef is not the problem
3.then ,i think i should check the attributedString first
4. i compared attributedString between working and not working
5. i found in the attributedString that not working,the image run,font is 17, but woking is 13
6. so i think the attributed text font is the problem
7. then, i checked attributed styles ,i found the faceStyle font is larger than default.
so,change this font to the default style font .and it works !

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.

How to control the string bold with coreText???

CGFloat widthValue = -1.0;
CFNumberRef strokeWidth = CFNumberCreate(NULL,kCFNumberFloatType,&widthValue);
//Setup the attributes dictionary with font and color
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(id)font, (id)kCTFontAttributeName,
_fontColor.CGColor, kCTForegroundColorAttributeName,
strokeWidth,kCTStrokeWidthAttributeName,
nil];
I set the kCTStrokeWidthAttributeName with the above code ,but it is still bold.
I don't want the string to be bold.
How i can control it ????
Checkout kCTFontWeightTrait and kCTFontBoldTrait.
Try changing your widthValue to:
CGFloat widthValue = 0.f;
to remove the bold text.
From Apple's docs:
kCTStrokeWidthAttributeName
The stroke width. Value must be a CFNumberRef object. Default value is 0.0, or no stroke. This attribute, interpreted as a
percentage of font point size, controls the text drawing mode:
positive values effect drawing with stroke only; negative values are
for stroke and fill. A typical value for outlined text is 3.0.

Resources