将嵌套的粗体斜体 HTML 标签转换为 NSAttributedString
Convert nested bold italic HTML tags to NSAttributedString
我正在使用此代码使用 RegEx 将 HTML 字符串转换为 NSAttributedString。我遇到的唯一问题是嵌套的粗体和斜体标签。 RegEx 是正确的方法吗?
我还想避免使用 HTML 解析器,因为我需要的只是粗体、斜体和下划线属性以及删除线(如果可能的话)。
有什么建议吗?
- (NSMutableAttributedString *)applyStylesToString:(NSString *)string searchRange:(NSRange)searchRange {
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
[attributedString addAttribute:NSFontAttributeName value:[StylesConfig regularLargeFont] range:searchRange];
NSDictionary *boldAttributes = @{ NSFontAttributeName : [StylesConfig regularBoldLargeFont] };
NSDictionary *italicAttributes = @{ NSFontAttributeName : [StylesConfig regularItalicLargeFont] };
NSDictionary *underlineAttributes = @{ NSUnderlineStyleAttributeName : @1};
NSDictionary *strikeThroughAttributes = @{ NSStrikethroughStyleAttributeName : @1};
NSDictionary *replacements = @{
@"<b>(.*?)</b>" : boldAttributes,
@"<i>(.*?)</i>" : italicAttributes,
@"<u>(.*?)</u>" : underlineAttributes,
@"<s>(.*?)</s>" : strikeThroughAttributes
};
for (NSString *key in replacements) {
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:key options:0 error:nil];
[regex enumerateMatchesInString:string options:0 range:searchRange usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
NSRange matchRange = [match rangeAtIndex:1];
[attributedString addAttributes:replacements[key] range:matchRange];
if ([key isEqualToString:@"<b>(.*?)</b>"]) {
[self makeBoldItalic:attributedString matchRange:matchRange font:@"SourceSansPro-It"];
} else if ([key isEqualToString:@"<i>(.*?)</i>"]) {
[self makeBoldItalic:attributedString matchRange:matchRange font:@"SourceSansPro-Semibold"];
}
}];
}
[[attributedString mutableString] replaceOccurrencesOfString:@"<b>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"</b>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"<i>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"</i>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"<u>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"</u>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"<s>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"</s>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
return attributedString;
}
- (void)makeBoldItalic:(NSMutableAttributedString *)attributedString matchRange:(NSRange)matchRange font:(NSString *)font {
[attributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
UIFont *oldFont = (UIFont *)value;
if ([oldFont.fontName isEqualToString:font]) {
[attributedString removeAttribute:NSFontAttributeName range:range];
NSDictionary *boldItalicAttributes = @{ NSFontAttributeName : [StylesConfig regularBoldItalicLargeFont] };
[attributedString addAttributes:boldItalicAttributes range:matchRange];
}
}];
}
问题在于,与下划线不同,粗体和斜体(以及其他属性)是字体的一部分。 iOS 不会像 Photoshop 那样提供 "fake bold" 或 "fake italic"。
所以,根据您的 StylesConfig
class 实用程序,我猜您拥有:
boldFont
、italicFont
和 boldItalicFont
。
boldFont = [StylesConfig regularBoldLargeFont]; //SourceSansPro-Semibold
italicFont = [StylesConfig regularItalicLargeFont];//SourceSansPro-It
boldItalicFont = [StylesConfig regularBoldItalicLargeFont];//SourceSansPro-SemiboldIt (or something like this)
所以要更改字体,希望其 symbolicTraits
fontDescriptor
中的字体包含正确的字体,您可以这样做:
-(void)boldText:(NSMutableAttributedText *)attributeString forRange:(NSRange)range
{
[attributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
UIFont *currentFont = (UIFont *)value;
if ([[currentFont fontDescriptor] symbolicTraits] & UIFontDescriptorTraitBold)
{
NSLog(@"Font is already bold);
}
else
{
UIFont *newFont = [self boldFontFromFont:currentFont];
[attributedString addAttribute:newFont range:range];
}
}];
}
-(void)italicText:(NSMutableAttributedText *)attributeString forRange:(NSRange)range
{
[attributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
UIFont *currentFont = (UIFont *)value;
if ([[currentFont fontDescriptor] symbolicTraits] & UIFontDescriptorTraitItalic)
{
NSLog(@"Font is already italic);
}
else
{
UIFont *newFont = [self italicFontFromFont:currentFont];
[attributedString addAttribute:newFont range:range];
}
}];
}
-(UIFont *)boldFontFromFont:(UIFont *)font
{
if ([[currentFont fontDescriptor] symbolicTraits] & UIFontDescriptorTraitItalic)
return boldItalicFont;
else
return boldFont;
}
-(UIFont *)italicFontFromFont:(UIFont *)font
{
if ([[currentFont fontDescriptor] symbolicTraits] & UIFontDescriptorTraitBold)
return boldItalicFont;
else
return italicFont;
}
在您当前的代码中:
if ([key isEqualToString:@"<b>(.*?)</b>"])
{
[self boldText: attributedString forRange:matchRange];
}
else if ([key isEqualToString:@"<i>(.*?)</i>"])
{
[self italicText: attributedString forRange:matchRange];
}
如果您的字体没有通过 symbolicTraits
测试,您可能需要查看 UIFont
fontName
并检查它是粗体、斜体还是粗斜体。
注:
我没有测试代码,我只写在这里,我什至不知道它是否编译,但这应该给你整个想法。
我正在使用此代码使用 RegEx 将 HTML 字符串转换为 NSAttributedString。我遇到的唯一问题是嵌套的粗体和斜体标签。 RegEx 是正确的方法吗?
我还想避免使用 HTML 解析器,因为我需要的只是粗体、斜体和下划线属性以及删除线(如果可能的话)。
有什么建议吗?
- (NSMutableAttributedString *)applyStylesToString:(NSString *)string searchRange:(NSRange)searchRange {
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
[attributedString addAttribute:NSFontAttributeName value:[StylesConfig regularLargeFont] range:searchRange];
NSDictionary *boldAttributes = @{ NSFontAttributeName : [StylesConfig regularBoldLargeFont] };
NSDictionary *italicAttributes = @{ NSFontAttributeName : [StylesConfig regularItalicLargeFont] };
NSDictionary *underlineAttributes = @{ NSUnderlineStyleAttributeName : @1};
NSDictionary *strikeThroughAttributes = @{ NSStrikethroughStyleAttributeName : @1};
NSDictionary *replacements = @{
@"<b>(.*?)</b>" : boldAttributes,
@"<i>(.*?)</i>" : italicAttributes,
@"<u>(.*?)</u>" : underlineAttributes,
@"<s>(.*?)</s>" : strikeThroughAttributes
};
for (NSString *key in replacements) {
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:key options:0 error:nil];
[regex enumerateMatchesInString:string options:0 range:searchRange usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
NSRange matchRange = [match rangeAtIndex:1];
[attributedString addAttributes:replacements[key] range:matchRange];
if ([key isEqualToString:@"<b>(.*?)</b>"]) {
[self makeBoldItalic:attributedString matchRange:matchRange font:@"SourceSansPro-It"];
} else if ([key isEqualToString:@"<i>(.*?)</i>"]) {
[self makeBoldItalic:attributedString matchRange:matchRange font:@"SourceSansPro-Semibold"];
}
}];
}
[[attributedString mutableString] replaceOccurrencesOfString:@"<b>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"</b>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"<i>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"</i>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"<u>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"</u>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"<s>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
[[attributedString mutableString] replaceOccurrencesOfString:@"</s>" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, attributedString.length)];
return attributedString;
}
- (void)makeBoldItalic:(NSMutableAttributedString *)attributedString matchRange:(NSRange)matchRange font:(NSString *)font {
[attributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
UIFont *oldFont = (UIFont *)value;
if ([oldFont.fontName isEqualToString:font]) {
[attributedString removeAttribute:NSFontAttributeName range:range];
NSDictionary *boldItalicAttributes = @{ NSFontAttributeName : [StylesConfig regularBoldItalicLargeFont] };
[attributedString addAttributes:boldItalicAttributes range:matchRange];
}
}];
}
问题在于,与下划线不同,粗体和斜体(以及其他属性)是字体的一部分。 iOS 不会像 Photoshop 那样提供 "fake bold" 或 "fake italic"。
所以,根据您的 StylesConfig
class 实用程序,我猜您拥有:
boldFont
、italicFont
和 boldItalicFont
。
boldFont = [StylesConfig regularBoldLargeFont]; //SourceSansPro-Semibold
italicFont = [StylesConfig regularItalicLargeFont];//SourceSansPro-It
boldItalicFont = [StylesConfig regularBoldItalicLargeFont];//SourceSansPro-SemiboldIt (or something like this)
所以要更改字体,希望其 symbolicTraits
fontDescriptor
中的字体包含正确的字体,您可以这样做:
-(void)boldText:(NSMutableAttributedText *)attributeString forRange:(NSRange)range
{
[attributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
UIFont *currentFont = (UIFont *)value;
if ([[currentFont fontDescriptor] symbolicTraits] & UIFontDescriptorTraitBold)
{
NSLog(@"Font is already bold);
}
else
{
UIFont *newFont = [self boldFontFromFont:currentFont];
[attributedString addAttribute:newFont range:range];
}
}];
}
-(void)italicText:(NSMutableAttributedText *)attributeString forRange:(NSRange)range
{
[attributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
UIFont *currentFont = (UIFont *)value;
if ([[currentFont fontDescriptor] symbolicTraits] & UIFontDescriptorTraitItalic)
{
NSLog(@"Font is already italic);
}
else
{
UIFont *newFont = [self italicFontFromFont:currentFont];
[attributedString addAttribute:newFont range:range];
}
}];
}
-(UIFont *)boldFontFromFont:(UIFont *)font
{
if ([[currentFont fontDescriptor] symbolicTraits] & UIFontDescriptorTraitItalic)
return boldItalicFont;
else
return boldFont;
}
-(UIFont *)italicFontFromFont:(UIFont *)font
{
if ([[currentFont fontDescriptor] symbolicTraits] & UIFontDescriptorTraitBold)
return boldItalicFont;
else
return italicFont;
}
在您当前的代码中:
if ([key isEqualToString:@"<b>(.*?)</b>"])
{
[self boldText: attributedString forRange:matchRange];
}
else if ([key isEqualToString:@"<i>(.*?)</i>"])
{
[self italicText: attributedString forRange:matchRange];
}
如果您的字体没有通过 symbolicTraits
测试,您可能需要查看 UIFont
fontName
并检查它是粗体、斜体还是粗斜体。
注:
我没有测试代码,我只写在这里,我什至不知道它是否编译,但这应该给你整个想法。