iOS富文本: NSMutableAttributedString + 链式编程 = 超简单的富文本编辑

CCAttributedString

CCAttributedString是我整理NSMutableAttributedString中AttributedName属性封装好的一个分类,通过链式编程,可以轻松完成对富文本的编辑。不多说,看效果吧。

代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"通过NSRange添加效果\n给第三个123123123加特效\n给所有的123123123加特效\n拉开距离\n扁平\n脉动不回来了\n一条线两条线删除线\n空心 实心\nfl是连在一起的\n听说代写多了,眼睛会花\n凸版印刷效果\n正常状态和设置偏移量后\n让我打一套组合拳\n这句话是反的\n最后我选择滑稽"];
    
    string.textColor([UIColor redColor]).font([UIFont systemFontOfSize:30]).range(NSMakeRange(2, 7));
    
    string.textColor([UIColor redColor]).font([UIFont systemFontOfSize:10]).backgroundColor([UIColor darkGrayColor]).rangeIndex(2).rangeString(@[@"3",@"1"]);
    string.textColor([UIColor blackColor]).font([UIFont boldSystemFontOfSize:30]).all().rangeString(@"2");
    string.textSpace(10).rangeString(@"拉开距离");
    string.textColor([UIColor blackColor]).font([UIFont systemFontOfSize:30]).backgroundColor([UIColor cyanColor]).rangeString(@"本质");
    string.obliqueness(YES).textColor([UIColor orangeColor]).rangeString(@"脉动不回来了");
    string.expansion(YES).textColor([UIColor purpleColor]).rangeString(@"扁平");
    string.underline(YES).underlineColor([UIColor orangeColor]).rangeString(@"一条线");
    string.underline(NSUnderlineStyleDouble).underlineColor([UIColor blueColor]).rangeString(@"两条线");
    string.strikethrough(YES).strikethroughColor([UIColor redColor]).rangeString(@"删除线");
    string.strokeWidth(5).strokeColor([UIColor brownColor]).font([UIFont systemFontOfSize:30]).rangeString(@"空心");
    string.strokeWidth(-5).strokeColor([UIColor brownColor]).font([UIFont systemFontOfSize:30]).rangeString(@"实心");
    string.ligature(YES).font([UIFont fontWithName: @"futura" size: 30]).textColor([UIColor redColor]).rangeIndex(0).rangeString(@"fl");
    string.effectLetterpressStyle(YES).rangeString(@"凸版印刷效果");
    string.textColor([UIColor darkGrayColor]).font([UIFont systemFontOfSize:8]).baselineOffset(10).rangeString(@"设置偏移量后");
    string.textColor([UIColor grayColor]).backgroundColor([UIColor orangeColor]).textSpace(10).obliqueness(YES).expansion(YES).underline(YES).underlineColor([UIColor blueColor]).strikethrough(YES).rangeString(@"让我打一套组合拳");
    string.writingDirection(@[@(NSWritingDirectionRightToLeft | NSTextWritingDirectionOverride)]).rangeString(@"这句话是反的");
    
    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    attachment.image = [UIImage imageNamed:@"huaji"];
    attachment.bounds = CGRectMake(0, -4, 20, 20);
    string.attachment(attachment,string.length);
    
    NSShadow *shadow = [[NSShadow alloc] init];
    shadow.shadowColor = [UIColor darkGrayColor];
    shadow.shadowOffset = CGSizeMake(2, 2);
    string.shadow(shadow).rangeString(@"听说代写多了,眼睛会花");
    
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height*4/5)];
    label.textColor = [UIColor blueColor];
    label.numberOfLines = 0;
    [self.view addSubview:label];
    label.attributedText = string;
    
    
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height*4/5, self.view.frame.size.width, self.view.frame.size.height/5)];
    textView.delegate = self;
    textView.editable = NO;
    [self.view addSubview:textView];
    NSMutableAttributedString *linkString = [[NSMutableAttributedString alloc] initWithString:@"textview\n这是一条链接"];
    linkString.link([NSURL URLWithString:@"https://www.baidu.com"]).textColor([UIColor orangeColor]).font([UIFont systemFontOfSize:20]).underline(YES).rangeString(@"链接");
    textView.attributedText = linkString;

}

效果:

熟悉Masonry的人都对链式写法司空见惯了,相比NSMutableAttributedString提供的方法,比如:

NSDictionary *attributeDict = [NSDictionary dictionaryWithObjectsAndKeys:
                                    [UIFontsystemFontOfSize:15.0],NSFontAttributeName,
                                    [UIColorredColor],NSForegroundColorAttributeName,
                                   NSUnderlineStyleAttributeName,NSUnderlineStyleSingle,nil];
NSMutableAttributedString *AttributedStr = [[NSMutableAttributedStringalloc]initWithString:@"富文本" attributes:attributeDict];

使用CCAttributedString可以一行之内解决战斗,最重要的是不用每次去翻文档找那一串参数名了:

string.font([UIFont systemFontOfSize:15]).textColor([UIColor redColor]).underline(YES).rangeString(@"xxxxx");

每次添加属性的时候,都需要以range()或rangeString()结束,所有的方法说明如下:

@interface NSMutableAttributedString (CCAttributedString)

- (NSMutableAttributedString *(^)(UIColor *))textColor;//字体颜色
- (NSMutableAttributedString *(^)(UIColor *))backgroundColor;//背景颜色
- (NSMutableAttributedString *(^)(UIFont *))font;//字号
- (NSMutableAttributedString *(^)(NSUnderlineStyle))underline;//下划线
- (NSMutableAttributedString *(^)(UIColor *))underlineColor;//下划线颜色
- (NSMutableAttributedString *(^)(UIColor *))strokeColor;//描边
- (NSMutableAttributedString *(^)(CGFloat))strokeWidth;//描边宽度
- (NSMutableAttributedString *(^)(BOOL))obliqueness;//斜体
- (NSMutableAttributedString *(^)(BOOL))expansion;//扁平体
- (NSMutableAttributedString *(^)(BOOL))strikethrough;//删除线
- (NSMutableAttributedString *(^)(UIColor *))strikethroughColor;//删除线颜色
- (NSMutableAttributedString *(^)(BOOL))ligature;//连体
- (NSMutableAttributedString *(^)(NSShadow *))shadow;//字体阴影
- (NSMutableAttributedString *(^)(BOOL))effectLetterpressStyle;//凸版印刷体
- (NSMutableAttributedString *(^)(CGFloat))baselineOffset;//字体偏移
- (NSMutableAttributedString *(^)(NSURL *))link;//URL链接
- (NSMutableAttributedString *(^)(id))writingDirection;//书写方向
- (NSMutableAttributedString *(^)(CGFloat))lineSpace;//行间距
- (NSMutableAttributedString *(^)(CGFloat))textSpace;//字间距
- (NSMutableAttributedString *(^)(NSTextAttachment *,NSInteger))attachment;//插入图片
- (NSMutableAttributedString *(^)(id))rangeString;//根据一串或一组字符串添加效果
- (NSMutableAttributedString *(^)(NSRange))range;//根据range添加效果
- (NSMutableAttributedString *(^)())all;//匹配所有的rangeString,添加效果
- (NSMutableAttributedString *(^)(NSInteger))rangeIndex;//给第几个rangeString添加效果

@end

一些注意到地方有,rangeString()这个方法可以传一个或一组字符串,all()可以找到所有目标字符串,rangeIndex()可以找到第n个目标字符串,以后我还会丰富它的功能。

实现原理

链式编程

通过返回一个返回值为NSMutableAttributedString的block,可以实现这种效果

- (NSMutableAttributedString *(^)(UIColor *))textColor {
    return ^ id (UIColor *color) {
        self.attributedBuilder.currentTextColor = color;
        return self;
    };
}

我建了一个名为CCAttributedStringBuilder的类,用来保存每个方法传过来的值,并在rangeString或range调用后给NSMutableAttributedString添加属性,最后清除掉当前builder保存的属性。


@interface CCAttributedStringBuilder : NSObject

@property (nonatomic, strong) UIColor *currentTextColor;
@property (nonatomic, strong) UIColor *currentBackgroundColor;
@property (nonatomic, strong) UIFont *currentFont;
@property (nonatomic, strong) NSNumber *obliqueness;
@property (nonatomic, strong) NSNumber *Expansion;
@property (nonatomic, strong) NSNumber *textSpace;
@property (nonatomic, strong) NSNumber *underline;
@property (nonatomic, strong) UIColor *underLineColor;
@property (nonatomic, strong) UIColor *strokeColor;
@property (nonatomic, strong) NSNumber *strokeWidth;
@property (nonatomic, strong) NSNumber *strikethrough;
@property (nonatomic, strong) UIColor *strikethroughColor;
@property (nonatomic, strong) NSNumber *ligature;
@property (nonatomic, strong) NSShadow *shadow;
@property (nonatomic, strong) NSNumber *effectLetterpressStyle;
@property (nonatomic, strong) NSNumber *baselineOffset;
@property (nonatomic, strong) NSURL *link;
/**
 @[@(NSWritingDirectionLeftToRight | NSTextWritingDirectionEmbedding)]
 @[@(NSWritingDirectionLeftToRight | NSTextWritingDirectionOverride)]
 @[@(NSWritingDirectionRightToLeft | NSTextWritingDirectionEmbedding)]
 @[@(NSWritingDirectionRightToLeft | NSTextWritingDirectionOverride)]
 */
@property (nonatomic, strong) id writingDirection;
@property (nonatomic, strong) NSNumber *all;
@property (nonatomic, strong) NSNumber *rangeIndex;
@property (nonatomic, assign) BOOL isAddingInArray;

@end
- (NSMutableAttributedString *(^)(id))rangeString {
    return ^ id (id rangeObj) {
        
        void(^addBlock)(NSString *) = ^ (NSString *targetString) {
            if (self.attributedBuilder.rangeIndex) {
                [self addAttributesAtRangeIndexWithObj:targetString];
            } else if (self.attributedBuilder.all && [self.attributedBuilder.all boolValue]) {
                [self addAttributesAllWithObj:targetString];
            } else {
                [self addAttributesWithObj:targetString];
            }
        };
        
        if ([rangeObj isKindOfClass:[NSString class]]) {
            addBlock(rangeObj);
        } else if ([rangeObj isKindOfClass:[NSArray class]]) {
            for (id obj in rangeObj) {
                if ([obj isKindOfClass:[NSString class]]) {
                    self.attributedBuilder.isAddingInArray = !([obj isEqual:[rangeObj lastObject]]);
                    addBlock(obj);
                }
            }
        }
        return self;
    };
}

- (NSMutableAttributedString *(^)(NSRange))range {
    return ^ id (NSRange range) {
        [self addAttributesWithRange:range];
        return self;
    };
}

代码在Github上,拙作也望大家路过给个星星,祝大家代码越写越好。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容