Find attributes from attributed string that user typed

ios, ios7, nsattributedstring, objective-c

Solution

You can enumerate the Attributes with this code (whole range).

[attrStr enumerateAttributesInRange:NSMakeRange(0, [attrStr length]) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:
         ^(NSDictionary *attributes, NSRange range, BOOL *stop) {
//Do something here
             }
         }];

For specific range, your have to change the code `NSMakeRange(0, [attrStr lenght])` by the range you need.

After your edit, your specific need, this code should do the work:

[attrStr enumerateAttributesInRange:NSMakeRange(0, [attrStr length])
                            options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
                         usingBlock:
 ^(NSDictionary *attributes, NSRange range, BOOL *stop)
{
     NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
     UIFont *currentFont = [mutableAttributes objectForKey: NSFontAttributeName];
     UIFont *newFont;
     if ([[currentFont fontName] isEqualToString:@"Calibri-BoldItalic"])
         newFont = [UIFont fontWithName:@"HelveticaNeue-BoldItalic" size:currentFont.pointSize];
     else if ([[currentFont fontName] isEqualToString:@"Calibri-Bold"])
         newFont = [UIFont fontWithName:@"HelveticaNeue-Bold" size:currentFont.pointSize];
     else if ([[currentFont fontName] isEqualToString:@"Calibri-Italic"])
         newFont = [UIFont fontWithName:@"HelveticaNeue-Italic" size:currentFont.pointSize];
    [mutableAttributes setObject:newFont forKey:@"NSFont"];
    [attrStr setAttributes:mutableAttributes range:range];
 }];

From your comments, you may see more complicated things, so I will give you some ideas/tips to do/check. You'll just have to complete my previous code to manage all cases. • Case: There is no font. You just have to check if `currentFont` is nil. If yes, you set what you want. Since there is no attribute for italic/bold (since they are "included" in the font), I suggest you set for `newFont`: HelveticaNeue. • Case: It's another font than Calibri. There it's quite more complicated. I've worked with that, and here what I got: Font name (which is the `postscript name`) is made like this for HelveticaNeue (as given example):

HelveticaNeue HelveticaNeue-Light HelveticaNeue-UltraLight HelveticaNeue-Medium HelveticaNeue-LightItalic HelveticaNeue-Italic HelveticaNeue-UltraLightItalic HelveticaNeue-Bold HelveticaNeue-BoldItalic HelveticaNeue-CondensedBold HelveticaNeue-CondensedBlack

So the first thing you could think of: I just have to check the suffix of the name (with `hasSuffix:`). But that may go wrong. Indeed, some fonts have a foundry suffix (the ones I found are LT, MT, and EF). Well, for example: Times New Roman "real name" is TimesNewRomanPSMT. And its italic version is TimesNewRomanPS-ItalicMT That's why we have to check if it has a foundry suffix because TimesNewRomanPSMT-Italic won't work (since it doesn't exist, if you try to set a font with this name, it will be nil). So, as far as I know, and as far I may have some experience and the case I've seen, what you could do: Check if the `[currentFont name]` contains either: `BoldItalic`, `Italic` or `Bold`, using a `NSRegularExpression` or a `rangeOfString:`. Note that I'd suggest to check before for `BoldItalic` since an `Italic` may "hides" a `Bold` just before it, and `Bold` may "hides" an `Italic` just after it. In order to do so, I'd suggest that you create a method like this (or of this style):

-(UIFont)getCorrespondingHelveticaNeueFontMatchingCharacteriticsOf:(UIFont *)font
{
    UIFont *helveticaNueue;
    if (!font) //Checking if there is a font)
    {
        helveticaNueue = [UIFont fontWithName@"HelveticaNueue" size:defaultSizeYouSet];
    }
    else
    {
        //Check if it's BoldItalic/Italic/Bold
    }
    return helveticaNueue;
}

checking all the case I mentioned before. So, from the previous code, you'll just have to replace the `if` tests that were working specificly for Calibri with:

UIFont *newFont = [self getCorrespondingHelveticaNeueFontMatchingCharacteriticsOf:currentFont];

Now a third part you may want to do (and add them to your test): Is the `Black` case of a font want to be managed as `Bold`? Well, `Helvetica Neue` has this "property", but you may want to use directly the `black` property for `Helvetica Neue`, and may want to check if `currentFont` has it (and again, another case).

EDIT: You can use since iOS7 we can use `UIFontDescriptor`. Here is what you could do:

-(UIFont *)customFontMatchingFont:(UIFont *)otherFont
{
    NSString *customFontName = @"Helvetica"; //The custom font
    UIFont *tempFont = [UIFont fontWithName:customFontName size:[otherFont pointSize]]; 
    return [UIFont fontWithDescriptor:[[tempFont fontDescriptor] fontDescriptorWithSymbolicTraits:[[otherFont fontDescriptor] symbolicTraits]] size:[otherFont pointSize]];
}

Note: I didn't check all the cases (what if your custom family doesn't have a matching font, what does it return? etc.), but that could be a nice add-on if you don't do too complicated things.

Problem

In my app the User will type anything that he wants and he can make the text Bold, Italic and Underline. I am able to do the Text - Bold, Italic and Underline. But I need to find which Text (or Character) is Bold, Italic and Underline from the attribute string that user typed. Any types of link, tutorial, code will be great help. EDIT : I need the result like this... if character 6 is Italic then print Italic. if character 14 is Bold then print Bold. I need to convert all the text from Calibri font family to HelveticaNeue font family without changing it's attribute.

Original source