iOS 中文斜体和粗体以及NSAttributedString转换HTML

最近做了一个需求 发布器(UITextView)支持文字的粗体斜体删除线以及添加颜色等样式,类似于


image.png

需求分析:

  1. 选中区域在修改UITextView的attributedString;
  2. 转换HTML显示在UILabel/DTCoreText中;
  3. HTML转换attributedString显示在UITextView中;

实现

  • 在加粗/删除线/文字颜色等样式操作的时候直接修改即可实现没有任何难度,贴出一个方法:
    _focusTextView为发布器主UITextView
- (void)setFocusSelectionBold:(BOOL)isBold
{
    modifyingTextStyle = YES;//标记正在修改样式
    
    NSRange selectedRange = self._focusTextView.selectedRange;
    NSMutableAttributedString *string = [self._focusTextView.attributedText mutableCopy];
    if (isBold){
        [string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"PingFangSC-Semibold" size:17] range:selectedRange];
    } else {
        [string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"PingFangSC-Regular" size:17] range:selectedRange];
    }
    self._focusTextView.attributedText = string;
    self._focusTextView.selectedRange = selectedRange; // 重置选中
    [self._focusTextView.delegate textViewDidChange:self._focusTextView];//更新代理
    modifyingTextStyle = NO;//标记修改样式结束
}

其他的不一一列举 都是调用API的模板代码

问题

主要问题集中在斜体
在实现斜体的时候发现了一个问题,中文要支持斜体需要字体支持斜体,可以打开苹果所有设备(Mac/iPhone/iPad...)上的笔记(Notes.app),输入中文和英文然后再斜体,可以看到
在苹果所有设备上没有字体支持斜体

image.png

于是找到部门设计师 最简单的方式就是添加字体然后直接设置那个字体的FamilyName就可以搞定,可是设计师反馈,为了UI统一性要换就全部换了,为了一个斜体这个要部门会议讨论更换整个App的字体,如果显示的时候只为斜体适配这样没有斜体的用PingfangSC有斜体的用斜体字体 这样也没有办法达到UI的统一性,最终这个方法就放弃了;

于是开始谷歌,各种焦头烂额的StackFlow和Google之后有了如下的方案:

    1. UIFont的动态字体,用字体描述类可以手动实现斜体功能,给出代码,字体倾斜角度请与设计师联调:
CGAffineTransform matrix = CGAffineTransformMake(1, 0, tanf(15 * (CGFloat)M_PI / 180), 1, 0, 0);
UIFontDescriptor *desc = [UIFontDescriptor fontDescriptorWithName:[UIFont systemFontOfSize:kFontSizeText].fontName matrix:matrix];
return [UIFont fontWithDescriptor:desc size:kFontSizeText];

在选中区域内替换此Font,实现了斜体,可是我们的需求是要富文本转成HTML去展示,HTML可不认这个仿射变换,HTML只认得标签;

  • 但是思路是对的在改变斜体的时候添加一个标记,接着在转换HTMl的时候识别出标记手动添加一个标签,在UIFont中有一个标签叫NSObliquenessAttributeName, Oblique的意思是倾斜,可以实现斜体的功能,具体角度请与设计师沟通,所以有了如下方法
- (void)setFocusSelectionItalic:(BOOL)isItalic {
    modifyingTextStyle = YES;
    
    NSRange selectedRange = self._focusTextView.selectedRange;
    NSMutableAttributedString *string = [self._focusTextView.attributedText mutableCopy];
    
    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:2];
    if ([self focusSelectionContainsBold]) {
        attributes[NSFontAttributeName] = [self _currentFocusTextParagraph].boldTextFont;
    } else {
        attributes[NSFontAttributeName] = [self _currentFocusTextParagraph].textFont;
    }
    
    if (isItalic) {
        attributes[NSObliquenessAttributeName] = @0.5;
    } else {
        attributes[NSObliquenessAttributeName] = @0;
    }
    [string addAttributes:attributes range:selectedRange];
    
    self._focusTextView.attributedText = string;
    self._focusTextView.selectedRange = selectedRange; // reset select range after set attributedText
    
    [self._focusTextView.delegate textViewDidChange:self._focusTextView];
    
    modifyingTextStyle = NO;
}

代码解释:
获取到选择区域的内容判断是否有加粗,有加粗则实现加粗与否的操作,因为设置了斜体会覆盖TextView的attributedText的值,所以加粗斜体需要都包含进去;
设置倾斜角度
然后运行,完美实现


image.png

转换HTML

NSAttributedString *convertAttributeToHTML = [attributedText copy];
        /* 用两个数组临时存储标记的位置 然后再 */
        NSMutableArray *needReplaceRanges = [NSMutableArray array];
        NSMutableArray *needReplaceFonts = [NSMutableArray array];
      //遍历找到那个标记的font
        [attributedText enumerateAttributesInRange:NSMakeRange(0, attributedText.length) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
            CGFloat oblique = [attrs[NSObliquenessAttributeName] floatValue];
            if (oblique) {
                UIFont *oldFont = attrs[NSFontAttributeName];
                //找到了之后替换字体
                UIFontDescriptor *fontDescriptor = oldFont.fontDescriptor;
                UIFontDescriptorSymbolicTraits fontDescriptorSymbolicTraits = fontDescriptor.symbolicTraits;
                BOOL isBold = (fontDescriptorSymbolicTraits & UIFontDescriptorTraitBold) != 0;
                UIFont *newFont = [self.class generateSpecialFontWithBold:isBold withItatic:YES fontSize:oldFont.pointSize];
                [needReplaceRanges addObject:[NSValue valueWithRange:range]];
                [needReplaceFonts addObject:newFont];
            }
        }];
        NSMutableAttributedString *newAttributedString;
        if (needReplaceRanges.count) {
            newAttributedString = [attributedText mutableCopy];
            for (NSUInteger i = 0; i < needReplaceRanges.count; i++) {
                NSRange range;[((NSValue *)needReplaceRanges[i]) getValue:&range];
                UIFont *font = needReplaceFonts[i];
                [newAttributedString addAttribute:NSFontAttributeName value:font range:range];
            }
        }
        if (newAttributedString.length) {
            convertAttributeToHTML = [newAttributedString copy];
        }
    
    /* 使用系统的富文本转换HTML, 请勿使用第三方的分类转换!HTML语言代码是不严谨的 格式会影响到最后的生成*/
    NSDictionary *documentAttributes = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
    NSData *htmlData = [convertAttributeToHTML dataFromRange:NSMakeRange(0, convertAttributeToHTML.length) documentAttributes:documentAttributes error:NULL];
    NSString *htmlString = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
//加粗和斜体的字体
+ (UIFont *)generateSpecialFontWithBold:(BOOL)bold withItatic:(BOOL)italic fontSize:(CGFloat)fontSize {
    UIFont *font = [self regularFontWithSize:fontSize];
    UIFontDescriptorSymbolicTraits symbolicTraits = 0;
    if (italic) {
        symbolicTraits |= UIFontDescriptorTraitItalic;
    }
    if (bold) {
        symbolicTraits |= UIFontDescriptorTraitBold;
    }
    UIFont *specialFont = [UIFont fontWithDescriptor:[[font fontDescriptor] fontDescriptorWithSymbolicTraits:symbolicTraits] size:font.pointSize];
    return specialFont;
}

这样会生成一个HTML的字符串,拿着字符串进行格式化和转化
HTMl是这样的:











    

这段代码直接可以贴到WebView里直接展示。
至此,已完成了这个需求。

你可能感兴趣的:(iOS 中文斜体和粗体以及NSAttributedString转换HTML)