NSKernAttributeName of a UITextView always 0 - ios

I'm setting an NSKernAttributeName to varying float values on the attributedText property of a UITextView on iOS 6, and within specific ranges. Every time the value is retrieved using the enumerateAttribute options block method of the attributedText, the Kerning is set to 0. Code is below.
Retrieval
NSAttributedString *currentText = _textView.attributedText;
[currentText enumerateAttribute:NSKernAttributeName inRange:NSMakeRange(0, currentText.length) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop){
float value = [(NSNumber *)value floatValue];
//Do something with value, but logs show value is always 0
}];
Storage
NSMutableAttributedString *updatedText = self.textView.attributedText.mutableCopy;
_kernValue += 0.1f;
NSDictionary *attributes = #{
NSForegroundColorAttributeName: UIColor.linkColor,
NSFontAttributeName: [UIFont systemFontOfSize:14.0],
NSKernAttributeName: [NSNumber numberWithFloat:_kernValue]
};
NSMutableAttributedString *replacementString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#"%# ", myString] attributes:attributes];
[updatedText replaceCharactersInRange:myRange withAttributedString:replacementString];
self.textView.attributedText = updatedText;

From the NSAttributedString header:
UIKIT_EXTERN NSString *const NSKernAttributeName NS_AVAILABLE_IOS(6_0); //
NSNumber containing floating point value, in points; amount to modify default kerning. 0 means kerning is disabled. (note: values other than nil and 0 are unsupported on iOS)

Related

Get string of first line in a UILabel

I have some text in a UILabel. I want to get the text of the first label in a string variable.
For example:
label.text = #"You make an object by creating an instance of a particular class. You do this by allocating the object and initializing it with acceptable default values. When you allocate an object, you set aside enough memory for the object and set all instance variables to zero. Initialization sets an object’s initial state—that is, its instance variables and properties—to reasonable values and then returns the object. The purpose of initialization is to return a usable object. You need to both allocate and initialize an object to be able to use it.";
Now I want to get the text of the first line in the UILabel.
1.Add CoreText.framework. 2. Import #import CoreText/CoreText.h>.
Then use below method -
-(NSArray *)getLinesArrayOfStringInLabel:(UILabel *)label
{
NSString *text = [label text];
UIFont *font = [label font];
CGRect rect = [label frame];
CTFontRef myFont = CTFontCreateWithName(( CFStringRef)([font fontName]), [font pointSize], NULL);
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:(NSString *)kCTFontAttributeName value:( id)myFont range:NSMakeRange(0, attStr.length)];
CFRelease(myFont);
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(( CFAttributedStringRef)attStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,rect.size.width,100000));
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
NSArray *lines = ( NSArray *)CTFrameGetLines(frame);
NSMutableArray *linesArray = [[NSMutableArray alloc]init];
for (id line in lines)
{
CTLineRef lineRef = ( CTLineRef )line;
CFRange lineRange = CTLineGetStringRange(lineRef);
NSRange range = NSMakeRange(lineRange.location, lineRange.length);
NSString *lineString = [text substringWithRange:range];
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attStr, lineRange, kCTKernAttributeName, (CFTypeRef)([NSNumber numberWithFloat:0.0]));
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attStr, lineRange, kCTKernAttributeName, (CFTypeRef)([NSNumber numberWithInt:0.0]));
//NSLog(#"''''''''''''''''''%#",lineString);
[linesArray addObject:lineString];
}
[attStr release];
CGPathRelease(path);
CFRelease( frame );
CFRelease(frameSetter);
return (NSArray *)linesArray;
}
I found this answer from below url - How to get text from nth line of UILabel?
NSString *firstLineString = [[self getLinesArrayOfStringInLabel:yourLabel] objectAtIndex:0];
NSString *text = label.text;
//remove any leading or trailing whitespace or line breaks
text = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//find the the range of the first occuring line break, if any.
NSRange range = [text rangeOfString:#"\n"];
//if there is a line break, get a substring up to that line break
if(range.location != NSNotFound)
text = [text substringToIndex:range.location];
This may help you
var array = string.componentsSeparatedByString("\r")
This will separate the string with new line, and you can get the line wise text in array.

Is is possible to format a specific part of an string within objective-c?

Is it possible to format a section of text in a string within the actual code of xcode to be displayed in a single label? For instance make a specific word bold while leaving the rest normal.
Or do I have to use separate strings with different labels with the specific formatting already determined?
Use attributed text. Example below bolds the word posted By but not the name
NSString *format = NSLocalizedStringFromTable(#"Posted By: %#", #"Details Screen", #"posted by %#");
NSString *text = [NSString stringWithFormat:format, name];
const CGFloat fontSize = 14.0;
UIFont *boldFont = [UIFont fontWithName:#"HelveticaNeue-Bold" size:fontSize];
UIFont *regularFont = [UIFont fontWithName:#"HelveticaNeue" size:fontSize];
UIColor *foregroundColor = [UIColor colorWithWhite:(51.0/255.0) alpha:1.0];
// Create the attributes
NSDictionary *attrs = #{NSFontAttributeName: boldFont,
NSForegroundColorAttributeName: foregroundColor};
NSDictionary *subAttrs = #{NSFontAttributeName: regularFont};
const NSRange range = NSMakeRange(text.length - name.length,name.length); // range of " Posted by: "
// Create the attributed string (text + attributes)
NSMutableAttributedString *attributedText =
[[NSMutableAttributedString alloc] initWithString:text
attributes:attrs];
[attributedText setAttributes:subAttrs range:range];
// Set it in our UILabel and we are done!
[_textLabel setAttributedText:attributedText];

How to find and apply two fonts in a label in IOS

I know to apply two fonts in a label for single word with one font and rest with another font using below code..
int lengthofname#"Anand";
UIFont *boldFont = [UIFont fontWithName:#"BodoniStd" size:15];
UIFont *regularFont = [UIFont fontWithName:#"BodoniStd-BoldItalic" size:15];
//UIColor *foregroundColor = [UIColor clearColor];
// Create the attributes
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
boldFont, NSFontAttributeName,nil];
//NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
// boldFont, NSFontAttributeName,
// foregroundColor, NSForegroundColorAttributeName, nil];
NSDictionary *subAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
regularFont, NSFontAttributeName, nil];
const NSRange range = NSMakeRange(0,lengthofname);
// range of " 2012/10/14 ". Ideally this should not be hardcoded const
// Create the attributed string (text + attributes)
NSMutableAttributedString *attributedText =
[[NSMutableAttributedString alloc] initWithString:#"Anand is good programmer"
attributes:attrs];
[attributedText setAttributes:subAttrs range:range];
// Set it in our UILabel and we are done!
[self.descLabel setAttributedText:attributedText];
My requirement is to find some x words in a label and then apply Bold font and rest of text with Regular font..
Please suggest any ideas. Thanks in Advance..!
In this example there highlighted words are not funded with regular expression but I believe there is some list (NSArray: STANDARD, EXPRESS, NEXT DAY, etc) of keywords. And what you have to do is enumerate that array to find the rang in text and if founded apply the different font style, something like that:
for (NSString * keyword in listOfKeywordArray) {
NSRange range = [longTextString rangeOfString:#"keyword"];
if (range.location != NSNotFound) {
//Keyword found, apply different font
// This of course needs to be changed to apply font you want
[attrString addAttribute:NSFontAttributeName value:fontName range:range];
}
}
Greg's answer is pretty close. If you have an array of keywords, you can use for.. in to loop through the array of keywords. Then you'd need to use an inner loop with rangeOfString:options:range to find all occurrences of a given keyword.
That method returns an NSRange. You could use setAttributes:range: on the range to set the text attributes of each occurrence of each keyword to use the font and style you want. (Greg's code using addAttribute only lets you set a single attribute on the text. setAttributes:range: and addAttributes:range: both let you specify a whole dictionary of attributes, like in the code you posted. Your code might look like this:
//Whatever font attributes you want to use
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
boldFont, NSFontAttributeName,nil];
NSArray *keywords = [#"STANDARD", #"EXPRESS", #"NEXT DAY"];
//Loop through each keyword in the array of keywords.
for (NSString aKeyword in keywords)
{
//Set the range to the whole string to start with.
NSRange range = NSMakeRange(0, longTextString.length];
//While there are still more occurrences of this keyword
//Not that the code both assigns the result to range, and evaluates the location.
//This is a bit of C language sleight-of-hand. It works, but is odd-looking code.
while ((range = [longTextString
rangeOfString: aKeyword
options: NSLiteralSearch
range: range]).location != NSNotFound)
{
//Set the attributes on this occurrence of this keyword.
[longTextString setAttributes: attrs range: range];
range.location = range.location+range.length;
range.length = longTextString - range.location;
}
Disclaimer: I typed out the code above in an editor window. It may not compile, much less run. It's intended for illustration purposes only. You will need to clean it up and adapt it to your needs.
Also note that rangeOfString:options:range is not a great choice for this problem, since it will detect word fragments in the middle of longer words. For example, if one of your keywords is "The" then it would detect "The" in the first 3 characters of "Them" and "These". It should really be rewritten to use regular expression string matching that requires a string to be a whole word.
Edit #2. I decided to actually code this up. The rangeOfString:options:range: approach is unsatisfactory because, as mentioned above, it detects word fragments inside larger words. Regular Expressions are a much better solution. Here is code that marks all occurrences of an array of words as bold:
NSMutableAttributedString *viewStyledText = [theTextView.attributedText mutableCopy];
NSString *viewText = viewStyledText.string;
if (viewText.length == 0)
return;
BOOL changed = NO;
NSArray *wordsToBold = #[#"The", #"for", #"to", #"league"];
UIFont *boldFont = [UIFont boldSystemFontOfSize: 15];
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
boldFont, NSFontAttributeName,nil];
//Loop through each keyword in the array of keywords.
for (NSString *wordToBold in wordsToBold)
{
//Set the range to the whole string to start with.
NSRange range = NSMakeRange(0, viewText.length);
/*
Create a regular expression string for the current word.
The "(?i) prefix tells the regular expression to be case-insenstive. Remove it if
you want your search to be case-sensitive.
The "\b" bits (with double backslashes so the output contains a backslash) cause
The regular expression to only match whole words.
*/
NSString *wordRegExString = [NSString stringWithFormat: #"(?i)\\b%#\\b", wordToBold];
//Now create a regular expression object using the current regular expression string.
NSRegularExpression *wordRegEx = [NSRegularExpression
regularExpressionWithPattern: wordRegExString
options: 0
error: nil];
//While there are still more occurrences of this keyword...
//Not that the code both assigns the result to range, and evaluates the location.
//This is a bit of C language sleight-of-hand. It works, but is odd-looking code.
while ((range = [wordRegEx rangeOfFirstMatchInString: viewText
options: 0
range: range]).location != NSNotFound)
{
//Set the attributes on this occurrence of this keyword.
changed = YES;
[viewStyledText setAttributes: attrs range: range];
range.location = range.location+range.length;
range.length = viewStyledText.length - range.location;
}
}
if (changed)
theTextView.attributedText = viewStyledText;

Replace the truncation ellipsis of UILabel in iOS 7

How can I replace the truncation ellipsis ("…") of a UILabel in iOS 7 with another attributed character? For example, with a colored ">".
I was hoping Text Kit's NSLayoutManager would make this possible, but it appears UILabel doesn't make it public if it uses it.
Also, can I safely assume that an ellipsis is used as the truncation character in every localisation? Maybe different languages have different truncation characters.
I recommend you use TTTAttributedLabel, just set property "attributedTruncationToken" to your custom string.
I don't think it gives you access to this. I think you would have do handle it manually. For example, use TextKit to determine the size of your string, if it doesn't fit in the available area, truncate it yourself and append a ">" and then put your new string in the label.
NSAttributedString has methods for getting the size of the string.
Let me know if you need any more detail on this..?
I think you can do some customization in -replaceElipsesForLabel method provided by Fonix to get your desired result.
I have written a method to do it, and works in iOS7
-(void)setCustomEllipsis:(NSString*)customEllipsis inLabel:(UILabel*)label with:(NSString*)string{
//Replace the ellipsis
NSMutableString* result = [[NSMutableString alloc] initWithString:#""];
NSArray* strings = [string componentsSeparatedByString:#" "];
for (NSString* s in strings) {
CGRect newSize = [[NSString stringWithFormat:#"%#%#%#",result,s,customEllipsis] boundingRectWithSize:CGSizeMake(label.frame.size.width,0) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:label.font} context:nil];
if (newSize.size.height < label.frame.size.height) {
[result appendString:s];
[result appendString:#" "];
}else{
[result appendString:customEllipsis];
break;
}
}
[label setText:result];
//Set different font to the ellipsis
const CGFloat fontSize = 13;
UIFont *boldFont = [UIFont boldSystemFontOfSize:fontSize];
UIFont *regularFont = [UIFont systemFontOfSize:fontSize];
UIColor *foregroundColor = [UIColor lightGrayColor];
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:regularFont, NSFontAttributeName,foregroundColor, NSForegroundColorAttributeName, nil];
NSDictionary *subAttrs = [NSDictionary dictionaryWithObjectsAndKeys:boldFont, NSFontAttributeName, nil];
const NSRange range = [label.text rangeOfString:customEllipsis];
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:result
attributes:attrs];
[attributedText setAttributes:subAttrs range:range];
[label setAttributedText:attributedText];
}

UITextView customization

I want to format the test in my textview, some texts in bold some thing in italic like that. Is it possible for uitextview?
Right now I'm using webview with HTML strings.
eg:
<html><head><style type=\"text/css\">h3 {color:white;} p {color:pink;} p {text-align: center} p {font-family:helvetica;font-size:20px;}</style></head><body>\
<h3></h3>\
<p><b>some text </b></p>\
<p>Short some text</p>\
<p>Child Infusion 7.5 to 15 mg/kg/hr<br>ie 7.5 to 15 times weight per hour</p>\
<p>Adult Infusion 3 to 12 mg/kg/hr<br>ie 3 to 12 mg times weight per hour</p>\
</body></html>
You can use NSAttributedString, Set Text Font, Foreground And Background Colors, StrikeThrough And Shadow etc..
Attributed strings make an association between characters and their attributes. Like NSString objects, there are two variations, NSAttributedString and NSMutableAttributedString.
Although previous versions of iOS supported attributed strings, it wasn’t until iOS 6 that controls such as buttons, labels, textfields and textviews defined a property to manage attributes.
Attributes are applied to a range of characters, so you can for example, set a strikethrough attribute for just a portion of a string. It’s also important to note that the default font for attributed string objects is Helvetica 12-point. Keep this in mind if you set the font attribute for a range other than the complete string.
The following attributes can be set with attributed strings:
NSString *const NSFontAttributeName;
NSString *const NSParagraphStyleAttributeName;
NSString *const NSForegroundColorAttributeName;
NSString *const NSBackgroundColorAttributeName;
NSString *const NSLigatureAttributeName;
NSString *const NSKernAttributeName;
NSString *const NSStrikethroughStyleAttributeName;
NSString *const NSUnderlineStyleAttributeName;
NSString *const NSStrokeColorAttributeName;
NSString *const NSStrokeWidthAttributeName;
NSString *const NSShadowAttributeName;
NSString *const NSVerticalGlyphFormAttributeName;
here are some examples
//-----------------------------
// Create attributed string
//-----------------------------
NSString *str = #"example for underline \nexample for font \nexample for bold \nexample for italics";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:str];
// Add attribute NSUnderlineStyleAttributeName
//[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSUnderlineStyleSingle] range:NSMakeRange(12, 9)];
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSUnderlineStyleSingle] range:NSMakeRange(12, 9)];
// Set background color for entire range
[attributedString addAttribute:NSBackgroundColorAttributeName
value:[UIColor yellowColor]
range:NSMakeRange(0, [attributedString length])];
// Create NSMutableParagraphStyle object
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.alignment = NSTextAlignmentCenter;
// Add attribute NSParagraphStyleAttributeName
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraph range:NSMakeRange(0, [attributedString length])];
// Set font, notice the range is for the whole string
UIFont *font = [UIFont fontWithName:#"Helvetica" size:18];
[attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(35, 4)];
// Set font, notice the range is for the whole string
UIFont *fontBold = [UIFont fontWithName:#"Helvetica-Bold" size:18];
[attributedString addAttribute:NSFontAttributeName value:fontBold range:NSMakeRange(53, 4)];
// Set font, notice the range is for the whole string
UIFont *fontItalics = [UIFont fontWithName:#"Helvetica-Oblique" size:18];
[attributedString addAttribute:NSFontAttributeName value:fontItalics range:NSMakeRange(71, 7)];
// Set label text to attributed string
[self.mytextView setAttributedText:attributedString];
`

Resources