Fork me on GitHub

记一次特殊需求实现

因为最近一个版本是移动端原生+Unity混合开发,所以有些功能需要U3D部门联调数据交互。由于先前休假几天,导致进度一直比安卓慢半拍,所以对接的事情就先由安卓同事和U3D沟通,有一个需求,不看不知道,一看吓一跳,这数据格式,似乎不科学啊。

1
试看还剩:<font color=#d7bf48>47</font>秒,请返回竖屏购买观看券观看完整版<font color=#ff0000>(¥6)</font>, #ffffff

无奈安卓一端已经调通,再让做出修改貌似不太人性化,只好硬着头皮一通百度Google,尝试各种方案,终于得到一些比较有用的信息。

大招1 NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType

字符串中很明显能看出,有通过HTML标签包装的痕迹,那么我们可以通过加载HTML标签文本的方式,将这个字符串转化为带属性的字符串。API如下:

1
- (nullable instancetype)initWithData:(NSData *)data options:(NSDictionary<NSAttributedStringDocumentReadingOptionKey, id> *)options documentAttributes:(NSDictionary<NSAttributedStringDocumentAttributeKey, id> * __nullable * __nullable)dict error:(NSError **)error NS_AVAILABLE(10_0, 7_0);

然后再对其属性做出调整,这就是设想的方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NSString *toast = [NSString stringWithUTF8String:tip];

UIColor *color = [UIColor whiteColor];
NSString *colorStr = [NSString stringWithUTF8String:toastColor];
if (colorStr && [colorStr hasPrefix:@"#"]) {
colorStr = [colorStr stringByReplacingOccurrencesOfString:@"#" withString:@"0x"];

NSInteger value = [IOSTools numberWithHexString:colorStr];
color = [UIColor colorWithHex:value];
}
if (!color) {
color = [UIColor whiteColor];
}

NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.alignment = NSTextAlignmentCenter;

NSMutableAttributedString * attrStr = [[NSMutableAttributedString alloc] initWithData:[toast dataUsingEncoding:NSUnicodeStringEncoding] options:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];
[attrStr addAttributes:@{ NSFontAttributeName: Unity_toast_font, NSParagraphStyleAttributeName: style } range:NSMakeRange(0, attrStr.length)];

将参数第二部分转化为UIColor

大招2 将一个代表16数的字符串 转成10进制数字

1
2
3
4
5
6
7
8
9
10
+ (NSInteger)numberWithHexString:(NSString *)hexString {

const char *hexChar = [hexString cStringUsingEncoding:NSUTF8StringEncoding];

int hexNumber;

sscanf(hexChar, "%x", &hexNumber);

return (NSInteger)hexNumber;
}

这一步其实是为了将字符串转化为颜色,方法有很多,能达到目的即可。

弄好之后跑起来,看到最终效果后傻眼了:背景是黑色的,没有格式的字也会显示成黑色,这就导致大部分内容直接看不到。给无格式的部分加颜色属性又没有头绪,不知如何区分Rang,无奈搞成这样,倒不如不显示格式,全展示成白色算了。

于是打开SourceTree,开始回退代码,回退过程中想着自己忙活了半天,就这么放弃了,总觉得不甘心,不自觉的去看代码,无意中进入NSAttributedString的类头文件,发现有这么一个API: enumerateAttribute:inRange:,出于职业的敏感,心里瞬间有点小兴奋,赶紧查找关于这个方法的说明,发现这个API可以枚举出AttributedString的Attributed并且能够判断条件,如果初始化的时候选择了NSAttributedString的子类NSMutableAttributedString,那么就可以实现对Attribute的修改,这下可好了,赶紧动手实验一番,run起来一看,成了!

可能大家对block里面的代码有些疑问,问什么判断条件要那么写,其实也是有些无奈,经过调试,我发现没有被颜色标签包裹的文字,经过转换后默认的颜色是黑色(打印出来颜色描述为 0 0 0 1),但是[UIColor blackColor]的颜色描述为0 1,两者不能用==来判断,无奈只能取其白色色值来做匹配,也算是曲线救国吧。

大招3 enumerateAttribute:inRange: 偷天换日

1
2
3
4
5
6
7
8
9
10
11
12
NSAttributedStringEnumerationOptions opts = NSAttributedStringEnumerationLongestEffectiveRangeNotRequired;
[attrStr enumerateAttribute:NSForegroundColorAttributeName inRange:NSMakeRange(0, attrStr.length) options:opts usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {

UIColor *tmpColor = value;
CGFloat white = 1;
CGFloat alpha = 0;
[tmpColor getWhite:&white alpha:&alpha];

if (white == 0 && alpha == 1) {
[attrStr addAttributes:@{ NSForegroundColorAttributeName: color } range:range];
}
}];

欣喜之余颇有感慨,做人做事确实不能轻易放弃,说不定哪个不经意的瞬间,就会有惊喜(^__^)

如果你有更好的方案,欢迎沟通交流!

------------- 本文结束感谢您的阅读 -------------