Sam Sam -4 years ago 143
iOS Question

Find attributes from attributed string that user typed

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.

Answer Source

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:@"NSFont"];
     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 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 it's 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 exists, 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 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 had 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 you custom family doesn't have a matching font, what does it returns? etc.), but that could be a nice add-on if you don't do too complicated things.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download