NSString简单细说(十六)—— 画字符串

版本记录

版本号 时间
V1.0 2017.06.03

前言

前面我简单的写了些NSString的初始化,写了几篇,都不难,但是可以对新手有一定的小帮助,对于大神级人物可以略过这几篇,NSString本来就没有难的,都是细枝末节,忘记了查一下就会了,没有技术难点,下面我们继续~~~
1. NSString简单细说(一)—— NSString整体架构
2. NSString简单细说(二)—— NSString的初始化
3. NSString简单细说(三)—— NSString初始化
4. NSString简单细说(四)—— 从URL初始化
5. NSString简单细说(五)—— 向文件或者URL写入
6. NSString简单细说(六)—— 字符串的长度
7. NSString简单细说(七)—— 与C字符串的转化
8. NSString简单细说(八)—— 识别和比较字符串
9. NSString简单细说(九)—— 字符串的合并
10. NSString简单细说(十)—— 字符串的分解
11. NSString简单细说(十一)—— 字符串的查找
12. NSString简单细说(十二)—— 字符串的替换
13. NSString简单细说(十三)—— 字符串的分行和分段
14. NSString简单细说(十四)—— 字符串位置的计算
15. NSString简单细说(十五)—— 字符串转化为propertyList

画字符串

一、- (void)drawAtPoint:(CGPoint)point withAttributes:(NSDictionary<NSString *,id> *)attrs;

这个方法的作用是:在指定的图形上下文中,画出具有字体大小和给定特性的字符串。下面看一下这个方法的两个参数。

  • point:该点就是当前图形上下文中你想开始画字符串的点,图形上下文的坐标由你想画字符串的视图决定,在AppKit中,原点位于画图区域的左下角,但是如果对焦视图被翻转,那么原点位于左上角。
  • attrs:该参数是字典类型,是字符串的特性。这些特性也可以用于NSAttributedString对象,但是,在NSString对象中,特性被应用于整个字符串,而不是用于一段字符串。
  • 注意:不同于drawInRect:withAttributes:方法使用边界矩形,这个方法的渲染区域是没有限制的,因此,此方法渲染在一行中,但是,如果字符串中包含换行符,那么这个换行符也会被解释和使用,导致后续文本放在起始点的下一行。在调用该方法时,必须有一个显示视图或者可用的图形上下文。

下面我们看代码

#import "JJStringDrawView.h"

@implementation JJStringDrawView

//画字符串
- (void)drawRect:(CGRect)rect
{
    /**
     * 1. - (void)drawAtPoint:(CGPoint)point withAttributes:(NSDictionary<NSString *,id> *)attrs;
     *
     *  @param point:The point in the current graphics context where you want to start drawing the string. The coordinate system of the graphics context is usually defined by the view in which you are drawing. In AppKit, the origin is normally in the lower-left corner of the drawing area, but the origin is in the upper-left corner if the focused view is flipped.
     
     *  @param attrs:A dictionary of text attributes to be applied to the string. These are the same attributes that can be applied to an NSAttributedString object, but in the case of NSString objects, the attributes apply to the entire string, rather than ranges within the string.
     *
     */
    
    NSString *str = @"我只是一个字符串而已!";
    CGPoint point = CGPointMake(50.0, 200.0);
    NSDictionary *attrDict = @{NSFontAttributeName:[UIFont systemFontOfSize:16.0],NSForegroundColorAttributeName:[UIColor redColor]};
    [str drawAtPoint:point withAttributes:attrDict];

}

@end

上面我就是在view的drawRect里面画的字符串,下面看结果。

画字符串

结论:看代码大家都能看懂,但是这里我碰到了bug

2017-06-03 00:28:38.485 NSString你会用吗?[2840:95594] -[__NSCFConstantString pointSize]: unrecognized selector sent to instance 0x10008c120
2017-06-03 00:28:38.545 NSString你会用吗?[2840:95594] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFConstantString pointSize]: unrecognized selector sent to instance 0x10008c120'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000100c00d4b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010066221e objc_exception_throw + 48
    2   CoreFoundation                      0x0000000100c70f04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x0000000100b86005 ___forwarding___ + 1013
    4   CoreFoundation                      0x0000000100b85b88 _CF_forwarding_prep_0 + 120
    5   UIFoundation                        0x0000000104075dd6 __NSStringDrawingEngine + 3204
    6   UIFoundation                        0x00000001040734fa -[NSString(NSStringDrawing) drawAtPoint:withAttributes:] + 229
    7   NSString你会用吗?             0x00000001000897d7 -[JJStringTestVC drawNSString] + 263
    8   NSString你会用吗?             0x00000001000896bd -[JJStringTestVC viewDidLoad] + 189
    9   UIKit                               0x00000001011c6a3d -[UIViewController loadViewIfRequired] + 1258
    10  UIKit                               0x00000001011cd062 -[UIViewController __viewWillAppear:] + 118
    11  UIKit                               0x00000001011f81d3 -[UINavigationController _startCustomTransition:] + 1290
    12  UIKit                               0x0000000101208e48 -[UINavigationController _startDeferredTransitionIfNeeded:] + 697
    13  UIKit                               0x0000000101209fdb -[UINavigationController __viewWillLayoutSubviews] + 58
    14  UIKit                               0x0000000101400dd7 -[UILayoutContainerView layoutSubviews] + 223
    15  UIKit                               0x00000001010e9ab8 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1237
    16  QuartzCore                          0x00000001058f8bf8 -[CALayer layoutSublayers] + 146
    17  QuartzCore                          0x00000001058ec440 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 366
    18  QuartzCore                          0x00000001058ec2be _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    19  QuartzCore                          0x000000010587a318 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 280
    20  QuartzCore                          0x00000001058a73ff _ZN2CA11Transaction6commitEv + 475
    21  QuartzCore                          0x00000001058a7d6f _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 113
    22  CoreFoundation                      0x0000000100ba5267 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    23  CoreFoundation                      0x0000000100ba51d7 __CFRunLoopDoObservers + 391
    24  CoreFoundation                      0x0000000100b89f8e __CFRunLoopRun + 1198
    25  CoreFoundation                      0x0000000100b89884 CFRunLoopRunSpecific + 420
    26  GraphicsServices                    0x00000001049f2a6f GSEventRunModal + 161
    27  UIKit                               0x0000000101024c68 UIApplicationMain + 159
    28  NSString你会用吗?             0x00000001000895df main + 111
    29  libdyld.dylib                       0x0000000103a6268d start + 1
    30  ???                                 0x0000000000000001 0x0 + 1
)

是因为

NSDictionary *attrDict = @{NSFontAttributeName:@"16",NSForegroundColorAttributeName:[UIColor redColor]};

这里字号不能直接写@"16",而是要像下边这样写

  NSDictionary *attrDict = @{NSFontAttributeName:[UIFont systemFontOfSize:16.0],NSForegroundColorAttributeName:[UIColor redColor]};

修改以后就不崩溃了。打印出来就会发现,苹果是这么转化这个字典的。

(lldb) po attrDict
{
    NSColor = "UIExtendedSRGBColorSpace 1 0 0 1";
    NSFont = "<UICTFont: 0x7f9537508c40> font-family: \".SFUIText\"; font-weight: normal; font-style: normal; font-size: 16.00pt";
}

二、- (void)drawInRect:(CGRect)rect withAttributes:(NSDictionary<NSString *,id> *)attrs;

有上面的方法做基础,那么这个也不难理解,这个是在某个固定区域上画字符串。我们还是先看参数。

  • rect:绘制字符串的矩形边界,在AppKit中,边框的原点位于左下角,但是如果视图被翻转,那么原点就位于左上角。
  • attrs:该参数是字典类型,是字符串的特性。这些特性也可以用于NSAttributedString对象,但是,在NSString对象中,特性被应用于整个字符串,而不是用于一段字符串。
  • 注意:该方法尽可能的在指定的矩形区域中画尽量多的字符串,如果字符串相对于矩形框太长,该方法会尽量去渲染并切除多余的部分,如果字符串中包含换行符,那么换行符会起作用,并且换行后会在初始原点的下方显示。在调用该方法时,必须有一个显示视图或者可用的图形上下文。

下面看代码

//画字符串
- (void)drawRect:(CGRect)rect
{
    /**
     * 2. - (void)drawInRect:(CGRect)rect withAttributes:(NSDictionary<NSString *,id> *)attrs;
     *
     *  @param rect:The bounding rectangle in which to draw the string. In AppKit, the origin of the bounding box is normally in the lower-left corner, but the origin is in the upper-left corner if the focused view is flipped.

     *  @param attrs:A dictionary of text attributes to be applied to the string. These are the same attributes that can be applied to an NSAttributedString object, but in the case of NSString objects, the attributes apply to the entire string, rather than ranges within the string.
     *
     */
    
    NSString *str = @"1.我只是一个字符串而已,就是有点长!2.我只是一个字符串而已,就是有点长!3.我只是一个字符串而已,就是有点长!4.我只是一个字符串而已,就是有点长!5.我只是一个字符串而已,就是有点长!6.我只是一个字符串而已,就是有点长!7.我只是一个字符串而已,就是有点长!";
    CGRect drawRect = CGRectMake(250.0, 400.0, 50.0, 50.0);
    NSDictionary *attrDict = @{NSFontAttributeName:[UIFont systemFontOfSize:20.0],NSForegroundColorAttributeName:[UIColor blueColor]};
    [str drawInRect:rect withAttributes:attrDict];
}

下面看输出结果

画字符串

结论:字符串是画出来了,但是我还是有一个疑问,这里我已经显示了rect区域,但是不管rect原点设置在哪里,都不起作用,都是在左上角开始画,方法一中的drawPoint就起作用。希望知道的能给我解答下,谢谢,待我后续更改。


三、- (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options attributes:(NSDictionary<NSString *,id> *)attributes context:(NSStringDrawingContext *)context;

该方法的作用就是根据指定的选型option,在指定区域中画字符串。还是先看参数配置

  • rect :画字符串的边界
  • options:选项也就是配置参数,在渲染时候需要参考这个额外的参数,这个是个枚举值
typedef enum NSStringDrawingOptions : NSInteger {
    // 整个文本将以每行组成的矩形为单位计算整个文本的尺寸
    // The specified origin is the line fragment origin, not the base line origin 
    NSStringDrawingUsesLineFragmentOrigin = 1 << 0,

   // 使用字体的行间距来计算文本占用的范围,即每一行的底部到下一行的底部的距离计算 
   // Uses the font leading for calculating line heights 
    NSStringDrawingUsesFontLeading = 1 << 1,

    // 将文字以图像符号计算文本占用范围,而不是以字符计算。也即是以每一个字体所占用的空间来计算文本范围  
    // Uses image glyph bounds instead of typographic bounds 
    NSStringDrawingUsesDeviceMetrics = 1 << 3,

    // 当文本不能适合的放进指定的边界之内,则自动在最后一行添加省略符号。如果NSStringDrawingUsesLineFragmentOrigin没有设置,则该选项不生效  
    // Truncates and adds the ellipsis character to the last visible line if the text doesn't fit into the bounds specified. 
    NSStringDrawingTruncatesLastVisibleLine = 1 << 5
} NSStringDrawingOptions;

  • attributes:该参数是字典类型,是字符串的特性。这些特性也可以用于NSAttributedString对象,但是,在NSString对象中,特性被应用于整个字符串,而不是用于一段字符串。

  • context:这个参数就是上下文,当attribute=nil的时候,这里的方法使用<UIKit/NSAttributedString.h>中每个attribute的默认方法,当stringDrawingContext=nil时,相当于使用默认的初始化方法,[[NSStringDrawingContext alloc] init], 看下面:

// When attributes=nil, the methods declared here uses the default behavior for each attribute described in <UIKit/NSAttributedString.h>. When stringDrawingContext=nil, it's equivalent of passing the default instance initialized with [[NSStringDrawingContext alloc] init].

NS_CLASS_AVAILABLE(10_11, 6_0) @interface NSStringDrawingContext : NSObject

下面直接看代码

//画字符串
- (void)drawRect:(CGRect)rect
{
    /**
     * 3. - (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options attributes:(NSDictionary<NSString *,id> *)attributes context:(NSStringDrawingContext *)context;
     *
     *  @param rect:The bounding rectangle in which to draw the string.
     
     *  @param options:Additional drawing options to apply to the string during rendering. For a list of possible values, see NSStringDrawingOptions.
     
     *  @param attributes:The text attributes with which to draw the string. These are the same attributes that can be applied to an NSAttributedString object, but in the case of NSString objects, the attributes apply to the entire string, rather than ranges within the string.
     
     *  @param context:A context object with information about how to adjust the font tracking and scaling information. On return, the specified object contains information about the actual values used to render the string. This parameter may be nil.
     *
     */
    
    NSString *str = @"1.我只是一个字符串而已,就是有点长!";
    CGRect drawRect = CGRectMake(10.0, 400.0, 150.0, 50.0);
    NSDictionary *attrDict = @{NSFontAttributeName:[UIFont systemFontOfSize:20.0],NSForegroundColorAttributeName:[UIColor blueColor]};
    [str drawWithRect:drawRect options:NSStringDrawingUsesDeviceMetrics attributes:attrDict context:nil];
}

下面看输出结果

画字符串

四、- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary<NSString *,id> *)attributes context:(NSStringDrawingContext *)context;

该方法计算并返回在当前图形上下文中指定的矩形内使用给定选项和字符串绘制的矩形。还是先看参数:

  • size:要画字符串的尺寸边界
  • options:选项值,这个是一个枚举。
  • attributes:该参数是字典类型,是字符串的特性。这些特性也可以用于NSAttributedString对象,但是,在NSString对象中,特性被应用于整个字符串,而不是用于一段字符串。
  • context:画字符串的上下文,指定最小比例因子和缩进调整值。
  • 注意:为了正确的画和计算多行文本,那么option参数需要传递值NSStringDrawingUsesLineFragmentOrigin。这个方法使用返回的尺寸计算视图大小,必须使用ceil函数得到最近的最大整数。这个方法返回字符串字形的实际边界,一些字形(例如空格)可以覆盖传入尺寸的布局约束,因此有些时候,返回的CGRect的width可能会超过size参数的宽度值。

下面直接看代码

- (void)drawRect:(CGRect)rect
{
    /**
     * 4. - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary<NSString *,id> *)attributes context:(NSStringDrawingContext *)context;
     *
     *  @param size:The size of the rectangle to draw in.
     
     *  @param options:String drawing options.
     
     *  @param attributes:The text attributes with which to draw the string. These are the same attributes that can be applied to an NSAttributedString object, but in the case of NSString objects, the attributes apply to the entire string, rather than ranges within the string.
     
     *  @param context:The string drawing context to use for the receiver, specifying minimum scale factor and tracking adjustments.
     *
     *  @return :The bounding rect for the receiver drawn using the given options and display characteristics. The rect origin returned from this method is the first glyph origin.
     */
    
    NSString *str = @"1.我只是一个字符串而已,就是有点长!";
    CGSize size = CGSizeMake(MAXFLOAT, 22);
    NSDictionary *attrDict = @{NSFontAttributeName:[UIFont systemFontOfSize:20.0],NSForegroundColorAttributeName:[UIColor magentaColor]};
    CGRect strRect = [str boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attrDict context:nil];
    NSLog(@"strRect=%@",NSStringFromCGRect(strRect));
}

下面看输出结果

2017-06-03 16:22:22.159 NSString你会用吗?[8190:292756] strRect={{0, 0}, {345.95304687499998, 23.8671875}}

结论:还是直接看代码吧。


五、- (CGSize)sizeWithAttributes:(NSDictionary<NSString *,id> *)attrs;

该方法返回的是在给定特性下计算得到的边界尺寸。下面先看参数

  • attrs:该参数是字典类型,是字符串的特性。这些特性也可以用于NSAttributedString对象,但是,在NSString对象中,特性被应用于整个字符串,而不是用于一段字符串。
  • return:给定特性下的字符串size。
  • 注意:该方法返回的是份数尺寸,要使用返回的尺寸来约束视图,必须利用ceil函数得到这个返回值的最近最小的整数。

下面还是直接看代码。

- (void)drawRect:(CGRect)rect
{
    /**
     * 5. - (CGSize)sizeWithAttributes:(NSDictionary<NSString *,id> *)attrs;
     *
     *  @param attrs:The text attributes with which to draw the string. These are the same attributes that can be applied to an NSAttributedString object, but in the case of NSString objects, the attributes apply to the entire string, rather than ranges within the string.
     *
     *  @return :The bounding box size the receiver occupies when drawn with the specified attributes.
     */
    
    NSString *str = @"1.我只是一个字符串而已,就是有点长!";
    NSDictionary *attrDict = @{NSFontAttributeName:[UIFont systemFontOfSize:20.0],NSForegroundColorAttributeName:[UIColor magentaColor]};
    CGSize strSize = [str sizeWithAttributes:attrDict];
    NSLog(@"strSize=%@",NSStringFromCGSize(strSize));
    NSLog(@"size=%.f--height=%.f",ceil(strSize.width),ceil(strSize.height));
}

下面看输出结果

2017-06-03 16:37:34.959 NSString你会用吗?[8584:306231] strSize={345.95304687499998, 23.8671875}
2017-06-03 16:37:34.959 NSString你会用吗?[8584:306231] size=346--height=24

结论:还是直接看代码吧。

后记

周六放假了,未完,待续,欢迎留言哦~~~

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

推荐阅读更多精彩内容