你能看过来,就说明这个问题很好玩!IT开发是一个兴趣,更是一个挑战!兴趣使你工作有热情。挑战使让你工作充满刺激拉满的状态!我们日复一日年复一年的去撸代码,那些普普通通的功能代码,已经厌倦了!要的就是这种充满挑战最终攻克而带来快感!这种快感比那种快感让人感觉更得劲。。错了,可能是我老了吧~
因为业务开发需求,同样的功能三端(WEB、安卓 、iOS),而web端将数据封装成H5对应的标签方式,需要APP端来解析,将其文本提取出来倒没有什么技术难度,但核心问题是要取出相应的样式表渲染成正确的UI,这才是最困难的!
如<b><span style="color:orange,font-size:15px;">重要告警</span></b>
要把它显示成如下的原生页面,怎么办呢?来分析一下这些标签的作用:
<b></b>意思是将里面的文本加粗显示 <span>标签普通的文本显示,但是文本的更多属性是在span的内联样式表中存放,我们要取出来颜色,字体大小来渲染为iOS的UILabel,通过setColor,setTextFont等API设置其显示样式!
我得到的原始数据为:
<b><a style="color:orange">【重要告警】</a></b> <b>事件名称:</b>CCE-CPU监控; <b>发生时间:</b>2023-11-08 09:48:00 GMT+08:00; <b>事件源:</b>AOM; <b>资源类型:</b>Application; <b>指标名称:</b>aom_container_cpu_usage; <b>最新指标值: </b>45.95425; <b>命名空间:</b>third-party; <b>容器名称:</b>z-canal-test; <b>告警信息:</b><br />阈值规则 CCE-CPU监控 状态从 “超限阈值” 变为 “正常” 。状态变化详细信息:指标名称为“aom_container_cpu_usage”,最新指标数据取值“45.954”,不满足阈值条件“>90”。; <b>修复建议:</b>建议进入容器 z-canal-test查看资源使用情况;
最终实现的显示效果为:
下面业结合开源的三方库来实现此功能的显示,第三方库:TFHpple
#if __has_include("TFHpple.h")
#import "TFHpple.h"
#endif
@interface CSSStyleMap : NSObject
+ (NSDictionary *)cssColorMap;
#if __has_include("TFHpple.h")
+ (UIColor *)colorFromCssText:(NSString *)ctext;
+ (NSMutableArray <NSMutableDictionary *>*)stylesFromElement:(TFHppleElement *)e;
#endif
@end
@implementation CSSStyleMap
//将CSS定义的颜色转为十六进制颜色
+ (NSDictionary *)cssColorMap
{
NSString *k = @"{\
\"CLEAR\": \"#00000000\",\
\"TRANSPARENT\": \"#00000000\",\
\"\": \"#00000000\",\
\"ALICEBLUE\": \"#F0F8FF\",\
\"ANTIQUEWHITE\": \"#FAEBD7\",\
\"AQUA\": \"#00FFFF\",\
\"AQUAMARINE\": \"#7FFFD4\",\
\"AZURE\": \"#F0FFFF\",\
\"BEIGE\": \"#F5F5DC\",\
\"BISQUE\": \"#FFE4C4\",\
\"BLACK\": \"#000000\",\
\"BLANCHEDALMOND\": \"#FFEBCD\",\
\"BLUE\": \"#0000FF\",\
\"BLUEVIOLET\": \"#8A2BE2\",\
\"BROWN\": \"#A52A2A\",\
\"BURLYWOOD\": \"#DEB887\",\
\"CADETBLUE\": \"#5F9EA0\",\
\"CHARTREUSE\": \"#7FFF00\",\
\"CHOCOLATE\": \"#D2691E\",\
\"CORAL\": \"#FF7F50\",\
\"CORNFLOWERBLUE\": \"#6495ED\",\
\"CORNSILK\": \"#FFF8DC\",\
\"CRIMSON\": \"#DC143C\",\
\"CYAN\": \"#00FFFF\",\
\"DARKBLUE\": \"#00008B\",\
\"DARKCYAN\": \"#008B8B\",\
\"DARKGOLDENROD\": \"#B8860B\",\
\"DARKGRAY\": \"#A9A9A9\",\
\"DARKGREY\": \"#A9A9A9\",\
\"DARKGREEN\": \"#006400\",\
\"DARKKHAKI\": \"#BDB76B\",\
\"DARKMAGENTA\": \"#8B008B\",\
\"DARKOLIVEGREEN\": \"#556B2F\",\
\"DARKORANGE\": \"#FF8C00\",\
\"DARKORCHID\": \"#9932CC\",\
\"DARKRED\": \"#8B0000\",\
\"DARKSALMON\": \"#E9967A\",\
\"DARKSEAGREEN\": \"#8FBC8F\",\
\"DARKSLATEBLUE\": \"#483D8B\",\
\"DARKSLATEGRAY\": \"#2F4F4F\",\
\"DARKSLATEGREY\": \"#2F4F4F\",\
\"DARKTURQUOISE\": \"#00CED1\",\
\"DARKVIOLET\": \"#9400D3\",\
\"DEEPPINK\": \"#FF1493\",\
\"DEEPSKYBLUE\": \"#00BFFF\",\
\"DIMGRAY\": \"#696969\",\
\"DIMGREY\": \"#696969\",\
\"DODGERBLUE\": \"#1E90FF\",\
\"FIREBRICK\": \"#B22222\",\
\"FLORALWHITE\": \"#FFFAF0\",\
\"FORESTGREEN\": \"#228B22\",\
\"FUCHSIA\": \"#FF00FF\",\
\"GAINSBORO\": \"#DCDCDC\",\
\"GHOSTWHITE\": \"#F8F8FF\",\
\"GOLD\": \"#FFD700\",\
\"GOLDENROD\": \"#DAA520\",\
\"GRAY\": \"#808080\",\
\"GREY\": \"#808080\",\
\"GREEN\": \"#008000\",\
\"GREENYELLOW\": \"#ADFF2F\",\
\"HONEYDEW\": \"#F0FFF0\",\
\"HOTPINK\": \"#FF69B4\",\
\"INDIANRED\": \"#CD5C5C\",\
\"INDIGO\": \"#4B0082\",\
\"IVORY\": \"#FFFFF0\",\
\"KHAKI\": \"#F0E68C\",\
\"LAVENDER\": \"#E6E6FA\",\
\"LAVENDERBLUSH\": \"#FFF0F5\",\
\"LAWNGREEN\": \"#7CFC00\",\
\"LEMONCHIFFON\": \"#FFFACD\",\
\"LIGHTBLUE\": \"#ADD8E6\",\
\"LIGHTCORAL\": \"#F08080\",\
\"LIGHTCYAN\": \"#E0FFFF\",\
\"LIGHTGOLDENRODYELLOW\": \"#FAFAD2\",\
\"LIGHTGRAY\": \"#D3D3D3\",\
\"LIGHTGREY\": \"#D3D3D3\",\
\"LIGHTGREEN\": \"#90EE90\",\
\"LIGHTPINK\": \"#FFB6C1\",\
\"LIGHTSALMON\": \"#FFA07A\",\
\"LIGHTSEAGREEN\": \"#20B2AA\",\
\"LIGHTSKYBLUE\": \"#87CEFA\",\
\"LIGHTSLATEGRAY\": \"#778899\",\
\"LIGHTSLATEGREY\": \"#778899\",\
\"LIGHTSTEELBLUE\": \"#B0C4DE\",\
\"LIGHTYELLOW\": \"#FFFFE0\",\
\"LIME\": \"#00FF00\",\
\"LIMEGREEN\": \"#32CD32\",\
\"LINEN\": \"#FAF0E6\",\
\"MAGENTA\": \"#FF00FF\",\
\"MAROON\": \"#800000\",\
\"MEDIUMAQUAMARINE\": \"#66CDAA\",\
\"MEDIUMBLUE\": \"#0000CD\",\
\"MEDIUMORCHID\": \"#BA55D3\",\
\"MEDIUMPURPLE\": \"#9370DB\",\
\"MEDIUMSEAGREEN\": \"#3CB371\",\
\"MEDIUMSLATEBLUE\": \"#7B68EE\",\
\"MEDIUMSPRINGGREEN\": \"#00FA9A\",\
\"MEDIUMTURQUOISE\": \"#48D1CC\",\
\"MEDIUMVIOLETRED\": \"#C71585\",\
\"MIDNIGHTBLUE\": \"#191970\",\
\"MINTCREAM\": \"#F5FFFA\",\
\"MISTYROSE\": \"#FFE4E1\",\
\"MOCCASIN\": \"#FFE4B5\",\
\"NAVAJOWHITE\": \"#FFDEAD\",\
\"NAVY\": \"#000080\",\
\"OLDLACE\": \"#FDF5E6\",\
\"OLIVE\": \"#808000\",\
\"OLIVEDRAB\": \"#6B8E23\",\
\"ORANGE\": \"#FFA500\",\
\"ORANGERED\": \"#FF4500\",\
\"ORCHID\": \"#DA70D6\",\
\"PALEGOLDENROD\": \"#EEE8AA\",\
\"PALEGREEN\": \"#98FB98\",\
\"PALETURQUOISE\": \"#AFEEEE\",\
\"PALEVIOLETRED\": \"#DB7093\",\
\"PAPAYAWHIP\": \"#FFEFD5\",\
\"PEACHPUFF\": \"#FFDAB9\",\
\"PERU\": \"#CD853F\",\
\"PINK\": \"#FFC0CB\",\
\"PLUM\": \"#DDA0DD\",\
\"POWDERBLUE\": \"#B0E0E6\",\
\"PURPLE\": \"#800080\",\
\"RED\": \"#FF0000\",\
\"ROSYBROWN\": \"#BC8F8F\",\
\"ROYALBLUE\": \"#4169E1\",\
\"SADDLEBROWN\": \"#8B4513\",\
\"SALMON\": \"#FA8072\",\
\"SANDYBROWN\": \"#F4A460\",\
\"SEAGREEN\": \"#2E8B57\",\
\"SEASHELL\": \"#FFF5EE\",\
\"SIENNA\": \"#A0522D\",\
\"SILVER\": \"#C0C0C0\",\
\"SKYBLUE\": \"#87CEEB\",\
\"SLATEBLUE\": \"#6A5ACD\",\
\"SLATEGRAY\": \"#708090\",\
\"SLATEGREY\": \"#708090\",\
\"SNOW\": \"#FFFAFA\",\
\"SPRINGGREEN\": \"#00FF7F\",\
\"STEELBLUE\": \"#4682B4\",\
\"TAN\": \"#D2B48C\",\
\"TEAL\": \"#008080\",\
\"THISTLE\": \"#D8BFD8\",\
\"TOMATO\": \"#FF6347\",\
\"TURQUOISE\": \"#40E0D0\",\
\"VIOLET\": \"#EE82EE\",\
\"WHEAT\": \"#F5DEB3\",\
\"WHITE\": \"#FFFFFF\",\
\"WHITESMOKE\": \"#F5F5F5\",\
\"YELLOW\": \"#FFFF00\",\
\"YELLOWGREEN\": \"#9ACD32\"\
}";
NSDictionary *map = [JsonKit jsonObject:k];
return map;
}
#if __has_include("TFHpple.h")
+ (UIColor *)colorFromCssText:(NSString *)val
{
NSString *cl = [[CSSStyleMap cssColorMap] stringForKey:val];
UIColor *color = nil;
if (cl.length > 0)
{
color = [UIColor colorFromHex:cl];
}
else
{
val = [val stringByReplacingOccurrencesOfString:@"rgba(" withString:@""];
val = [val stringByReplacingOccurrencesOfString:@"rgb(" withString:@""];
val = [val stringByReplacingOccurrencesOfString:@")" withString:@""];
NSArray *zlist = [val componentsSeparatedByString:@","];
if (zlist.count >= 3)
{
color = [UIColor colorWithRed:[zlist[0] doubleValue]/255.0 green:[zlist[1] doubleValue]/255.0 blue:[zlist[2] doubleValue]/255.0 alpha:zlist.count>3?[zlist[3] doubleValue]:1];
}
}
return color;
}
+ (NSMutableArray <NSMutableDictionary *>*)stylesFromElement:(TFHppleElement *)e
{
NSString *b = [e content];
NSArray <TFHppleElement *> *child = [e children];
NSMutableArray *attributesList = [NSMutableArray arrayWithCapacity:0];
[child enumerateObjectsUsingBlock:^(TFHppleElement * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![obj isTextNode])
{
/*企管只关注CSS样式中的color,backgroudColor,fontSize这三个属性,其余根据后期要求再解析展示
CSS对于样式的定义为:color:orange,font-size:16px,background-color:xxx
*/
NSDictionary *attrMap = [obj attributes];
[attrMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop)
{
if (obj && [obj isKindOfClass:[NSString class]])
{
NSArray *plist = [obj componentsSeparatedByString:@","];
[plist enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
{
NSArray *xlist = [obj componentsSeparatedByString:@":"];
if ([xlist.firstObject isEqualToString:@"color"])
{
NSString *val = [[xlist lastObject] uppercaseString];
UIColor *color = [CSSStyleMap colorFromCssText:val];
if (color)
{
[attributesList addObject:@{@"fontColor":color,@"range":[NSValue valueWithRange:NSMakeRange(0, b.length)]}];
}
}
else if ([xlist.firstObject isEqualToString:@"background-color"])
{
NSString *val = [[xlist lastObject] uppercaseString];
UIColor *color = [CSSStyleMap colorFromCssText:val];
if (color)
{
[attributesList addObject:@{@"backgroundColor":color,@"range":[NSValue valueWithRange:NSMakeRange(0, b.length)]}];
}
}
else if ([xlist.firstObject isEqualToString:@"font-size"])
{
NSInteger val = [[xlist lastObject] integerValue];
[attributesList addObject:@{@"fontSize":intToStr(val),@"range":[NSValue valueWithRange:NSMakeRange(0, b.length)]}];
}
}];
}
}];
}
}];
return attributesList;
}
#endif
@end
页面使用:
NSData *tdata = [cnt dataUsingEncoding:NSUTF8StringEncoding];
TFHpple *doc = [[TFHpple alloc] initWithHTMLData:tdata];
TFHppleElement *e = [[doc searchWithXPathQuery:@"//b"] firstObject];
//包含解析出来的样式数据列表
NSMutableArray <NSMutableDictionary *>*styleList = [CSSStyleMap stylesFromElement:e];
//加粗
NSMutableArray <NSValue *>*btagList = [rMap arrayForKey:@"bTag"];
//a标签,跳转
NSMutableArray <NSDictionary *>*atagList = [rMap arrayForKey:@"aTag"];
//CSS样式
NSMutableArray <NSDictionary *>*styleList = [rMap arrayForKey:@"style"];
//textView是定义的UITextView的实例对象
NSMutableAttributedString *mtext = [[NSMutableAttributedString alloc] initWithAttributedString:textView.attributedText];
NSString *textStr = mtext.string;
// NSArray *ranges = [self mutableRangeOfString:@"®[^\\s]+" targetString:textStr];
[btagList enumerateObjectsUsingBlock:^(NSValue *a, NSUInteger idx, BOOL * _Nonnull stop)
{
[mtext addAttribute:NSFontAttributeName value:[UIFont boldSystemFontOfSize:imTextFont(16)] range:a.rangeValue];
}];
[atagList enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
{
NSRange range = [(NSValue *)[obj objectForKey:@"range"] rangeValue];
[mtext addAttribute:NSForegroundColorAttributeName value:[UIColor colorFromHex:@"#0000ee"] range:range];
[mtext addAttribute:NSLinkTag value:[obj stringForKey:@"href"] range:range];
}];
[styleList enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
{
UIColor *fcolor = [obj objectForKey:@"fontColor"];
NSRange range = [(NSValue *)[obj objectForKey:@"range"] rangeValue];
[mtext addAttribute:NSForegroundColorAttributeName value:fcolor range:range];
}];
[textView setAttributedText:mtext];
这个示例只是展示其中一部分逻辑,你可以进行二次开发,扩展其功能,支持更多的属性的展示!
对了,肯定会有人说使用Webview直接加载就行了嘛,兄弟,我使用的场景是会话页面,你使用webview加载,就会存在性能体验问题!如果你全页面只有一个这样的消息,当然首选就是Webview,而我的页面如下,就不得不考虑用户体验!