Dynamic Number of UILabels in custom tableviewcell - ios

I created a custom tableview cell and I want to display some strings in that cell. I fetch strings from backend thus I don't know how many labels I needed. I tried to concat strings in one label and implemented like below however I want to display strings with different attributes after char ":".
for (AttributesModel* attribute in model.attributes) {
NSString *attributeName = attribute.name;
attributeString = [[attributeString stringByAppendingString: attributeName] mutableCopy];
attributeString = [[attributeString stringByAppendingString: #" : "] mutableCopy];
for (NSDictionary *value in attribute.options) {
attributeString = [[attributeString stringByAppendingString: [value objectForKey:#"name"] ] mutableCopy];
attributeString = [[attributeString stringByAppendingString: #", "] mutableCopy];
}
attributeString = [[attributeString stringByAppendingString: #"\n"] mutableCopy];
}
I could not change attributes of strings thats located after char ":".
Are there any way to do that? Can I create dynamic number of labels in cell or only change attributes of strings that are only located after ":" ?

It sounds like you want to change the attributes such as formatting of a section of text within a UILabel. You can do this by:
Creating an attributed, mutable copy of your string (a.k.a converting an NSString into an NSMutableAttributedString).
Changing the attributes to parts of this copy.
Setting your label's attributedText property to your attributed string.
NSString *myString = #"This is my string";
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:myString];
NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy];
// The range of text to change, i.e. start from the 5th index
// (starting from 0 like arrays), and continue for 2 characters:
NSRange rangeOfSecondWord = NSMakeRange(5, 2);
// The list of attributes to apply to that range:
NSDictionary *myAttributes = #{
NSForegroundColorAttributeName : [UIColor redColor],
};
// Actually apply the attributes:
[mutableAttributedString setAttributes:myAttributes range:rangeOfSecondWord];
// Set the text of the label to the attributed string:
myLabel.attributedText = mutableAttributedString;
For a list of attributes that you can set in your dictionary, see the Character Attributes reference.
Since you're downloading your strings, you may not know the range beforehand. Since you're concatenating them, here is how you can find the range dynamically:
NSString *stringOne = #"My name is ";
NSString *stringTwo = #"John Citizen";
NSString *joinedStrings = [stringOne stringByAppendingString:stringTwo];
NSRange rangeOfStringTwo = [joinedStrings rangeOfString:stringTwo];

Related

UITextView where should I set color and font for for attributed string

When I use UITextView in storyboard, I can select text as attributed string and set its attributes like color, font and other attributes. And when I set textView.text = #"my string" in code it show up as I set.
My question is if I want to do this in code, what would be equivalent to this storyboard attributes setting? From document https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextView_Class/index.html#//apple_ref/occ/instp/UITextView/text I see some property i.e. textColor, font, but lack of some property that I can set like line-height.
This is done via the attributed strings object! You should definitely have a look at the programming guide:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/AttributedStrings/AttributedStrings.html
Upon request, a short example to show how you create an attributed string.
- (void)viewDidLoad
{
[super viewDidLoad];
//
// Example 1: Create attributed string and set attributes for ranges
//
NSString *plainString = #"Hello World";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:plainString];
// change the font of the entire string
[attributedString addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"Courier New" size:16.0]
range:NSMakeRange(0, plainString.length)];
// set the font color to blue for the word 'World'
[attributedString addAttribute:NSForegroundColorAttributeName
value:[UIColor blueColor]
range:NSMakeRange(6, 4)];
// assign to texfield 1
self.tfAttributedString1.attributedText = attributedString;
//
// Example 2: Create attributed string based on html
//
NSString *htmlString = #"<p style='font-family:Courier New'>Hello <span style='color:blue'>html</span></p>";
NSData *htmlStringData = [htmlString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *attributedStringOptions = #{
NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)
};
// assign to texfield 2
self.tfAttributedString2.attributedText = [[NSAttributedString alloc] initWithData:htmlStringData
options: attributedStringOptions
documentAttributes:nil error:nil];
}
And this is what the result will look like:

How to change case of an NSMutableAttributedString in Objective-C?

I have a NSMutableAttributedString that contain values in lower case letters. I need to convert all the lowercase letters to uppercase. We can solve this using uppercaseString for normal string as:
[string uppercaseString];
How can I change my case for NSMutableAttributedString? Thanks!
Hope the below code snippet may help you
NSMutableAttributedString * linkString = [[NSMutableAttributedString alloc]initWithString:#"Google"];
NSString * strings = [[linkString string]uppercaseString];
[linkString replaceCharactersInRange:NSMakeRange(0, [linkString length]) withString:strings];
NSLog(#"UpperCase %#",linkString);
NSMutableAttributedString class doesn't have uppercaseString method. So you can use like this..
NSString *str = #"objective-c";
str = [str uppercaseString];
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:str];
NSLog(#"Attributed String %#",attStr);
And You wanna upper latter in particular range then do something like this...
[attStr replaceCharactersInRange:NSMakeRange(0, 1) withString:#"O"];

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;

Display edited NSString in order

I have been working on this for a few days with help from this great community.
I have a NSArray that I need to edit NSStrings within. I have managed to detect a marker in the string and make it bold. However now I am trying to display the strings in the order that they are within the NSArray whilst maintaining the Bold that was added to the specific strings.
I can display the individual Bold String 'string' but I need it to be in order that it is within the array. I know of stringByAppendingString but this would put it at the end.
Any directions would be brilliant.
for (NSString *testWord in legislationArray) {
if ([testWord rangeOfString:#"BOLDME"].location != NSNotFound) {
//Remove Marker
NSString *stripped = [testWord stringByReplacingOccurrencesOfString:#"BOLDME" withString:#""];
//Get string and add bold
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:stripped];
NSRange selectedRange = [stripped rangeOfString:(stripped)];
[string beginEditing];
[string addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"Helvetica-Bold" size:18.0]
range:selectedRange];
[string endEditing];
//Where to go now with string?
}
}
cell.dynamicLabel.text = [legislationArray componentsJoinedByString:#"\n"];
EDIT
Based on the answers below I got it working however the bold method invokes this error:
componentsJoinedByString return a NSString, when you want a NSAttributedString.
Plus, you're setting your text to a receiver that awaits a NSString (cell.dynamicLabel.text), where what you want should be cell.dynamicLabel.attributedText.
Since there is no equivalent to componentsJoinedByString for a NSAttributedString return, you have to do it the oldway, with a for loop, starting with initializing a NSMutableAttributedString, and adding to it each components (that you may "transform") to it.
Here is a example and related question.
Just use additional array. Change your code to
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
for (NSString *testWord in legislationArray) {
if ([testWord rangeOfString:#"BOLDME"].location != NSNotFound) {
//Remove Marker
NSString *stripped = [testWord stringByReplacingOccurrencesOfString:#"BOLDME" withString:#""];
//Get string and add bold
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:stripped];
NSRange selectedRange = [stripped rangeOfString:(stripped)];
[string beginEditing];
[string addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"Helvetica-Bold" size:18.0]
range:selectedRange];
[string endEditing];
//Where to go now with string?
[attrString appendAttributedString:string];
}
else
{
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:testWord]];
}
// NEW LINE
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:#"\n"]];
}
cell.dynamicLabel.attributedText = attrString;
UPDATE:
Your additional issue is not a error - this is a way how XCode shows attributed strings in debug window:

Change attributes of substrings in a NSAttributedString

This question may be a duplicate of this one. But the answers don't work for me and I want to be more specific.
I have a NSString, but I need a NS(Mutable)AttributedString and some of the words in this string should be given a different color. I tried this:
NSString *text = #"This is the text and i want to replace something";
NSDictionary *attributes = # {NSForegroundColorAttributeName : [UIColor redColor]};
NSMutableAttributedString *subString = [[NSMutableAttributedString alloc] initWithString:#"AND" attributes:attributes];
NSMutableAttributedString *newText = [[NSMutableAttributedString alloc] initWithString:text];
newText = [[newText mutableString] stringByReplacingOccurrencesOfString:#"and" withString:[subString mutableString]];
The "and" should be uppercase an red.
The documentation says that mutableString keeps the attribute mappings. But with my replacing-thing, I have no more attributedString on the right side of the assignment (in the last line of my code-snippet).
How can I get what I want? ;)
#Hyperlord's answer will work, but only if there is one occurence of the word "and" in the input string. Anyway, what I would do is use NSString's stringByReplacingOccurrencesOfString: initially to change every "and" to an "AND", then use a little regex to detect matches in attributed string, and apply NSForegroundColorAttributeName at that range. Here's an example:
NSString *initial = #"This is the text and i want to replace something and stuff and stuff";
NSString *text = [initial stringByReplacingOccurrencesOfString:#"and" withString:#"AND"];
NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:text];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(AND)" options:kNilOptions error:nil];
NSRange range = NSMakeRange(0,text.length);
[regex enumerateMatchesInString:text options:kNilOptions range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange subStringRange = [result rangeAtIndex:1];
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:subStringRange];
}];
And finally, just apply the attributed string to your label.
[myLabel setAttributedText:mutableAttributedString];
I think you should create a NSMutableAttributedString using the existing NSString and then add the style attributes with the appropriate NSRange in order to colorize the parts you want to emphasize for example:
NSString *text = #"This is the text and i want to replace something";
NSMutableAttributedString *mutable = [[NSMutableAttributedString alloc] initWithString:text];
[mutable addAttribute: NSForegroundColorAttributeName value:[UIColor redColor] range:[text rangeOfString:#"and"]];
Be aware: this is just from my head and not tested at all ;-)
Please try this code in Swift 2
var someStr = "This is the text and i want to replace something"
someStr.replaceRange(someStr.rangeOfString("and")!, with: "AND")
let attributeStr = NSMutableAttributedString(string: someStr)
attributeStr.setAttributes([NSForegroundColorAttributeName: UIColor.yellowColor()], range: NSMakeRange(17, 3) )
testLbl.attributedText = attributeStr
Here's another implementation (in Swift) that's helpful if you're doing some more complex manipulations (such as adding/deleting characters) with your attributed string:
let text = "This is the text and i want to replace something"
let mutAttrStr = NSMutableAttributedString(string: text)
let pattern = "\\band\\b"
let regex = NSRegularExpression(pattern: pattern, options: .allZeros, error: nil)
while let result = regex!.firstMatchInString(mutAttrStr.string, options: .allZeros, range:NSMakeRange(0, count(mutAttrStr.string)) {
let substring = NSMutableAttributedString(attributedString: mutAttrStr.attributedSubstringFromRange(result.range))
// manipulate substring attributes here
substring.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range NSMakeRange(0, count(substring.string))
mutAttrStr.replaceCharactersInRange(result.range, withAttributedString: substring)
}
Your final attributed string should be:
let finalAttrStr = mutAttrStr.copy() as! NSAttributedString

Resources