最近做了一個需求 發(fā)布器(UITextView)支持文字的粗體斜體刪除線以及添加顏色等樣式救鲤,類似于
需求分析:
- 選中區(qū)域在修改UITextView的attributedString夏跷;
- 轉(zhuǎn)換HTML顯示在UILabel/DTCoreText中潜秋;
- 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)跺讯,輸入中文和英文然后再斜體,可以看到
在蘋果所有設備上沒有字體支持斜體
于是找到部門設計師 最簡單的方式就是添加字體然后直接設置那個字體的FamilyName就可以搞定殉农,可是設計師反饋,為了UI統(tǒng)一性要換就全部換了局荚,為了一個斜體這個要部門會議討論更換整個App的字體超凳,如果顯示的時候只為斜體適配這樣沒有斜體的用PingfangSC有斜體的用斜體字體 這樣也沒有辦法達到UI的統(tǒng)一性,最終這個方法就放棄了耀态;
于是開始谷歌轮傍,各種焦頭爛額的StackFlow和Google之后有了如下的方案:
- 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)
轉(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里直接展示扰柠。
至此,已完成了這個需求疼约。