NSAttributedString是一种带有属性的字符串,通过它我们可以在一个字符串中实现不同的字体大小、字体颜色、删除线等功能。
可是它的使用经常是这样的:
NSDictionary *attributeDic = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont systemFontOfSize:15.0],NSFontAttributeName,
[UIColor redColor],NSForegroundColorAttributeName,
@(NSUnderlineStyleSingle),NSUnderlineStyleAttributeName,nil];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"Hello world" attributes:attributeDic];
又或者是这样的:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"Hello world"];
[attributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:15.0] range:NSMakeRange(0, attributedString.length)];
[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, attributedString.length)];
[attributedString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, attributedString.length)];
当一个字符串拥有多种样式的时候,更是苦不堪言。
那么是否有什么优雅的方式可以解决这个问题呢?
Masonry与AutoLayout
Masonry是一个对系统NSLayoutConstraint进行封装的第三方自动布局框架,采用链式编程的方式提供给开发者API。
在使用Masonry之前我们的代码通常是这样的:
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
当使用Masonry之后我们的代码是这样的:
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
AutoLayout的代码书写无疑比NSAttributedString的还复杂的多,但经过Masonry的封装之后,我们使用起来可方便多了,感谢Masonry。
那么我们是否也能使用Masonry链式编程的思想来封装NSAttributedString呢?
链式编程分析
make.edges.equalTo(superview).with.insets(padding);
分析以上代码
- 用点语法的形式调用可以得出调用的应该是一个属性
- 调完一次之后能够接着继续调用可以得出应该是在每次调用之后返回了实例本身
- 能在括号里传参数,且有返回值,可以进一步得出这个属性是一个block
NSAttributedString链式编程实现
这里以一个color属性为例。
1.给NSMutableAttributedString创建分类,之所以是NSMutableAttributedString而不是NSAttributedString,是因为NSAttributedString是不可变的,不方便进行属性添加操作
2.创建color的block
- (NSMutableAttributedString *(^)(UIColor *))color;
3.color的实现
- (NSMutableAttributedString *(^)(UIColor *))color {
return ^(UIColor *color) {
[self addAttributeName:NSForegroundColorAttributeName value:color];
return self;
};
}
经过以上几步我们便能对一个NSMutableAttributedString的对象进行.color([UIColor redColor])
的操作了。其它属性的封装同理。
一个关于删除线的Bug
当我封装完成进行测试时,突然发现有时候设置删除线无效,即设置NSStrikethroughStyleAttributeName的时候,该情况发生在iOS10.3。
我在StackOverflow上搜了一下,发现很多人也遇到了这个问题,应该是iOS10.3的一个bug,具体出现的场景还不太清楚,如果有人知道的话可以和我讨论下。
解决方法是同时添加NSBaselineOffsetAttributeName属性。
我最后封装的效果图如下:
如果觉得对您有帮助的话请给我一个Star,如果有bug的话可以给我提Issues,我会尽快解决。
参考
- https://github.com/SnapKit/Masonry
- https://github.com/Draveness/Typeset
- //www.greatytc.com/p/0bf1a7052d25
- https://stackoverflow.com/questions/43070335/nsstrikethroughstyleattributename-how-to-strike-out-the-string-in-ios-10-3
- https://stackoverflow.com/questions/25956183/nsmutableattributedstrings-attribute-nsstrikethroughstyleattributename-doesnt