iOS 7 Applying kerning and line spacing to text - iphone

I have the following code which works flawlessly in iOS 6, problem is it has no effect whatsoever in iOS 7.
How do I apply kerning and line spacing in iOS 7?
+ (NSAttributedString *)attributedText:(NSString *)text inFont:(UIFont *)font {
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];
NSMutableParagraphStyle *style = [NSMutableParagraphStyle new];
style.lineSpacing = 5;
CGFloat kernSize = -0.6;
[attributedString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, text.length)];
[attributedString addAttribute:NSKernAttributeName value:#(kernSize) range:NSMakeRange(0, text.length)];
return attributedString;
}

Maybe a duplicate to Why Does Kerning fail for NSAttributedString in IOS7
For some strange reason it does not work with the font Courier (in iOS7!) but with CourierNewPSMT.
Maybe try with different fonts.... here is a nice list of fonts on the iphone: http://iosfonts.com/

Related

UILabel auto size error on iOS 7

I'm making my app transition to iOS 7 and have this method (already modified for iOS 7, using boundingRectWithSize...):
+ (CGSize)messageSize:(NSString*)message {
NSDictionary *attributes = #{NSFontAttributeName : [UIFont fontWithName:#"Futura-Medium" size:13]};
CGRect frame = [message boundingRectWithSize:CGSizeMake([PTSMessagingCell maxTextWidth], CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading attributes:attributes context:nil];
return frame.size;
}
I am getting this appearance:
The message UILabel is being cut. It feels like line spacing is too big. It tried many other answers I found but none of them work.
If someone knows how to help me, I appreciate! ;)
Thanks!
Try changing NSStringDrawingUsesFontLeading as your option to NSStringDrawingUsesLineFragmentOrigin.
If you were only supporting iOS 6 and iOS 7, then I would definitely change all of your NSString's sizeWithFont:... to the NSAttributeString's boundingRectWithSize. Starting in iOS 6, the NSAttributedString's NSStringDrawing functions were introduced and they're threadsafe unlike the old NSString+UIKit methods we're used to (eg. sizeWithFont:..., etc), which were UIStringDrawing functions (and act unpredictably when you use them from a non-main thread. It'll save you a lot of headache if you happen to have a weird multi-threading corner case! Here's how I converted NSString's sizeWithFont:constrainedToSize::
What used to be:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font
constrainedToSize:(CGSize){width, CGFLOAT_MAX}];
Can be replaced with:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
NSAttributedString *attributedText =
[[NSAttributedString alloc]
initWithString:text
attributes:#
{
NSFontAttributeName: font
}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
CGSize size = rect.size;
Please note the documentation mentions:
In iOS 7 and later, this method returns fractional sizes (in the size
component of the returned CGRect); to use a returned size to size
views, you must use raise its value to the nearest higher integer
using the ceil function.
So to pull out the calculated height or width to be used for sizing views, I would use:
CGFloat height = ceilf(size.height);
CGFloat width = ceilf(size.width);
I think you are updating your label frame from either viewDidLoad or viewWillAppear, so it is not working.
if you will update frame of label from viewDidAppear method then you will get updated frame of label.
I am not sure why this is happened, I think it is iOS 7 bug.
Try this
+ (CGSize)messageSize:(NSString*)message {
CGSize nameSize = [message sizeWithFont:[UIFont fontWithName:#"Futura-Medium" size:13]
constrainedToSize:CGSizeMake(maxWidth, maxHeight) lineBreakMode:NSLineBreakByWordWrapping];
NSLog(#"width = %f, height = %f", nameSize.width, nameSize.height);
return nameSize;
}

respond to selector method for unsupported implementations

In my app I need to display under line text in a label so I used following code to display underlined text
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:normalString];
[attributeString addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:1]
range:(NSRange){0,[attributeString length]}];
wesiteAddressLabel.attributedText = attributeString;
This method and some other implementations which works fine in iOS 6.1
But when I executed in iOS 5.1 and below, app gets crashed due to the reason,
[attributeString addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:1]
range:(NSRange){0,[attributeString length]}];
not supported in previous versions
So I want to use respondsToSelector: method to check if instance responds and implement another method for unsupported selector.
How I use this method?
As from the documentation:
attributedText The styled text displayed by the label.
#property(nonatomic,copy) NSAttributedString *attributedText
Discussion This property is nil by default. Assigning a new value to
this property also replaces the value of the text property with the
same string data, albeit without any formatting information. In
addition, assigning a new a value updates the values in the font,
textColor, and other style-related properties so that they reflect the
style information starting at location 0 in the attributed string.
Availability Available in iOS 6.0 and later. Declared In UILabel.h
You should check if the a specific UIView element is able to respond to the attributedText. In this case:
[wesiteAddressLabel respondsToSelector:#selector(attributedText)];
Should be enough
For previoes versions you have to draw an UIImageView Just below the Text by getting the with and Height of text in each line.
Or you can create a category of label by using DrawRect method.
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx, 0.0f/255.0f, 0.0f/255.0f, 255.0f/255.0f, 1.0f); // Your underline color
CGContextSetLineWidth(ctx, 1.0f);
UIFont *font = [UIFont systemFontOfSize:16.0f];
CGSize constraintSize = CGSizeMake(MAXFLOAT, MAXFLOAT);
CGSize labelSize;
labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, labelSize.width + 10, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}

Modifying entire NSMutableAttributedString using addAttribute:

I am looking for a way to color the first word in a sentence a different color to that of the rest of the sentence. METHOD_001 first colors the whole string white then re-colors the first 8 characters red. METHOD_002 colors the first 8 characters red, before using the string length to calculate the remaining characters and color them white.
METHOD_001 is definitely the best, but I am curious if there is a simpler way, I was expecting to find a NSMutableAttributedString addAttribute: that did not take a range and just applied the attribute to the whole string, it seems a bit of an oversight that all modifications to a NSMutableAttributedString require you to specify a range, am I missing something?
NB: Code includes hard coded values to aid readability.
// METHOD_001
NSMutableAttributedString *attrString_001 = [[NSMutableAttributedString alloc] initWithString:#"Distance 1720 mm" attributes:#{NSForegroundColorAttributeName : [UIColor whiteColor]}];
[attrString_001 addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 8)];
[[self nameLabel] setAttributedText:attrString_001];
// METHOD_002
NSString *string = #"Distance 1720 mm";
NSUInteger stringLength = [string length];
NSMutableAttributedString *attrString_002 = [[NSMutableAttributedString alloc] initWithString:string];
[attrString_002 addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 8)];
[attrString_002 addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(9, (stringLength-9))];
[[self distanceLabel] setAttributedText:attrString_002];
Actually there is quite an easy way to do that. Even if you set an attributed text to your label, first it is stylized by the regular properties of the label, it is then your attributed string overrides corresponding ones. So if you do [distanceLabel setTextColor:[UIColor whiteColor]] beforehand (in storyboard or code) you can recolor only the needed parts by using attr. strings and achieve your desired effect.

How to set UITextfield width dynamically?

I have to make iPad application.
There are three questions textfields. I have to set width UITextField dynamically, text is coming from webservice. and I have to set UITextField width same as questions text..
when texts question size is small then UITextField size is small,
when texts question size is large then UITextField size is large,
I have following code.....
txtQuestionOne.hidden=NO;
txtQuestionTwo.hidden=NO;
txtQuestionThree.hidden=NO;
imgQueone.hidden=NO;
imgQueTwo.hidden=NO;
imgQueThree.hidden=NO;
[txtQuestionOne setPlaceholder:[[appDelegate.questions objectAtIndex:0] objectForKey:#"question"]];
appDelegate.FirstQues =[[appDelegate.questions objectAtIndex:0] objectForKey:#"question"];
NSLog(#"current q1 %#", [[appDelegate.questions objectAtIndex:0] objectForKey:#"question"]);
if ([[[appDelegate.questions objectAtIndex:0] objectForKey:#"permission"] isEqualToString:#"1"]) {
ratingButton1.hidden=NO;
ratingLabel1.hidden=NO;
}
[txtQuestionTwo setPlaceholder:[[appDelegate.questions objectAtIndex:1] objectForKey:#"question"]];
appDelegate.SecondQues =[[appDelegate.questions objectAtIndex:1] objectForKey:#"question"];
NSLog(#"current q2 %#", [[appDelegate.questions objectAtIndex:1] objectForKey:#"question"]);
if ([[[appDelegate.questions objectAtIndex:1] objectForKey:#"permission"] isEqualToString:#"1"]) {
ratingButton2.hidden=NO;
ratingLabel2.hidden=NO;
}
[txtQuestionThree setPlaceholder:[[appDelegate.questions objectAtIndex:2] objectForKey:#"question"]];
appDelegate.ThridQues =[[appDelegate.questions objectAtIndex:2] objectForKey:#"question"];
NSLog(#"current q3 %#", [[appDelegate.questions objectAtIndex:2] objectForKey:#"question"]);
if ([[[appDelegate.questions objectAtIndex:2] objectForKey:#"permission"] isEqualToString:#"1"]) {
ratingButton3.hidden=NO;
ratingLabel3.hidden=NO;
}
Please help me i dont know what can i do?
Use this to get size for a NSString of specified font style. And use the returned CGSize to set size of UI element can be label, textfield.
(comments from apple documentation)
// Single line, no wrapping. Truncation based on the UILineBreakMode.
- (CGSize)sizeWithFont:(UIFont *)font; // Uses UILineBreakModeWordWrap
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode;
// for multi lined
// Wrapping to fit horizontal and vertical size. Text will be wrapped and truncated using the UILineBreakMode. If the height is less than a line of text, it may return
// a vertical size that is bigger than the one passed in.
// If you size your text using the constrainedToSize: methods below, you should draw the text using the drawInRect: methods using the same line break mode for consistency
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size; // Uses UILineBreakModeWordWrap;
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode;
Eg:
NSString *valueString = #"This is example";
CGSize newSize = [valueString sizeWithFont: [UIFont fontWithName: #"TrebuchetMS" size: 12] ];
// assign new size
CGRect textFrame = textbox. frame;
textFrame. size = newSize;
did you try this
CGRect frame= yourTextField.frame;
frame.size.width=your_required_width;
yourTextField.frame=frame;
Have a look at this post
It shows you the use of sizeWithFont which gives you a CGSize object from the size of your text. Then edit your textfields frame with the data of this CGSize object.

NSMutableAttributedString - NSForegroundColorAttributeName

I am trying to set the color of all the ranges in my array but I am getting this error. I don't understand why. the ranges are all valid. I even tried manually inserting a range to test it. Thank You.
CGContextSetFillColorWithColor: invalid context 0x0
NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString:tv.text];
for (NSString * s in array) {
[string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSRangeFromString(s)];
}
CATextLayer *textlayer = [[CATextLayer alloc]init];
textlayer.frame = CGRectMake(0, 0, 320, 480);
[self.view.layer addSublayer:textlayer];
textlayer.string = #"aString"; //works
textlayer.string = string; //does not work
tv.text = #"";
Is the code example the exact same code as you are trying to build? Im quite sure NSForegroundColorAttributeName is only available in Mac OS X SDK and iOS 6.0 and later so the example code should not even compile.
What you want instead is probably kCTForegroundColorAttributeName and pass a CGColorRef instead of a NSColor.
[string addAttribute:(id)kCTForegroundColorAttributeName
value:(id)[UIColor redColor].CGColor
range:NSRangeFromString(s)];
But im not sure if this really is the cause of the invalid context error.
Your context is wrong, not your range. You're trying to set the color of something that doesn't have a color.