Vidhyanand
Vidhyanand

Reputation: 5369

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..

Bold to some words and Regular to rest of the texts in a label

Please suggest any ideas. Thanks in Advance..!

Upvotes: 1

Views: 371

Answers (2)

Duncan C
Duncan C

Reputation: 131408

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;

Upvotes: 0

Greg
Greg

Reputation: 25459

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];
    }
}

Upvotes: 2

Related Questions