iOS 中文斜體和粗體以及NSAttributedString轉(zhuǎn)換HTML

最近做了一個需求 發(fā)布器(UITextView)支持文字的粗體斜體刪除線以及添加顏色等樣式救鲤,類似于


image.png

需求分析:

  1. 選中區(qū)域在修改UITextView的attributedString夏跷;
  2. 轉(zhuǎn)換HTML顯示在UILabel/DTCoreText中潜秋;
  3. HTML轉(zhuǎn)換attributedString顯示在UITextView中;

實現(xiàn)

  • 在加粗/刪除線/文字顏色等樣式操作的時候直接修改即可實現(xiàn)沒有任何難度驴娃,貼出一個方法:
    _focusTextView為發(fā)布器主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;//標記修改樣式結(jié)束
}

其他的不一一列舉 都是調(diào)用API的模板代碼

問題

主要問題集中在斜體
在實現(xiàn)斜體的時候發(fā)現(xiàn)了一個問題漆诽,中文要支持斜體需要字體支持斜體,可以打開蘋果所有設備(Mac/iPhone/iPad...)上的筆記(Notes.app)跺讯,輸入中文和英文然后再斜體,可以看到
在蘋果所有設備上沒有字體支持斜體

image.png

于是找到部門設計師 最簡單的方式就是添加字體然后直接設置那個字體的FamilyName就可以搞定殉农,可是設計師反饋,為了UI統(tǒng)一性要換就全部換了局荚,為了一個斜體這個要部門會議討論更換整個App的字體超凳,如果顯示的時候只為斜體適配這樣沒有斜體的用PingfangSC有斜體的用斜體字體 這樣也沒有辦法達到UI的統(tǒng)一性,最終這個方法就放棄了耀态;

于是開始谷歌轮傍,各種焦頭爛額的StackFlow和Google之后有了如下的方案:

    1. UIFont的動態(tài)字體,用字體描述類可以手動實現(xiàn)斜體功能首装,給出代碼创夜,字體傾斜角度請與設計師聯(lián)調(diào):
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];

在選中區(qū)域內(nèi)替換此Font,實現(xiàn)了斜體仙逻,可是我們的需求是要富文本轉(zhuǎn)成HTML去展示驰吓,HTML可不認這個仿射變換,HTML只認得標簽系奉;

  • 但是思路是對的在改變斜體的時候添加一個標記檬贰,接著在轉(zhuǎn)換HTMl的時候識別出標記手動添加一個<i>標簽,在UIFont中有一個標簽叫NSObliquenessAttributeName, Oblique的意思是傾斜缺亮,可以實現(xiàn)斜體的功能翁涤,具體角度請與設計師溝通,所以有了如下方法
- (void)setFocusSelectionItalic:(BOOL)isItalic {
    modifyingTextStyle = YES;
    
    NSRange selectedRange = self._focusTextView.selectedRange;
    NSMutableAttributedString *string = [self._focusTextView.attributedText mutableCopy];
    
    NSMutableDictionary<NSAttributedStringKey, id> *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;
}

代碼解釋:
獲取到選擇區(qū)域的內(nèi)容判斷是否有加粗萌踱,有加粗則實現(xiàn)加粗與否的操作葵礼,因為設置了斜體會覆蓋TextView的attributedText的值,所以加粗斜體需要都包含進去并鸵;
設置傾斜角度
然后運行鸳粉,完美實現(xiàn)


image.png

轉(zhuǎn)換HTML

NSAttributedString *convertAttributeToHTML = [attributedText copy];
        /* 用兩個數(shù)組臨時存儲標記的位置 然后再 */
        NSMutableArray *needReplaceRanges = [NSMutableArray array];
        NSMutableArray *needReplaceFonts = [NSMutableArray array];
      //遍歷找到那個標記的font
        [attributedText enumerateAttributesInRange:NSMakeRange(0, attributedText.length) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _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];
        }
    
    /* 使用系統(tǒng)的富文本轉(zhuǎn)換HTML, 請勿使用第三方的分類轉(zhuǎn)換能真!HTML語言代碼是不嚴謹?shù)?格式會影響到最后的生成*/
    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的字符串赁严,拿著字符串進行格式化和轉(zhuǎn)化
HTMl是這樣的:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<style type="text/css">
    p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px '.SF UI Text'; color: #333333}
    span.s1 {font-family: '.SFUIText-SemiboldItalic'; font-weight: bold; font-style: italic; font-size: 17.00pt}
    span.s2 {font-family: 'PingFangSC-Regular'; font-weight: normal; font-style: normal; font-size: 17.00pt; color: #fdaa25}
</style>
</head>
<body>
    <p class="p1">
        <span class="s1">斜</span>
        <span class="s2">體</span>
    </p>
</body>
</html>

這段代碼直接可以貼到WebView里直接展示扰柠。
至此,已完成了這個需求疼约。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卤档,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子程剥,更是在濱河造成了極大的恐慌劝枣,老刑警劉巖押赊,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肝集,死亡現(xiàn)場離奇詭異十性,居然都是意外死亡哲鸳,警方通過查閱死者的電腦和手機粱腻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門口锭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來类嗤,“玉大人祈餐,你說我怎么就攤上這事瀑踢“饣梗” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵橱夭,是天一觀的道長氨距。 經(jīng)常有香客問我,道長棘劣,這世上最難降的妖魔是什么俏让? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮茬暇,結(jié)果婚禮上首昔,老公的妹妹穿的比我還像新娘。我一直安慰自己而钞,他們只是感情好沙廉,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著臼节,像睡著了一般撬陵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上网缝,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天巨税,我揣著相機與錄音,去河邊找鬼粉臊。 笑死草添,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的扼仲。 我是一名探鬼主播远寸,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抄淑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了驰后?” 一聲冷哼從身側(cè)響起肆资,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灶芝,沒想到半個月后郑原,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡夜涕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年犯犁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片女器。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡酸役,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晓避,到底是詐尸還是另有隱情簇捍,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布俏拱,位于F島的核電站,受9級特大地震影響吼句,放射性物質(zhì)發(fā)生泄漏锅必。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一惕艳、第九天 我趴在偏房一處隱蔽的房頂上張望搞隐。 院中可真熱鬧,春花似錦远搪、人聲如沸劣纲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽癞季。三九已至,卻和暖如春倘潜,著一層夾襖步出監(jiān)牢的瞬間绷柒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工涮因, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留废睦,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓养泡,卻偏偏與公主長得像嗜湃,于是被迫代替她去往敵國和親奈应。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容