分类目录归档:iOS

iOS开发入门和提高::::::演道网IOS专栏提供一线IOS研发人员在工作学习过程中的经验总结。让大家少走弯路。

autoresizingMask的使用

autoresizingMask的使用

作者QQ:415074476

QQ群:191280586

用来处理当父视图发生变化时,自身的frame怎么跟随变化。常用于适配,和对有无键盘的处理。
主要考虑六个值,上下左右和宽高。
分两个方向。弄清楚了一个方向,另一个方向也就明白了。
这里以Y轴方向举例,有上,高,下 三个值。对应于代码是UIViewAutoresizingFlexibleTopMargin, 上间距可变
UIViewAutoresizingFlexibleHeight 高度可变
UIViewAutoresizingFlexibleBottomMargin 下间距可变 .

下面看图的示例。
初始值:
父视图高200,宽200
子视图为50 50; 100 100.
当我把父视图的高度从200变成400时,子视图会怎么变化呢?
一下一步步看上间距,高度,下间距是怎么影响的。一共有8种可能性。
第一种: 三个值全不设,就是上间距可变,高度不可变,下间距可变。

先来个猜测: 首先高度不可变,那么还是100, 余下就间距为400-100=300.
由于上间距和下间距都可变,我们有理由相信他们是按比例缩放的。
原来是50:50 = 1:1, 那么现在应该还是1:1 = 150:150. 验证下:

猜想成立!!!
第二种:选择上间距为不可变。高度不可变,下间距可变。

再次猜想:上间距不可变,还是50,高度不可变100,余下400-50-100 = 250。
耶,只剩下可变的下间距,必然为250啊。。一个可爱的数字。验证下:

再次验证成立。

第三种:
上间距不选为可变,选择高度可变,下间距可变。

猜想总结:不可变的保留原值。 可变的按比例缩放。
全都可变,比值为50:100:50 = 1:2:1 = 100:200:100 对否?

好吧。又一次验证正确。
可否下结论了呢?
不可变的保留原值。 可变的按比例缩放。

第四种:
上间距不可变。高度可变,下间距可变。

根据上面的猜想:
不可变的50去掉。剩下350。
高度:下间距= 100:50 = 116.6: 233.3
哇,出小数了。。

呵呵。。被四舍五入了。

还继续么?我想用不着了。
再来个吧:
上间距可变, 高度可变,下间距不可变。

根据猜想应该是:
可变的上间距:可变的高度 = 50:100 = 116.6: 233.3

好吧。 纠正一个小错误。不是四舍五入。而是舍弃小数。
这么一来下间距还是不可变的50么?

和猜想冲突了?
好吧, 这里有个事实:
nib文件里的frame只能用整数,而实际上cocoa是支持小数的。
用代码测试下第五种情况。
UIView *parentView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
parentView.backgroundColor = [UIColor blackColor];
UIView *childView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
childView.backgroundColor = [UIColor redColor];
childView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin| UIViewAutoresizingFlexibleHeight;
[self.view addSubview:parentView];
[parentView addSubview:childView];
[parentView release];
[childView release];
[parentView setFrame:CGRectMake(50, 50, 200, 400)];
NSLog(@”%@”, NSStringFromCGRect(childView.frame));
很遗憾还是这个值。
{{50, 116}, {100, 233}}
一定是舍掉小数么?

UIView *parentView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
parentView.backgroundColor = [UIColor blackColor];
UIView *childView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100.5)];
childView.backgroundColor = [UIColor redColor];
childView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin| UIViewAutoresizingFlexibleHeight;
[self.view addSubview:parentView];
[parentView addSubview:childView];
[parentView release];
[childView release];
[parentView setFrame:CGRectMake(50, 50, 200, 400)];
NSLog(@”%@”, NSStringFromCGRect(childView.frame));
结果是:
{{50, 116}, {100, 234}}
猜想结果是:
{{50, 116.27}, {100, 233.7.3}}
这次又四舍五入了。

汗,没源码可整不明白了。
只能顶一个结论:不可变的保留原值。 可变的按比例缩放。
至于算出来有小数会不会四舍五入。
目前看来高度会四舍五入,上间距都是舍弃。
有兴趣的同学可以对这个小数问题再研究啊~~

MP3文件的ID3V1信息与ID3V2信息结构的分析

一:“ID3v1”信息的分析

MP3的基本歌曲信息存在了MP3文件的最后128个字节里,其结构是:
Public Structure ID3v1Info

Dim ID3v1TAG As String ‘TAG三个字母,ID3V1的标识

Dim Title As String ‘存储标题信息,30个字节

Dim Artist As String ‘存储歌手信息,30个字节

Dim Album As String ‘存储专辑信息,30个字节

Dim Year As String ‘存储年代信息,4个字节

Dim Comments As String ‘存储备注信息,28个字节(有时为30字节)

Dim Genre As String ‘存储音乐风格信息,保留位,1个字节 

Dim Reserved As String ‘保留位,1个字节(有时没有意思

Dim Track As String ‘音轨(曲号)保留位,1个字节(有时没有)

End Structure

ID3V1信息存储结构如下(如图1):

图 1 一个MP3文件的ID3v1信息

1-3 TAG

4-33 歌曲名(Take Me To Your Heart )

34-63 歌手名(Michael Learns to Rock)

64-93 专辑名(Take Me to Your Heart)

94-97 年(2004)

98-125 备注 (http://www.uptu.com)

126 保留位,这时为0,则说明有音轨,下一位就是音轨

127 保留位,为音轨(第几首歌)(OC)

128 保留位 (风格)(66)

 

而在Winamp的ID3v1歌曲信息里(如图1),我们看到的是他都包括:
Title(歌曲名)
Artist(歌手名)
Album(专辑名)
Year(年)
Comment(备注)
Genre(歌曲风格)注,见下面有详细的列表
Track#(歌曲在专辑里的顺序,就是我们经常说的“第几首”)

  Title,Artist,Album,Year,Comment我们都是可以在那个128个字节里得到,Genre和Track哪里去了呢?有的朋友都重视了那128字节信息的前125个信息了,而这两个信息是却放在了最后的126-128字节里。其实,127那处就是Track信息,而 128处就是Genre信息。他们的存储方式都不是字符,我们提取他们的时候需要注意,他们都是数字。比如,就如我们看到的这首歌的126处是0x0D,那么很显然,他就是13。也就是第13号歌曲风格,Pop流行( 下面列表)。

这时,你也该猜到了,127和128都是有意义的,自然126处也是有其意义!ID3v1信息的Comment(注释)一共占用28个字节。这个说法并不是完全的正确。准确的说应该是正确了一部分。有的时候注释也可以超过这个数字的。ID3v1要求注释最多可以到30个字节。那么有的读者会问“MP3的 ID3v1就是得有130个字节的信息了嘛?”不是,当然不是。ID3v1是固定的128个字节,这个你不用担心。其实ID3v1是这样安排的:如果 MP3的注释是大于28个字节的,那么就要借用126-127两个字节。所以ID3v1的注释部分可能是28个字节也可能是30个字节。那么,怎么区分到底是28个字节还是30个字节呢?很简单,126处就是管这个的,我们只要看看126处是不是0x00,如果是0x00那么注释就有28个字节。如果不等于0x00,那么就是说注释是30个字节。同时别忘了,由于第127字节存储了Track信息,那么如果注释是30个字节的时候,这首歌的ID3v1里的那个127处的信息自然就不是Track信息了。Track自然就是没有地方存了,所以127处变的没有Track意义了,它只是Comment的一部分了。在你决定制作读取ID3v1的程序的时候,请特别注意一下。

我们最终知道了126处是ID3v1信息的注释部分到底是28个字节还是30个字节的标志位。127处是音轨信息(Track),而Reserved3则是歌曲风格(Genre)。现在我们重新再写一次结构

原理2:MP3文件有没有ID3v1信息的错误理解
  那就是到底什么能叫做“MP3文件没有ID3v1信息”。检测的方法是先提取指定的MP3文件的最后128字节信息,然后确定这128个字节的前3个字节是“TAG”。很多朋友都会同意这个方法没有问题的。可是,实际上问题并不是那么简单。

  Winamp或者其他的MP3播放相关的软件都有MP3信息的写入和读取的功能,然而这些写入ID3v1的软件都会不自觉的当你一打开这个MP3文件就会给它加上这128个字节的信息。也就是说当我们用这种软件打开MP3文件的时候,这些软件就会自动的在这个MP3文件尾端添加了一个128字节的 ID3v1结构,而且还是以“TAG”开头!(如图3)。那么很显然,光靠检测那“TAG”三个字节的信息,还是不能完全确定MP3到底有没有ID3v1 信息的。我们还要确定这“TAG”后的125字节是不是正确的信息。一般情况下,这类软件产生的ID3v1结构都是由一堆00,或者一堆空格组成的,所以我们要判断一下是不是ID3v1的信息是一堆00或者一堆空格。如果是,那么MP3文件虽然有这“TAG”三个字母,却仍然不是一个合法的ID3v1信息。MP3文件仍然应该认为没有ID3v1信息。我觉得这个东西有必要特别提醒大家注意。

二:ID3v2信息的提取
MP3文件的“ID3v1信息”。这个信息结构提取起来非常容易,写入到文件也不是什么难事。但是它的信息安排和可扩展性却非常之差(只能128个字节)。就如你所知,MP3文件还有另外的一个信息结构,这个结构具有更好的可扩展性,而且存储的容量也不受限制(也就是总长度不固定)。这个信息就是 ID3v2信息(相对ID3v1而言)。由于ID3v1信息存储在了文件的最后128个字节里,那么ID3v2就不得不放弃选择存储在文件的末尾了,于是它被存储在了文件的起始位置。

ID3v2信息的存储和读取远远要比ID3v1信息复杂的多。这是因为ID3v2信息不再固定,而且由于这段信息存储在了文件的首端,所以重新写入的时候也远比ID3v1麻烦的多。

我用尽可能清楚而且简练的话,给大家讲一下ID3v2信息的读取方法。ID3v2到现在一共有4个版本,不过比较流行的MP3播放软件一般只支持第3版,即ID3v2.3。我们要读取的就是ID3v2.3信息。ID3v2信息包括两个部分,一个部分是标头信息,另一个部分是标体信息。其中标头信息占固定的十个字节,

 每个ID3V2的标签部一个标签头和若干个标签帧或一个扩展标签组成关于曲目的信息如标题、作者等都放在在不同的标签帧中,扩展标签头和标签帧关不是必要的,但每个标签至少要有一个标签头和标签帧一直顺序存放在MP3文件首部。

它的结构如下:

(一)、标签头

Private Structure ID3v2Header

  Dim Header() As Byte’ID3v2标识位,应该是“ID3”三个字母为对

  Dim Ver As Byte’版本号ID3V2就记录3

  Dim Revision As Byte’副版本号此版本记录为0

  Dim Flag As Byte‘存放标志的字节,这个版本只定义了三位,稍后详细解说

  Dim Size() As Byte’标签大小,不包括标签头的10个字节(但是有的文章说包括)我是通过核实 才这样说的,看看源代码就知道了

End Structure

这十个字节的信息作用:

1、Header(2),一般为“ID3”,否则没有ID3V2信息

2、Flag 标志字节:标志字节一般为0,字义为abc00000

a-表示是否使用Unsynchronisation

b-表示是否有扩展头部,一般没有(WINAMP也没有)所以一般不设置

 c-表示是否为测试标签(99.9%的标签都不是测试用的,所以一般不设置)

3、Sixe(3) 标签大小:一共四个字节,但每个字节只使用7位,最高位不使用恒为0,所以格式如下:

  0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx

 计算大小时要将0去掉,得到一个28位的二进制数,就是标签的大,计算公式如下

   ①、VC的:ID3size =(Size[0]&0x7F)*0x200000 +(Size[1]&0x7F)*0x400 +(Size[2]&0x7F)*0x80 +(Size[3]&0x7F);

②、VB的:ID3size =Size(0) * (2 ^ 21) + Size(1) * (2 ^ 14) + Size(2) * (2 ^ 7) + Size(3) * (2 ^ 0)

VB的我已在类中声明了一个函数ByteToLong,很方便,直调用就OK了。

通过解析这段标头信息我们可以知道一个MP3文件是不是有ID3v2信息,如果有我们就知道了ID3v2的数据体的总长度。

(二)、标签帧

  接下来我们要解析ID3v2的 标签帧,别担心,虽然复杂,但也没你想象的那么的痛苦。ID3v2的数据体又分为很多相同的数据结构。

  每个标签帧都有一个10个字节的帧头和至少一个字节的不固定长度的内容组成,它们也是顺序存放在文件中,和标签头和其他的标签帧也没有特殊的字符分隔,得到一个完整的帧的内容只有从帧头中的到内容大小 后才能读出,读取时要注意大小,不要将其它的帧的内容或帧头读入。帧的定义如下:

Private Structure ID3v2Frame

  Dim FrameID As String‘用4个字符标识一个帧,说明其内容,常用的标识对照表见附表

  Dim Size() As Byte’4个字节 帧内容的大小,不包括帧头,不得小于1,计算时也用上面的公式计算

  Dim Flags() As Byte’2个字节 存放标志,只定义了6位,稍后详细解说

End Structure 

 1、FrameID 帧标:用四个字符标识一个帧的内容含义,常用的对照如下:

TEXT: 歌词作者 TENC: 编码
WXXX: URL链接(URL) TCOP: 版权(Copyright)
TOPE: 原艺术家 TCOM: 作曲家
TDAT: 日期 TPE3: 指挥者
TPE2: 乐队 TPE1: 艺术家相当于ID3v1的Artist
TPE4: 翻译(记录员、修改员) TYER: 年代相当于ID3v1的Year
USLT: 歌词 TALB: 专辑相当于ID3v1的Album
TIT1: 内容组描述 TIT2: 标题相当于ID3v1的Title
TIT3: 副标题 TCON: 流派(风格)相当于ID3v1的Genre见下表
TBPM: 每分钟节拍数 COMM: 注释相当于ID3v1的Comment
TDLY: 播放列表返录 TRCK: 音轨(曲号)相当于ID3v1的Track
TFLT: 文件类型 TIME: 时间 
TKEY: 最初关键字 TLAN: 语言
TLEN: 长度 TMED: 媒体类型
TOAL: 原唱片集 TOFN: 原文件名
TOLY: 原歌词作者 TORY: 最初发行年份
TOWM: 文件所有者(许可证者) TPOS: 作品集部分
TPUB: 发行人 TRDA: 录制日期
TRSN: Intenet电台名称 TRSO: Intenet电台所有者
TSIZ: 大小   TSRC: ISRC(国际的标准记录代码)
TSSE: 编码使用的软件(硬件设置) UFID: 唯一的文件标识符
AENC: 音频加密技术    

   其中要说明的是这个FrameID,在ID3v1里我们是根据每一个信息所占用的固定的字节数和位置来判断他是哪个信息的。而ID3v2为了提供更好的可扩展性,把这些信息变得“动态”化了,因为长度并不是预先设定好的,而是在size[4]里存储的。这样长度就可以不再固定了。我觉得在我们自己定义文件的时候ID3v2和ID3v1也是值得我们考虑的一个方面。如果结构很小而且存储的量也不大,我们可以采用ID3v1的信息存储方式。如果存储的信息不固定,而且要求有很好的可扩展性,那么ID3v2当然成了首选。实际上,现在很多格式的文件的存储方式都是ID3v2的存储方式非常接近的。

2、Size() 帧内容大小:不再是总标头那样的每个字节只取后7位了,它是按照正常的8位存储的。得到帧内容的大小的格式如下 :

xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx

 计算成整形,公式如下:

   ①、VC的: FSize = Size[0]*0x100000000 + Size[1]*0x10000 + Size[2]*0x100 + Size[3];

②、VB的:ID3size =Size(0) * (2 ^ 21) + Size(1) * (2 ^ 14) + Size(2) * (2 ^ 7) + Size(3) * (2 ^ 0)

VB的我已在类中声明了一个函数ByteToLong,很方便,直调用就OK了。

3、Flags() 标志:只定义了6位,另外10位为0 但大部分的情况下16位都为0就可以了,格式如下:

 a-标签保护标志,设置时认为此帧作废

b-文件保护标志,设置时认为此帧作废

c-只读标志,设置时认为此帧不能修改(目前好像没有看到过)

i-压缩标志,设置时一个字节存放两个BCD码表示数字

j-加密标志(好像不太实用)

k-组标志,设置时说明此帧和其它的某帧是一组。

  详细你可能到www.ID3.org去了解一下。

4、帧内容(数据体)

  标头后面就是数据体了,我们提取数据体的前十个字节,我们知道了这个数据结构存储的FrameID是TIT2,查上面的表,说明这个数据结构存储的是歌曲名信息。大小是00 00 00 17,换成十进制就是23。也就是歌曲名是这个子标头后的23个字节的信息。也就是:“Take Me To Your Heart ”。接下来的一个数据结构的FrameID是TPE1,说明是歌手名,而大小是00 00 00 17,说明这个数据体有23个字节,也就是:“Michael Learns to Rock”。依次类推。这里需要大家知道的是一个汉字占用两个字节。在写入时,要计算字节数,我已编写了一个函数ByteSize,大家可以直接使用了。

  还有特别要提醒大家的是,ID3v2的注释信息(FrameID是COMM)的数据体的前四(但经我测试为前5个字节)个字节,并不是注释内容,而是注释使用的自然语言,这个例子里我们看到是:”eng\0”,我们要跳过这四个字节的信息进行解析。此外ID3v2的歌曲类型Genre(FrameID是 TCON)的存储也不太一样的。由于很多MP3播放器的写入方式并不是非常一致,而在Genre写入的也不一致。比如,这首歌的ID3v2的Genre是 Classic Rock,其实有的还会写入成:(1),或者1,还有(1)Classic Rock,所以格式五花八门,我们要在解析的时候注意一下。还有,值得一提的是winamp在保存和读取帧内容的时候会在内容前面加个’\0’,并把这个字节计算在帧内容的大小中。所以前面提到的歌手名“Michael Learns to Rock”本身应该22个字节,可是却占了23个字节。

ios阶段性小结

从php彻底转到ios已经半年了..小结下.
应该说ios的入门相对php来说确实难多了.
php学习一个星期可以写出一个简单的留言本了.

而在没有人教的情况下.ios估计还在纠结在mainwindow.xib里的delegate,viewcontroller之间的关系.
最多就是抄了一个helloworld出来.

另一个区别就是php的代码架构相对来说是好改的.
而ios一个小小的界面改动可能都会导致大量的代码修改.

入了门之后呢?
php这个时候为了做更好的web页面,得学下css,js了..css还有builder写好..但js一般也是phper兼职做的, 不排除有些公司有专门的js.
网页好看了, 用户来了, 网站性能提到日程上来了. 这个时候要学的就是mysql性能优化, 增加缓存(几乎所有的优化都会围绕缓存来做)了.
打开网页依然过慢.. 这时又要把时间放在网页端来做了. 页面分块,用js异步加载. 加cdn分流.

ok,客户端也优化好了.是不是万事大吉了呢? 非也.还有个用户体验的问题. 譬如用户上传个5m的图片,服务器要生成相应的几个图片,然后要分发到各图片服务器去.
不能让服务器把事情都做了,再告诉用户处理结果吧.这叫需要队列处理了..先告诉用户处理成功了,服务器再慢慢地处理去. 看, 有了队列,你又要学一个东东.更不用说日志分析啥的, 又得学习shell了. 

可见php入门容易, 真要做出好成绩要学的东西真是多了去了..杂啊..真是杂..

再来看ios吧..其实ios入门难点.就是因为是E文的. 国人都不喜欢看E文啊,看了就头疼,看得也慢吧.但是最好的入门教程依然首推官方.
E文就E文吧, 比网上找helloworld强多了..这是我血的教训啊.
ios相对php最好的优点就是专.啥也不用关心,就写ios,更直白一点,学会查api就行了..
都封装的相当好了.直接用好这个工具吧.从这里看,要学好ios实在比php容易太多了.要学的东西少啊.
当然在用到wap的场合, 也有可能要学点js的.不过场景极少.我现在开发的应用都还没有遇到这个需求,让我的js用无武之地啊.
工作中就是各种控制的使用啊. 各种api调用啊. 除了因为api的农历处理有问题,我自己翻译了一个算法外,好像其它的东西都是api的调用.
最多就是自己再封装一层,方便自己使用及代码维护.

总结起来:做php开发,要求掌握更多的技术. ios开发,要求更多的是产品细节.

如果时光可以倒退, 我不见得会转过来做ios开发.
可惜这世上没有如果, 那么就努力脱离api调用的层次. 多研究下ios哲学吧. 也许还有一个值得我玩玩的世界等着我.

ios编译静态库

参考文章(写得很好):
http://blog.csdn.net/steve1018/article/details/6902973

修改一处地方. 合并.a文件的命令

 lipo -create Debug-iphoneos/liblibhello.a Debug-iphonesimulator/liblibhello.a -output liblibhello.a                                                       
 

lipo命令是创建通用文件
可以使用man lipo查看具体使用方法

arm6, arm7, i386 都是指CPU体系架构
只是arm6, arm7适应嵌入式设备
i386适应pc设备

说明:
生成.a文件后,为了给别人使用 .要把头文件和图片资源文件提出来一起给别人.
工程中不能使用xib文件. 用代码写界面吧.

ios 公历转农历

作者QQ:415074476

QQ群:191280586

由于NSChineseCalendar有些日期显示不正确. 如2012年08月的日期会比正常的农历晚一天.
所以我自己写了个简单的根据NSDate转换农历的算法. 是根据其它的查表法, 翻译过来的. 验证好用.
需要的同学可以直接使用.

LunarCalendar.h

//
// LunarCalendar.h
// Clock
// NSChineseCalendar有bug, 有些日子不准..所以需要自己写
// Created by shunping liu on 12-7-13.
// Copyright (c) 2012年 tencent. All rights reserved.
//

#import

@interface LunarCalendar : NSObject
{
@private
NSInteger lcMonth;
NSInteger lcYear;
NSInteger lcDay;

NSArray *chineseMonths;
NSArray *chineseDays;
BOOL isLeap;
}

@property (nonatomic, assign) NSInteger lcYear;
@property (nonatomic, assign) NSInteger lcMonth;
@property (nonatomic, assign) NSInteger lcDay;

@property (nonatomic, retain) NSArray *chineseMonths;
@property (nonatomic, retain) NSArray *chineseDays;

- (NSString*)toChinese;
- (id)initWithDate:(NSDate*)date;
+ (LunarCalendar*)initWithDate:(NSDate*)date;
@end

NSString *getChineseCalendarWithDate(NSDate *date);

LunarCalendar.m

//
// LunarCalendar.m
// Clock
//
// Created by shunping liu on 12-7-13.
// Copyright (c) 2012年 tencent. All rights reserved.
//

#import "LunarCalendar.h"

@interface NSDate(custom)
//以年月日初始化一个NSDate对象
+(NSDate*)dateFromYear:(int)year Month:(int)month Day:(int)day;
@end

@implementation NSDate(custom)
//以年月日初始化一个NSDate对象
+(NSDate*)dateFromYear:(int)year Month:(int)month Day:(int)day
{
NSString *date = [NSString stringWithFormat:@"%4d-%02d-%02d", year, month, day];
NSDateFormatter *formater = [[NSDateFormatter alloc] init];
[formater setDateFormat:@"yyyy-MM-dd"];
NSDate *returnDate = [formater dateFromString:date];
[formater release];
return returnDate;
}
@end

@interface LunarCalendar(private)
//返回农历year年的总天数
- (NSInteger)daysOfYear:(NSInteger)year;

//返回农历year年闰月的天数
- (NSInteger)leapDays:(NSInteger)year;

//返回闰月
- (NSInteger)leapMonthOfYear:(NSInteger)year;

//获取某年某一月的天数
- (NSInteger)daysOfMonth:(NSInteger)month Year:(NSInteger)year;

- (void)initCusor;

@end

@implementation LunarCalendar
@synthesize lcYear;
@synthesize lcMonth;
@synthesize lcDay;
@synthesize chineseDays = _chineseDays;
@synthesize chineseMonths = _chineseMonths;

//公历转农历需要用查表法, 没有万能公式

int lunarInfo[] = {0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260
,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,
0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,
0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,
0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,
0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,
0x06ca0,0x0b550,0x15355,0x04da0,0x0a5d0,0x14573,0x052d0,0x0a9a8,0x0e950,0x06aa0,
0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,
0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b5a0,0x195a6,
0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,
0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,
0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,
0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,
0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,
0x05aa0,0x076a3,0x096d0,0x04bd7,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,
0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0};

- (id)init
{
if (self = [super init]) {
_chineseMonths=[[NSArray arrayWithObjects:
@"正月", @"二月", @"三月", @"四月", @"五月",
@"六月", @"七月", @"八月",
@"九月", @"十月", @"冬月", @"腊月", nil] retain];

_chineseDays=[[NSArray arrayWithObjects:
@"初一", @"初二", @"初三", @"初四", @"初五",
@"初六", @"初七", @"初八", @"初九", @"初十",
@"十一", @"十二", @"十三", @"十四", @"十五",
@"十六", @"十七", @"十八", @"十九", @"二十",
@"廿一", @"廿二", @"廿三", @"廿四", @"廿五",
@"廿六", @"廿七", @"廿八", @"廿九", @"三十", nil] retain];
}
return self;
}

//计算date对应的农历
- (id)initWithDate:(NSDate*)date
{
if(self = [super init])
{
[self init];
//用户可能会调系统时间.必须支持到1970年,1969冬月23
NSDate *baseDate = [NSDate dateFromYear:1969 Month:12 Day:31];

NSTimeInterval totalTimes = [date timeIntervalSinceDate:baseDate];
NSInteger offset = totalTimes/86400.0;

NSInteger i, leapMonth=0, temp=0;

//补上农历到1970年的差异
offset -= 37;//36天后到农历1970腊月廿九, 后面要补1, 因为是从1号开始

for(i=1970; i0; i++) {
temp = [self daysOfYear:i];
offset -= temp;
}

if(offsetoffset += temp;
i--;
}
self.lcYear = i;

leapMonth = [self leapMonthOfYear:i]; //闰哪个月
isLeap = NO;
for(i=1; i0; i++)
{
//闰月, 有闰月时, 一年有13月
if(leapMonth>0 && i==(leapMonth+1) && isLeap==NO)
{
--i;
isLeap = YES;
temp = [self leapDays:self.lcYear];
}
else
{
temp = [self daysOfMonth:i Year:self.lcYear];
}

//解除闰月
if(isLeap && i==(leapMonth+1))
{
isLeap = NO;
}
offset -= temp;

}

if(offset==0 && leapMonth>0 && i==leapMonth+1)
if(isLeap)
{
isLeap = NO;
}
else
{
isLeap = YES;
--i;
}

if(offset0x8; i>>=1)
{
sum += (lunarInfo[someYear-1900] & i) ? 1: 0;
}
return sum+[self leapDays:someYear];
}

//返回农历year年闰月的天数
- (NSInteger)leapDays:(NSInteger)someYear
{
if([self leapMonthOfYear: someYear])
{
return((lunarInfo[someYear-1900] & 0x10000)? 30: 29);
}
else
{
return(0);

}
}

//返回闰月
- (NSInteger)leapMonthOfYear:(NSInteger)someYear
{
return lunarInfo[someYear-1900] & 0xf;
}

//获取某年某一月的天数
- (NSInteger)daysOfMonth:(NSInteger)aMonth Year:(NSInteger)aYear
{
return( (lunarInfo[aYear-1900] & (0x10000>>aMonth))? 30: 29 );
}

- (NSString*)chineseMonth:(int)aMonth
{
return [self.chineseMonths objectAtIndex:aMonth-1];
}

- (NSString*)chineseDay:(int)aDay
{
return [self.chineseDays objectAtIndex:aDay-1];
}

- (void)dealloc
{
[_chineseDays release];
[_chineseMonths release];
[super dealloc];
}

+ (LunarCalendar*)initWithDate:(NSDate*)date
{
LunarCalendar *lunarCalendar = [[[LunarCalendar alloc] initWithDate:date] autorelease];
return lunarCalendar;
}
@end

NSString *getChineseCalendarWithDate(NSDate *date)
{
/*
NSArray *chineseYears = [NSArray arrayWithObjects:
@"甲子", @"乙丑", @"丙寅", @"丁卯", @"戊辰",
@"己巳", @"庚午", @"辛未", @"壬申", @"癸酉",
@"甲戌", @"乙亥", @"丙子", @"丁丑", @"戊寅",
@"己卯", @"庚辰", @"辛己", @"壬午", @"癸未",
@"甲申", @"乙酉", @"丙戌", @"丁亥", @"戊子",
@"己丑", @"庚寅", @"辛卯", @"壬辰", @"癸巳",
@"甲午", @"乙未", @"丙申", @"丁酉", @"戊戌",
@"己亥", @"庚子", @"辛丑", @"壬寅", @"癸丑",
@"甲辰", @"乙巳", @"丙午", @"丁未", @"戊申",
@"己酉", @"庚戌", @"辛亥", @"壬子", @"癸丑",
@"甲寅", @"乙卯", @"丙辰", @"丁巳", @"戊午",
@"己未", @"庚申", @"辛酉", @"壬戌", @"癸亥", nil];
*/

LunarCalendar *lunar = [LunarCalendar initWithDate:date];

NSString *m_str = [lunar chineseMonth:lunar.lcMonth];
NSString *d_str = [lunar chineseDay:lunar.lcDay];

NSString *chineseCal_str =[NSString stringWithFormat: @"农历%@%@",m_str,d_str];
return chineseCal_str;
}

使用示例
NSString *nonglistr = getChineseCalendarWithDate([NSdate date]);
NSLog(@”%@”, nonglistr);

放到了git上:

https://github.com/bjmayor/layout

iPad开发

iPad开发.
这里主要是列出和iPhone开发的一些区别.
1.iPad的状态栏转屏之后,高度不会变.而且状态栏只能是黑色.
2.独属于iPad的类.
splitViewController
UIPopoverController

相对于iPhone来说, iPad应用基本都要求适应四个方向.
对于view来说, 只有两个情况
1.直接或间接加入到window上.
2.直接或间接加入到rootviewcontroller上.
为什么要这样区分呢?
因为系统会自动给rootviewcontroller的view做转屏.
当然前提是你的shouldAutorotateToInterfaceOrientation返回为YES.
这种情况,系统已经给你做转屏了..你要做的事只有一个, 重新设置大小和位置.也就是frame.

那对于没有通过rootviewcontroller而添加到window上的view呢..
没有办法, 自己去监听UIDeviceOrientationDidChangeNotification 事件吧.
监听到事件之后, 有两件事你得做:
1. 根据[[[UIApplication sharedApplication].statusBarOrientation] 对view做transform.

– (CGAffineTransform)transformForOrientation
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (orientation == UIInterfaceOrientationLandscapeLeft)
{
return CGAffineTransformMakeRotation(M_PI*1.5);
}
else if (orientation == UIInterfaceOrientationLandscapeRight)
{
return CGAffineTransformMakeRotation(M_PI/2);
}
else if (orientation == UIInterfaceOrientationPortraitUpsideDown)
{
return CGAffineTransformMakeRotation(-M_PI);
}
else
{
return CGAffineTransformIdentity;
}
}
2.当然是设置frame了.

拍照:
这里要涉及到UIPopoverController了.
因为从图片库选择照片时, 只能把UIImagePickerController放到UIPopoverController里.
这里要注意的是- (void)presentPopoverFromRect:(CGRect)rect inView:(UIView *)view permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated;
这里面 rect怎么填..其实很简单, 就是后面参数view的bounds即可.

浅析Objective-C字面量

编写Objective-C程序时,总会用到某几个类,它们属于Foundation框架。虽然从技术上来说,不用Foundation框架也能写出Objective-C代码,但实际上却经常要用到此框架。这几个类是NSString、NSNumber、NSArray、NSDictionary。从类名上即可看出各自所表示的数据结构。

Objective-C以语法繁杂而著称。事实上的确是这样。不过,从Objective-C 1.0起,有一种非常简单的方式能创建NSString对象。这就是“字符串字面量”(string literal),其语法如下:

NSString *someString = @”Effective Objective-C 2.0”;

如果不用这种语法的话,就要以常见的alloc及init方法来分配并初始化NSString对象了。在版本较新的编译器中,也能用这种字面量语法来声明NSNumber、NSArray、NSDictionary类的实例。使用字面量语法(literal syntax)可以缩减源代码长度,使其更为易读。

字面数值

有时需要把整数、浮点数、布尔值封入Objective-C对象中。这种情况下可以用NSNumber类,该类可处理多种类型的数值。若是不用字面量,那么就需要按下述方式创建实例:

NSNumber *someNumber = [NSNumber numberWithInt:1];

上面这行代码创建了一个数字,将其值设为整数1。然而使用字面量能令代码更为整洁:

NSNumber *someNumber = @1;

大家可以看到,字面量语法更为精简。不过它还有很多好处。能够以NSNumber实例表示的所有数据类型都可使用该语法。例如:

NSNumber intNumber = @1;

NSNumber floatNumber = @2.5f;

NSNumber doubleNumber = @3.14159;

NSNumber boolNumber = @YES;

NSNumber *charNumber = @’a’;

字面量语法也适用于下述表达式:

以字面量来表示数值十分有用。这样做可以令NSNumber对象变得整洁,因为声明中只包含数值,而没有多余的语法成分。

字面量数组

数组是常用的数据结构。如果不使用字面量语法,那么就要这样来创建数组:

而使用字面量语法来创建则是:

上面这种做法不仅简单,而且还利于操作数组。数组的常见操作就是取某个下标所对应的对象,这用字面量来做更为容易。如果不用字面量,那么通常会用“objectAtIndex:”方法:

若使用字面量,则是:

这也叫做“取下标”操作(subscripting),与使用字面量语法的其他情况一样,这种方式也更为简洁、更易理解,而且与其他语言中依下标来访问数组元素时所用的语法类似。

不过,用字面量语法创建数组时要注意,若数组元素对象中有nil,则会抛出异常,因为字面量语法实际上只是一种“语法糖”(syntactic sugar),其效果等于是先创建了一个数组,然后把方括号内的所有对象都加到这个数组中。抛出的异常会是这样:

*** Terminating app due to uncaught exception
‘NSInvalidArgumentException’, reason: ‘***
-[__NSPlaceholderArray initWithObjects:count:]: attempt to
insert nil object from objects[0]’

在改用字面量语法来创建数组时就会遇到这个问题。下面这段代码分别以两种语法创建数组:

id object1 = /* … */;
id object2 = /* … */;
id object3 = /* … */;

NSArray *arrayA = [NSArray array WithObjects:
                      object1, object2, object3, nil];
NSArray *arrayB = @[object1, object2, object3];

大家想想:如果object1与object3都指向了有效的Objective-C对象,而object2是nil,那么会出现什么情况呢?按字面量语法创建数组arrayB时会抛出异常。arrayA虽然能创建出来,但是其中却只含有object1一个对象。原因在于,“arrayWithObjects:”方法会依次处理各个参数,直到发现nil为止,由于object2是nil,所以该方法会提前结束。

这个微妙的差别表明,使用字面量语法更为安全。抛出异常令应用程序终止执行,这比创建好数组之后才发现元素个数少了要好。向数组中插入nil通常说明程序有错,而通过异常可以更快地发现这个错误。

字面量字典

“字典”(Dictionary)是一种映射型数据结构,可向其中添加键值对。与数组一样,Objective-C代码也经常用到字典。其创建方式如下:

这样写令人困惑,因为其顺序是,,,。这与通常理解的顺序相反,我们一般认为是把“键”映射到“对象”。因此,这种写法不容易读懂。如果改用字面量语法,就清晰多了:

上面这种写法更简明,而且键出现在对象之前,理解起来较顺畅。此范例代码还说明了使用字面量数值的好处。字典中的对象和键必须都是Objective-C对象,所以不能把整数28直接放进去,而要将其封装在NSNumber实例中才行。使用字面量语法很容易就能做到这一点,只需给数字前加一个@字符即可。

与数组一样,用字面量语法创建字典时也有个问题,那就是一旦有值为nil,便会抛出异常。不过基于同样的原因,这也是个好事。假如在创建字典时不小心用了空值对象,那么“dictionaryWithObjectsAndKeys:”方法就会在首个nil之前停下,并抛出异常,这有助于查错。

字典也可以像数组那样用字面量语法访问。按照特定键访问其值的传统做法是:

与之等效的字面量语法则是:

这样写也省去了冗赘的语法,令此行代码简单易读。

可变数组与字典

通过取下标操作,可以访问数组中某个下标或字典中某个键所对应的元素。如果数组与字典对象是可变的(mutable),那么也能通过下标修改其中的元素值。修改可变数组与字典内容的标准做法是:

若换用取下标操作来写,则是:

局限性

字面量语法有个小小的限制,就是除了字符串以外,所创建出来的对象必须属于Foundation框架才行。如果自定义了这些类的子类,则无法用字面量语法创建其对象。要想创建自定义子类的实例,必须采用“非字面量语法”(nonliteral syntax)。然而,由于NSArray、NSDictionary、NSNumber都是业已定型的“子族”(class cluster,参见第9条),因此很少有人会从其中自定义子类,真要那样做也比较麻烦。而且一般来说,标准的实现已经很好了,无须再改动。创建字符串时可以使用自定义的子类,然而必须要修改编译器的选项才行。除非你明白这样做的后果,否则不鼓励使用此选项,用NSString就足够了。

使用字面量语法创建出来的字符串、数组、字典对象都是不可变的(immutable)。若想要可变版本的对象,则需复制一份:

这么做会多调用一个方法,而且还要再创建一个对象,不过使用字面量语法所带来的好处还是多于上述缺点的。

要点

应该使用字面量语法来创建字符串、数值、数组、字典。与创建此类对象的常规方法相比,这么做更加简明扼要。

应该通过取下标操作来访问数组下标或字典中的键所对应的元素。

用字面量语法创建数组或字典时,若值中有nil,则会抛出异常。因此,务必确保值里不含nil。

Objective-C中@property的所有属性详解 http://www.linuxidc.com/Linux/2014-03/97744.htm

Objective-C 和 Core Foundation 对象相互转换的内存管理总结 http://www.linuxidc.com/Linux/2014-03/97626.htm

使用 Objective-C 一年后我对它的看法 http://www.linuxidc.com/Linux/2013-12/94309.htm

10个Objective-C基础面试题,iOS面试必备 http://www.linuxidc.com/Linux/2013-07/87393.htm

Objective-C适用C数学函数 http://www.linuxidc.com/Linux/2013-06/86215.htm

好学的 Objective-C 高清PDF http://www.linuxidc.com/Linux/2014-09/106226.htm

查询iPhone设备id方法

1.准备

macbook/xp/win7, iphone, 数据线

 

2.用数据线连接macbook 和iphone.

进入iTunes.

 

3.在左侧选择设备,如我的iPhone设备名为maynard.

4.见到右侧有个序列号.单击序列号会切换成UDID,即为要求的设备id

 

如我的设备ID号为: 113f2611bf3d0530b89311c20621bf4be560483f
如需要 加入设备ID及时体验最新版本请点击

Objective-C中Block语法、Block使用以及通过Block实现数组排序

Block:语法块,本质上是匿名函数(没有名称的函数)

标准C里面没有Block,C语言的后期扩展版本,加入了匿名函数

在C++、JS、Swift等语言有类似语法,叫做闭包

Block语法和C语言里的函数指针很相似,下面我们先来回顾下C语言中的函数和函数指针

C语言中函数是指:实现某一功能的代码段

完整的函数包括两部分:函数声明、函数定义

函数声明,即函数原型。例如:int sum (int x,int y);具有两个整形参数,一个整形返回值的函数

函数定义,即函数实现。例如:int sum(int x,int y){

return x + y;

}

函数指针(变量):存放函数地址(函数名)的指针变量

int(*p)(int x,int )= sum;

函数指针类型:int(*)(int x,int y)即:指向两个整形参数,一个整形返回值函数的指针

函数指针变量:p

函数指针的值:sum

Block 匿名函数:没有名称的函数

例如:int (int x,int y)

因为Block是匿名函数,block变量存放的函数的实现,通过block变量能直接调用函数

Block类型:int (^)(int)

Block变量:myBlock

Block值:^int (int num) {return 7*num;}

即:^返回值类型(参数列表){函数体}其中返回值类型可以省略

例如:写一个 返回值为整形 参数为OC字符串(仅一个参数)的block,实现将字符串转换为整形的功能

 int (^myBlock)(NSString *) = ^(NSString *string) {

        return [string intValue];

    };
    NSLog(@”%d”,myBlock(@”123″));

block的数据类型代表了匿名函数的格式(返回值类型,形参的类型)

block变量的定义与函数指针变量的定义类似,唯一区别于函数指针变量的是变量名前通过脱字符(^)修饰

首先应该用^修饰,剩余的部分与C语言函数定义一致,最大的不同就是没有函数名(同时返回值类型也可以省略)

block变量在定义时具有变量定义的基本特征,赋值号右侧的匿名函数可以当做一个整体被赋值,类似于 int a = 5;

block变量所赋的值是匿名函数。又兼具函数的特征,并且是唯一可以定义在某个函数实现内部(C语言中认为函数是不能嵌套定义的,block是个特例)

Block进行typedef

typedef int (^BlockType)(int x,int y)

原类型:int(^)(int x,int y)

新类型:BlockType

类比函数指针的类型定义,格式与函数指针一致,类型定义一定程度上简化了block的使用。

    typedef int (^SumType)(int,int);
    SumType sumBlock = ^(int x,int y) {
        return x + y;
    };
    int result = sumBlock(5,4);
    NSLog(@”%d”,result);

 

 __block类型标识可以运行局部变量在其后续定义的block内部正常访问,

    __block int num = 0;

    void (^testBlock)() = ^() {
        for (int i = 0; i < 10; i++) {
            count++;
            num++;
            NSLog(@”%d”,count);
        }

    };

block调用

    testBlock(); 

 

数组使用Block排序

数组使用Block对两个字符串进行比较

    NSComparisonResult (^compareBlock)(NSString *,NSString *) = ^(NSString *str1,NSString *str2) {

        return [str2 compare:str1];

//如果是升序返回-1,如果是降序返回1,如果相等返回0

    };

    NSLog(@”%ld”,compareBlock(@”11″,@”12″));

    NSArray *array = @[@1,@2,@13,@12,@23];
    NSArray *resultArray1 = [array sortedArrayUsingSelector:@selector(compare:)];
    NSLog(@”%@”,resultArray1);

数组使用Block排序    降序排列

    NSComparator sortBlock = ^(id obj1,id obj2) {

        return [obj2 compare:obj1];
    };
    NSArray *resultArray2 = [array sortedArrayUsingComparator:sortBlock];
    NSLog(@”%@”,resultArray2);

升序序排列

  NSArray *resultArray3 = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {

        return [obj1 compare:obj2];

    }];

    NSLog(@”%@”,resultArray3);

Objective-C中@property的所有属性详解 http://www.linuxidc.com/Linux/2014-03/97744.htm

Objective-C 和 Core Foundation 对象相互转换的内存管理总结 http://www.linuxidc.com/Linux/2014-03/97626.htm

使用 Objective-C 一年后我对它的看法 http://www.linuxidc.com/Linux/2013-12/94309.htm

10个Objective-C基础面试题,iOS面试必备 http://www.linuxidc.com/Linux/2013-07/87393.htm

Objective-C适用C数学函数 http://www.linuxidc.com/Linux/2013-06/86215.htm

好学的 Objective-C 高清PDF http://www.linuxidc.com/Linux/2014-09/106226.htm

ios图像处理

作者QQ:415074476

QQ群:191280586

图像处理

一.获取图片
a.摄像头
b.本地图片
c.网络图片
d.应用程序内截图
e.从头创建图片

二.图片处理
a. 缩放
b.旋转
c.质量

一.获取图片
a.摄像头
使用UIImagePickerController类拍打照或从相册获取图片
使用注意事项:使用之前一定要先做检测
isSourceTypeAvailable

b.本地图片
分应用程序包和沙盒.
应用程序包中的图片是开发阶段放入的.
获取方法 分两种:
第一种最简单:
myImage = [UIImage imageNamed:@”icon.png”];
第二种:
NSString *path =[[NSBundle mainBundle] pathForResource:@”icon ” ofType:@”png”];
myImage = [UIImage imageWithContentsOfFile:path];
第一种使用简单,但是会缓存下来,浪费内存.反复使用的图片建议使用第一种方式,这样效率高..只使用一两次,建议使用第二种,节省内存.

沙盒是程序运行过程中,存储下来的图片.
默认情况下, 沙盒有三个文件夹: Documents, Library, tmp
Documents存放生成的用户数据和图片等
Library 存放用户默认设置和其它状态信息.
tmp存放临时文件.临时文件一定得放在tmp目录下,否则上架审核通不过.

两种方式:
一.通用方式
NSArray  *paths= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [paths lastObject];

二专用方法
return [NSHomeDirectory() stringByAppendingPathComponent:@”Documents”];

获取路径之后,使用UIImage的imageWithContentsOfFile方法生成图片.

c.网络图片
UIImage类可以从NSData实例加载图像.
最简单的办法

myImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:urlstring]]];
对应的方法,把UIImage对象转成NSData对象
UIImagePNGRepresentation

由于上述方法是同步的,会阻塞用户交互,更好的办法是使用
NSURLConnection来异步获取图片数据.

d.应用程序内截图

1.把view转换成图片
其实就是使用CALayer的renderInContext方法.

+(UIImage *)imageFromView: (UIView *) theView
{

    UIGraphicsBeginImageContext(theView.frame.size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    [theView.layer renderInContext:context];//key point

    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicEndImageContext();

    return theImage;

}

e.从头创建图片
使用Quartz 2D创建图形
适用于创建一些简单的标签图形.
几个概念:
1.图形上下文
2.绘图状态,如填充颜色,笔触宽度等
3.绘制路径

二.图片处理
a.缩放

UIGraphicsBeginImageContext(viewsize);
float dwidth = (viewsize.width - size.width) / 2.0f;
float dheight = (viewsize.height - size.height) / 2.0f;
CGRect rect = CGRectMake(dwidth, dheight, size.width, size.height);
[image drawInRect:rect];//key point
UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

b.旋转

仅用于视图显示时可简单地用动画属性
transform.rotation.z
如果要处理图片本身就要使用core graphics了.
UIImage 对象有个属性为imageOrientation.
8个值.上下左右,及在上下左右的基础上加的镜像.
代码如下:

    CGImageRef imgRef = aImage.CGImage;
    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);

    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    CGFloat scaleRatio = 1;
    CGFloat boundHeight;

UIImageOrientation orient = aImage.imageOrientation;
switch(orient)
{
case UIImageOrientationUp: //EXIF = 1
{
transform = CGAffineTransformIdentity;
break;
}
case UIImageOrientationUpMirrored: //EXIF = 2
{
transform = CGAffineTransformMakeTranslation(width, 0.0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
break;
}
case UIImageOrientationDown: //EXIF = 3
{
transform = CGAffineTransformMakeTranslation(width, height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
}
case UIImageOrientationDownMirrored: //EXIF = 4
{
transform = CGAffineTransformMakeTranslation(0.0, height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
break;
}
case UIImageOrientationLeftMirrored: //EXIF = 5
{
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(height, width);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
}
case UIImageOrientationLeft: //EXIF = 6
{
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(0.0, width);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
}
case UIImageOrientationRightMirrored: //EXIF = 7
{
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeScale(-1.0, 1.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
}
case UIImageOrientationRight: //EXIF = 8
{
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(height, 0.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
}
default:
{   DLOG(@"Invalid image orientation");
return aImage;
}
}

UIGraphicsBeginImageContext(bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
CGContextScaleCTM(context, -scaleRatio, scaleRatio);
CGContextTranslateCTM(context, -height, 0);
}
else {
CGContextScaleCTM(context, scaleRatio, -scaleRatio);
CGContextTranslateCTM(context, 0, -height);
}

c.降低质量

UIKIT_EXTERN NSData *UIImageJPEGRepresentation(UIImage *image, CGFloat compressionQuality);  // return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least)