图形与动画(一)--附Demo

最近在看《iOS 6 Programming Cookbook》的翻译版,它由DevDiv论坛的网友翻译,原文地址:点击跳转。由于下载的pdf都有水印,并且排版不是很好,特别是代码排版,基本不能看,所以这里就整理了一下,方便再次查看。另外把里面提到的点写了一个demo,由于里面一些代码现在已经废弃,所以demo中都是用的新api,下载地址在这里:图形与动画Demo

1.1 枚举和加载字体

字体是在图形用户界面上显示文字的基础。UIKIt 框架为程序员提供了便于枚举,加载,和使用字体的高级别 API。字体被封装于 Cocoa Touch 中的 UIFont 类中。每个 iOS 设 备自身都有内建的系统字体。字体被组织到 family 中,每个 family 中包含 face。比如,Helvetica 是一个字体 family,Helvetica BoldHelvetica 家庭的一个 face。为了能加载字体,你必须知道字体的face(也就是他的名字)--- 要想知道它的face,就必须知道 family,所以,我们首先要枚举出安装到设备上的所有的字体family,使用 UIFont 类的 familyNames 类方法:

- (void) enumerateFonts
{
    for (NSString *familyName in [UIFont familyNames])
    {
        NSLog(@"Font Family = %@", familyName);
    }
}

在 iOS 模拟器上运行这个程序,我得到了类似下面的结果:

Font Family = Heiti TC
Font Family = Sinhala Sangam MN
Font Family = Kannada Sangam MN
Font Family = Georgia
Font Family = Heiti J
Font Family = Times New Roman Font
Family = Snell Roundhand
Font Family = Geeza Pro
Font Family = Helvetica Neue ...

在得到字体 family 之后,我们可以在每个 family 内部枚举字体名字。我们使用UIFont 类的 fontNamesForFamilyName:类方法,可以得到以 family 名字作为参数得到的字体名字数组:

- (void) enumerateFonts
{
    for (NSString *familyName in [UIFont familyNames])
    { 
        NSLog(@"Font Family = %@", familyName);
        for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName])
        {
            NSLog(@"\t%@", fontName);
        }
    }
}

在 iOS 设备上运行上面的代码,我们得到如下结果:

...
Font Family = Geeza Pro
GeezaPro
GeezaPro-Bold
Font Family = Helvetica Neue
HelveticaNeue-Italic 
HelveticaNeue-Bold 
HelveticaNeue-BoldItalic
HelveticaNeue
...

你可以看到,Helvetica Neue 是字体 family,HelveticaNeue-Bold 是这个family 中的一个字体名字。现在我们知道了字体名字,我们就可以使用 UIFont 的fontWithName:size:类方法 将字体加载到 UIFont 类型对象中:

UIFont *helveticaBold = [UIFont fontWithName:@"HelveticaNeue-Bold" size:12.0f];

如果 UIFont 类的 fontWithName:size:类方法的返回结果为 nil,指定的字体名字不能被找到。首先枚举出所有的字体 family 和该 family 中的所有字体名字,可以确 保你提供的字体名字在系统中可用。
你也可以使用 UIFont 类的 systemFontOfSize:类方法(译者:原文是 instance method,但 实际上是类方法)(或者它的粗体方法,boldSystemFontOfSize:)从你代码运行的设备上加 载系统字体,不管这些字体是什么。iOS 设备的默认系统字体是 Helvetica

1.2 绘制文本

  • 使用 NSString 的 drawAtPoint:withAttributes:方法绘制文字

drawRect:方法是我们要进行绘制的地方,如前面所提到的那样。在此,我们可以开始加载字体,然后在屏幕上 x 轴的 40 及 y 轴 180 处以 40 点的字体画出一个简单的字符串(图 1-1):

- (void)drawRect:(CGRect)rect
{
    UIFont *font = [UIFont fontWithName:@"IowanOldStyle-BoldItalic"
                                   size:30];
    NSString *str = @"some string";
    [str drawAtPoint:CGPointMake(20, 200)
      withAttributes:@{NSFontAttributeName:font}];
}

在上面的代码中,我见仅仅是加载了一个 40 点尺寸的粗体 Helvetica 字体,然后使用它 来在点(40, 180)画出了文本“Some String”。

图 1-1 在一个视图的图形环境上绘制的一个随机字符串

绘制颜色和字体

- (void)drawRect:(CGRect)rect
{
    UIColor *magentaColor =[UIColor redColor];
    UIFont *font = [UIFont fontWithName:@"IowanOldStyle-BoldItalic"
                                   size:30];
    NSString *str = @"some string";
    [str drawAtPoint:CGPointMake(20, 200)
      withAttributes:@{NSFontAttributeName:font,
                       NSForegroundColorAttributeName:magentaColor}];

使用 NSString 的 drawInRect:withAttributes:方法绘制文字

使用 NSString 类的drawInRect:withAttributes:实例方法,将文本绘制在指定矩形空间 中。文本为了适配矩形会被拉伸

- (void)drawRect:(CGRect)rect
{
    // Drawing code
    UIColor *magentaColor =[UIColor redColor];
    UIFont *font = [UIFont fontWithName:@"IowanOldStyle-BoldItalic"
                                   size:30];
    NSString *str = @"I Learn Really Fast";
    [str drawInRect:CGRectMake(20, 200, 100, 200)
     withAttributes:@{NSFontAttributeName:font,
                      NSForegroundColorAttributeName:magentaColor}];
}

效果如下图所示:

图1-2 绘制文本"I Learn Really Fast"

获取颜色的组成

/* Load the color */
    UIColor *steelBlueColor = [UIColor colorWithRed:0.3f
                                              green:0.4f
                                               blue:0.6f
                                              alpha:1.0f];
    CGColorRef colorRef = [steelBlueColor CGColor];
    const CGFloat *components = CGColorGetComponents(colorRef);
    NSUInteger componentsCount = CGColorGetNumberOfComponents(colorRef);
    NSUInteger counter = 0;
    for (counter = 0; counter < componentsCount;  counter++)
    {
        NSLog(@"Component %lu = %.02f", (unsigned long)counter + 1, components[counter]);
    }
   

在我们运行上面的代码后,控制台窗口的输出为:

Component 1 = 0.30
Component 2 = 0.40
Component 3 = 0.60
Component 4 = 1.00

1.3 绘制图像

  • iOS 中的一些比较重要的方法
imageNamed: 类方法

加载图片(如果加载成功还会缓存图像)。参这个方法的参数是 bundle 中的图像名字,比如 Tree Texture.png。

imageWithData: 类方法

NSData 对象实例中包裹的数据中加载图片,NSData 对象是此方法的参数传入的。

initWithContentsOfFile:实例方法(用于初始化)

使用指定参数作为路径来加载一个图像,并用来初始化图像对象。路径应该是在 bundle 中图像的完整路径

initWithData:实例方法(用于初始化)

使用 NSData 类型的指定参数来初始化图像。这个数据应该属于一个有效图像。

  • 在图形环境上绘制 UIImage 类型的图片的两种最简单的方法是:
 drawAtPoint:UIImage 的实例方法

将图片以原始尺寸绘制到指定坐标点。使用 CGPointMake 函数来构造坐标点。

drawInRect:UIImage 的实例方法 

在指定的矩形空间绘制图片,要构造这个矩形空间,请使用 CGRectMake 函数。

两个绘制图片的方法与绘制文字方法类似,就不贴代码了,可以在demo里面查看。

1.4 构造可伸缩图片

使用 UIImage 类的实例方法 resizableImageWithCapInsets:创建一个可伸缩图片。

第一次听说可伸缩图片可能会感到陌生,在程序中它主要是针对不同的显示需求。例如,你的程序可能希望为按钮 供一个背景图片。按钮内部有一个很大的文本,按钮本身也很宽。这对这样的情况,有两种方法为按钮供背景图片:

  • 为不同尺寸的按钮各自创建一个图片。这将会增加 bundle 的大小,并消耗更多的内存,你也需要做更多的工作。另外,如果按钮内的文本发生了任何改变,你都需要为按钮 供一个适当的图片。

  • 创建一个可伸缩图片,程序中所有的按钮都可以使用这个图片。

毫无疑问,第二种方法非常合适。那么伸缩图片是什么呢?伸缩图片是将一个图片分为虚拟的两部分:

  • 不需要被拉伸的部分
  • 被拉伸以适应任何尺寸的部分
    如图 4-1 所示,我为一个按钮创建了一个图片。这个图片具有渐变的效果。我在图片上画的这个矩形区域内容可以将其从图片中裁减出来。
图4-1 一个可以作为伸缩图片候选的图片

你可能会问为什么?如图 4-2,

图4-2 中间的各个切片图片内容都是相同的

我以相同的大小将图片切为许多切片,这些切片内容是一样的。仔细看一下,如果矩形区域内我只留下 1pixel 宽,高度跟图片一样,那么我还能用这个图片为一个按钮构建一个背景图片吗?答案是可以,并且很简单。在这里,图片保持着相同的跨度,在中间,我们可以只留下 1像素的宽度即可.如图 图4-3 所示——对图片操作过后的结果。

图4-3 利用 1 个像素的宽度当做可伸缩区域

我们如何告诉 iOS SDK 哪部份图片保存不变,而哪部份图片有可以进行伸缩呢?事实证明,iOS SDK已经对此做好了准备。首先,利用本章学到的内容——使用 UIImage APIs 将图片加载到内存。然后使用 UIImage 的实例对象的实例方法 resizableImgaeWithCapInsets:对图片进行可伸缩区域的设置。这个方法的参数类似UIEdgeInsets,定义方法属下:

typedef struct UIEdgeInsets
{
    CGFloat top, left, bottom, right;
} UIEdgeInsets

Edge insets 运允许我们创建九图。九图是一个图片,由九不分组成:

  • 左上(Upper left corner)
  • 上(Top edge)
  • 右上(Upper right corner)
  • 右(Right edge)
  • 右下(Lower right corner)
  • 下(Bottom edge)
  • 左下(Lower left corner)
  • 左(Left edge)
  • 中心(Center)
    图 4-4 是九图的各个部分:
图4-4 九图的组成

将一个图片存储为九图的目的是允许开发者可以任意的对图片进行垂直和水平伸缩。当开发者要对图片大小进行调整时,九图中的有些部分保持不变,有些则需要调整。保持不变的部分是边角,边角的尺寸不会改变。而其它部分则会进行大小调整:

  • 上(Top edge)
    图片中的这部分宽度会进行调整,而高度不会。

  • 右(Right edge)
    图片中的这部分高度会进行调整,而宽度不会。

  • 下(Bottom edge)
    跟 Top edge 部分一样,图片中的这部分宽度会进行调整,而高度不会。

  • 左(Left edge)
    跟 Bottom edge 部分一样,图片中的这部分宽度会进行调整,而高度不会。

  • 中心(Center)
    图片中的这部分会进行宽和高的调整。

Inset 中的上下左右值代表你不想要伸缩的区域。例如,如果指定左的值为 10,上的值为 11,有的值为 10,下的值为 5,这会告诉 iOS 在图片距离左边 10 像素地方放置一条垂直线,在距离顶部 11 像素的地方,放置一条水平线。另外一条垂直线在离右边 14 像素的位置,最后一条水平现在距离底部 5 像素的地方。这些线条限定的矩形区域内部是可伸缩的,外部是不可伸缩的。这听起来可能会有点迷惑,不过可以这样想象一下这里有两个矩形区域:一个是图片矩形,另外一个则是在这个图片内绘制的另外一个矩形。内部矩形区域是可伸缩的,而外部矩形区域保持不变。如图 4-5 可以演示一下上面的这些值:

图 4-5 使用 edge insets 定义的可伸缩区域

下面看一个示例,代码如图4-5:

- (void)drawResizableImage
{
    UIImage *img = [[UIImage imageNamed:@"1"] resizableImageWithCapInsets:UIEdgeInsetsMake(5, 10, 8, 5)];
    UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 81, 200, 100)];
    imgView.image = img;
    
    [self addSubview:imgView];
}

被拉伸的图片如下:

图4-5 被拉伸的原图

拉伸后结果如图4-6:

图4-6 被拉伸后

可以很明显看出哪里被拉伸。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,085评论 25 707
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,705评论 1 92
  • 一、使用UIImage和CGImage处理图位 可用通过Quartz的图片对象或原始图片数据来创建UIImage。...
    MD_963阅读 1,566评论 0 3
  • 20160210 团队智慧一定会大于个人智慧吗?我第一次思考这个问题的时候,说实话,我是怀疑的。因为在我的大多数经...
    f7d01673dcd6阅读 5,829评论 0 2
  • 偶遇 一场跨年舞会 美丽精巧的面具 遮不住奔突的灵魂 在光影中 人们用狂欢和过去告别 我站在新旧交替的门口 希望再...
    初见___阅读 563评论 2 3