疯狂iOS讲义疯狂连载之在内存中绘图

简介:

前面介绍的都是通过扩展UIView、重写drawRect:方法进行绘图,这种绘图方式是直接在UIView控件上绘制所有的图形——由于每次该控件显示出来时,drawRect:方法都会被调用,这意味着每次该控件显示出来时,程序都需要重绘所有的图形,很明显,这种方式的性能并不好。除此之外,总有些时候需要在内存中绘制图片,这样既可导出到手机本地,也可上传到网络上。


与直接在UIView控件上绘图不同,在内存中绘图时,需要开发者自己准备绘图环境,Quartz 2D提供了一个非常便捷的函数:UIGraphicsBeginImageContext(CGSize size),该函数用于准备绘图环境。当图形绘制完成后,可调用UIGraphicsEndImageContext()函数结束绘图和关闭绘图环境。


总结来说,在内存中绘图的步骤如下。




调用UIGraphicsBeginImageContext(CGSize size)函数准备绘图环境。


调用UIGraphicsGetCurrentContext()函数获取绘图CGContextRef


用前面介绍的绘制集合图形、使用路径等方式进行绘图。


调用UIGraphicsGetImageFromCurrentImageContext()函数获取当前绘制的图形,该方法返回一个UIImage对象。


调用UIGraphicsEndImageContext()函数结束绘图,并关闭绘图环境。


除了使用前面介绍需要CGContextRef参数的方法绘图之外,还可调用如下函数进行绘图。


UIRectFill(CGRect rect):向当前绘图环境所创建的内存中的图片上填充一个矩形。


UIRectFillUsingBlendMode(CGRect rect , CGBlendMode blendMode):向当前绘图环境所创建的内存中的图片上填充一个矩形,绘制使用指定的混合模式。


UIRectFrame(CGRect rect):向当前绘图环境所创建的内存中的图片上绘制一个矩形边框。


UIRectFrameUsingBlendMode(CGRect rect , CGBlendMode blendMode):向当前绘图环境所创建的内存中的图片上绘制一个矩形边框,绘制使用指定的混合模式。


上面4个方法都是直接绘制在当前绘图环境所创建的内存中的图片上,因此,这些方法都不需要传入CGContextRef作为参数。


下面的程序示范了在内存中绘图,并将图片输出到手机本地。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制


器和配套的Storyboard界面设计文件。本程序无须修改界面设计文件,也无须自定义UIView控件,该程序只是向该UIView上添加一个UIImageView,并控制该UIImageView显示内存中绘制的图片即可。因此,该程序只要修改视图控制器类,该视图控制器类的实现代码如下。


程序清单:codes/12/12.2/DrawImage/DrawImage/FKViewController.m

wKiom1L5iUfzd_3vAANfrjqqbto100.jpg

wKioL1L5iSKSc67yAALB0z9bMtg880.jpg

程序中的第一行粗体字代码用于创建内存中图片的绘制环境,接着调用UIGraphics- GetCurrentContext()方法获取绘图的CGContextRef,剩下的绘图操作与前面介绍的各种绘图代码完全相同。


图形绘制完成后,第二行粗体字代码调用UIGraphicsEndImageContext()方法结束绘图,绘图结束后,即可调用UIGraphicsGetImageFromCurrentImageContext()函数获取当前绘制的图片,程序既可使用UIImageView显示该图片(如程序的viewDidLoad方法所示),也可将图片输出到手机本地。


最后一行粗体字代码调用了UIImagePNGRepresentation()函数获取UIImage图像的数据。该方法返回NSData对象,接下来就可利用NSData的方法执行输出了。





提示:
UIImagePNGRepresentation()函数的作用是获取UIImage图片转换为PNG格式的图片数据,还有一个与之类似的UIImageJPEGRepresentation()函数,其作用是获取UIImage图片转换为JPEG格式的图片数据,UIImageJPEGRepresentation()函数多一个参数,用于指定图片的压缩质量。




094318_FUru_262659.jpg




编译、运行该程序,即可看到如图12.13所示的效果。


该程序还将绘制的图片输出到该应用程序沙盒的Documents文件夹下。对模拟器而言,该Documents文件夹位于OS X系统的一个隐藏文件夹下。为了查看iOS应用程序沙盒的文件夹,请按如下步骤进行。



提示:
为了保证系统安全,iOS应用程序只能在系统为该应用所分配的文件区域下读、写文件,该文件区域被称为该应用程序的沙盒。iOS应用的所有非代码文件都要保存在此,例如,图像、图标、声音、映像、属性列表、文本文件等。iOS的每个应用程序都有自己独立的沙盒,该应用程序不能访问其他应用的沙盒。


094416_eJmK_262659.jpg





1)打开Mac OS X系统的Finder


2)单击Finder应用中主菜单的“前往”→“前往文件夹”菜单项,或单击command+Shift+G快捷键,系统弹出如图12.14所示的对话框。


3)在图12.14所示的对话框中输入/users/登录用户名/library/application suport/iPhone Simulator,然后单击“前往”按钮进入该文件夹,即可看到5.05.16.06.17.0等文件夹,这就是该电脑上已经安装过的iOS模拟器文件夹——不同的文件夹对应不同版本的iOS模拟器。



4)进入当前iOS模拟器版本对应的文件夹,比如当前使用iOS 7.0模拟器,则经过7.0文件夹进入该模拟器,接下来即可看到该模拟器目录下包括ApplicationsMediaRoottmp、资源库等文件夹,其中,Applications文件夹下保存了该模拟器上安装的所有iOS应用。


5)进入Applications文件夹,即可在该文件夹下看到大量的子文件夹,每个子文件夹对应一个应用程序,找到本应用对应的子文件夹,并进入该子文件夹,即可看到包含DocumentsDrawImage.appLibrarytmp内容,在Documents文件夹下即可看到刚刚创建的newPng.png图片,如图12.15所示。



094604_qWqK_262659.jpg


12.15  查看模拟器沙盒下的文件


提示:
读者可能会想,如果可以直接查看OS X系统的隐藏文件,是不是就可以直接通过Finder进入该模拟器沙盒目录?实际上,OS X并没有提供图形化界面来设置显示隐藏文件,不过可以通过命令行进行修改,在OS X系统命令行窗口输入defaults write com.apple.finder AppleShowAllFiles -bool true,然后退出所有的Finder,重启Finder程序,即可看到隐藏文件;如果希望恢复隐藏,在命令行窗口输入defaults write com.apple.finder AppleShowAllFiles -bool false,然后退出所有的Finder,并重启Finder程序即可


anchor.gif实例:绘图板


该实例将会实现一个绘图板,用户可以根据喜好随心所欲地在手机上“涂鸦”,涂鸦完成后,即可得到自己想要的图片——这张图片既可保存到手机本地,也可通过网络分享。


为了实现这个应用,仅仅通过重写UIViewdrawRect:方法并不适合——如果仅通过重写UIViewdrawRect:方法来实现绘图,用户每次绘图的时候就会丢失上一次绘图的内容。这显然不是本实例要实现的效果。


为了保证用户每次绘图的内容不会丢失,将会在内存中创建一张图片,当用户开始绘图时,程序会通过重写drawRect:方法进行实时绘制,当用户想要绘制的图形确定下来后,将该图形绘制到内存中的图片上。


举例来说,当用户想要在屏幕上绘制直线时,程序会把用户开始触碰屏幕的第一个点作为绘图的起始点,当用户手指不离开屏幕而是在屏幕上拖动时,程序会不断地获取拖动点的坐标,并调用该UIViewdrawRect:方法,该方法会从起始点绘制到当前拖动点——由于drawRect:每次重绘都只绘制起始点到当前拖动点的直线,因此,用户可以实时看到拖动绘制的直线。当用户松开手指时(表明用户确定了最终绘制点),在内存中的图片上绘制从起始点到手指松开点的直线即可。


需要指出的是,为了保证用户能看到之前绘制的图形,重写UIViewdrawRect:方法时一定要先把内存中的图片绘制到UIView上。





首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。在Interface Builder中打开界面设计文件,将该界面设计文件中最大的UIView改为使用自定义的FKDrawView类。在界面上方放置一个UISegmentedControl控件,该控件用于控制绘图颜色;在界面下方放置一个工具条,并向工具条中添加一个UISegmentedControl控件,该控件用于控制绘图形状。本应用的界面设计如图12.16所示。



095701_e883_262659.jpg
为了让界面上的两个UISegmentedControl控件能控制用户绘制的颜色和形状,需要在Interface Builder中为这两个UISegmentedControl控件分别绑定changeColor:changeShape:两个IBAction方法。


为了能记录该应用当前需要绘制的图形,本程序先创建一个头文件,该文件中仅定义一个枚举类型,代码如下。


程序清单:codes/12/12.2/HandDraw/HandDraw/Constant.h

wKioL1L5iVbTKTDSAADAf3b2p2k525.jpg

本应用的视图控制器类比较简单,主要就是实现changeColor:changeShape:两个IBAction方法。下面是该视图控制器类的实现代码。
程序清单:codes/12/12.2/HandDraw/HandDraw/FKViewController.m

wKioL1L5iXSQySS8AAMG25-wKZg393.jpg

从上面程序中的两行粗体字代码可以看出,该视图控制器类的view并不是UIView,而是自定义的FKDrawView,这个FKDrawView就是实现本应用的关键,FKDrawView不仅要重写drawRect:方法,该方法还完成两件事情:将内存中的图片绘制出来;用户手指拖动进行“实时”绘制。


FKDrawView类的接口代码比较简单,只是定义currentColorshape两个属性即可,这两个属性用于接收控制器传入的绘制颜色和绘制形状。FKDrawView类的实现代码如下。


程序清单:codes/12/12.2/HandDraw/HandDraw/FKDrawView.

wKioL1L5iaqwI-qmAAUF41-W1To278.jpgwKiom1L5id3C5Dh9AAPppKRjeK4758.jpgwKioL1L5icmQF21LAAWaJ2ZNAPY999.jpg

wKiom1L5ib2Qlc7AAABTn-nw9cY470.jpg

该程序的关键是上面的粗体字draw:方法,该方法会根据想要绘制的图形类型,绘制不同的形状。需要读者留意的是,该方法在两个地方被调用过——当用户拖动手指时,激发touchesMoved: withEvent:方法,该方法中会通知该控件重绘自己,该控件会调用drawRect:方法进行重绘,drawRect:方法中调用了这个粗体字draw:方法执行实时绘制。除此之外,当用户手指结束触碰时,激发touchesEnded: withEvent:方法,该方法中调用了[self draw:buffCtx];代码,这行代码将会把起始点到结束触碰点的形状绘制在内存中的图片上。


程序中还有一个稍微有点复杂的地方,就是所谓的“自由绘制”。即当用户手指在屏幕上拖动时,程序需要绘制手指拖动的轨迹,这条轨迹表面上看是一条不规则的曲线,但实际上它由很多很短的线段组成——当用户手指在屏幕上拖动时,程序会不断地从上一个触碰点绘制到当前触碰点,由于这些线段都非常短,因此,用户会以为我们绘制了一条光滑的曲线。程序为了完成这个绘制过程,使用prevPoint保存上一个触碰点的坐标,并不断地绘制从prevPointlastPoint的线段,每次绘制完成后,使用prevPoint保存当前触碰点的坐标,这对下一次绘制而言,当前触碰点就变成了上一个触碰点。


编译、运行该程序,用户即可在屏幕上绘制任意的形状,绘制效果如图12.17所示。
095925_eDtJ_262659.jpg


——————本文节选自《疯狂ios讲义(上)》
095954_7cDA_262659.jpg 



前面介绍的都是通过扩展UIView、重写drawRect:方法进行绘图,这种绘图方式是直接在UIView控件上绘制所有的图形——由于每次该控件显示出来时,drawRect:方法都会被调用,这意味着每次该控件显示出来时,程序都需要重绘所有的图形,很明显,这种方式的性能并不好。除此之外,总有些时候需要在内存中绘制图片,这样既可导出到手机本地,也可上传到网络上。


与直接在UIView控件上绘图不同,在内存中绘图时,需要开发者自己准备绘图环境,Quartz 2D提供了一个非常便捷的函数:UIGraphicsBeginImageContext(CGSize size),该函数用于准备绘图环境。当图形绘制完成后,可调用UIGraphicsEndImageContext()函数结束绘图和关闭绘图环境。


总结来说,在内存中绘图的步骤如下。




调用UIGraphicsBeginImageContext(CGSize size)函数准备绘图环境。


调用UIGraphicsGetCurrentContext()函数获取绘图CGContextRef


用前面介绍的绘制集合图形、使用路径等方式进行绘图。


调用UIGraphicsGetImageFromCurrentImageContext()函数获取当前绘制的图形,该方法返回一个UIImage对象。


调用UIGraphicsEndImageContext()函数结束绘图,并关闭绘图环境。


除了使用前面介绍需要CGContextRef参数的方法绘图之外,还可调用如下函数进行绘图。


UIRectFill(CGRect rect):向当前绘图环境所创建的内存中的图片上填充一个矩形。


UIRectFillUsingBlendMode(CGRect rect , CGBlendMode blendMode):向当前绘图环境所创建的内存中的图片上填充一个矩形,绘制使用指定的混合模式。


UIRectFrame(CGRect rect):向当前绘图环境所创建的内存中的图片上绘制一个矩形边框。


UIRectFrameUsingBlendMode(CGRect rect , CGBlendMode blendMode):向当前绘图环境所创建的内存中的图片上绘制一个矩形边框,绘制使用指定的混合模式。


上面4个方法都是直接绘制在当前绘图环境所创建的内存中的图片上,因此,这些方法都不需要传入CGContextRef作为参数。


下面的程序示范了在内存中绘图,并将图片输出到手机本地。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制


器和配套的Storyboard界面设计文件。本程序无须修改界面设计文件,也无须自定义UIView控件,该程序只是向该UIView上添加一个UIImageView,并控制该UIImageView显示内存中绘制的图片即可。因此,该程序只要修改视图控制器类,该视图控制器类的实现代码如下。


程序清单:codes/12/12.2/DrawImage/DrawImage/FKViewController.m

wKiom1L5iUfzd_3vAANfrjqqbto100.jpg

wKioL1L5iSKSc67yAALB0z9bMtg880.jpg

程序中的第一行粗体字代码用于创建内存中图片的绘制环境,接着调用UIGraphics- GetCurrentContext()方法获取绘图的CGContextRef,剩下的绘图操作与前面介绍的各种绘图代码完全相同。


图形绘制完成后,第二行粗体字代码调用UIGraphicsEndImageContext()方法结束绘图,绘图结束后,即可调用UIGraphicsGetImageFromCurrentImageContext()函数获取当前绘制的图片,程序既可使用UIImageView显示该图片(如程序的viewDidLoad方法所示),也可将图片输出到手机本地。


最后一行粗体字代码调用了UIImagePNGRepresentation()函数获取UIImage图像的数据。该方法返回NSData对象,接下来就可利用NSData的方法执行输出了。





提示:
UIImagePNGRepresentation()函数的作用是获取UIImage图片转换为PNG格式的图片数据,还有一个与之类似的UIImageJPEGRepresentation()函数,其作用是获取UIImage图片转换为JPEG格式的图片数据,UIImageJPEGRepresentation()函数多一个参数,用于指定图片的压缩质量。




094318_FUru_262659.jpg




编译、运行该程序,即可看到如图12.13所示的效果。


该程序还将绘制的图片输出到该应用程序沙盒的Documents文件夹下。对模拟器而言,该Documents文件夹位于OS X系统的一个隐藏文件夹下。为了查看iOS应用程序沙盒的文件夹,请按如下步骤进行。



提示:
为了保证系统安全,iOS应用程序只能在系统为该应用所分配的文件区域下读、写文件,该文件区域被称为该应用程序的沙盒。iOS应用的所有非代码文件都要保存在此,例如,图像、图标、声音、映像、属性列表、文本文件等。iOS的每个应用程序都有自己独立的沙盒,该应用程序不能访问其他应用的沙盒。


094416_eJmK_262659.jpg





1)打开Mac OS X系统的Finder


2)单击Finder应用中主菜单的“前往”→“前往文件夹”菜单项,或单击command+Shift+G快捷键,系统弹出如图12.14所示的对话框。


3)在图12.14所示的对话框中输入/users/登录用户名/library/application suport/iPhone Simulator,然后单击“前往”按钮进入该文件夹,即可看到5.05.16.06.17.0等文件夹,这就是该电脑上已经安装过的iOS模拟器文件夹——不同的文件夹对应不同版本的iOS模拟器。



4)进入当前iOS模拟器版本对应的文件夹,比如当前使用iOS 7.0模拟器,则经过7.0文件夹进入该模拟器,接下来即可看到该模拟器目录下包括ApplicationsMediaRoottmp、资源库等文件夹,其中,Applications文件夹下保存了该模拟器上安装的所有iOS应用。


5)进入Applications文件夹,即可在该文件夹下看到大量的子文件夹,每个子文件夹对应一个应用程序,找到本应用对应的子文件夹,并进入该子文件夹,即可看到包含DocumentsDrawImage.appLibrarytmp内容,在Documents文件夹下即可看到刚刚创建的newPng.png图片,如图12.15所示。



094604_qWqK_262659.jpg


12.15  查看模拟器沙盒下的文件


提示:
读者可能会想,如果可以直接查看OS X系统的隐藏文件,是不是就可以直接通过Finder进入该模拟器沙盒目录?实际上,OS X并没有提供图形化界面来设置显示隐藏文件,不过可以通过命令行进行修改,在OS X系统命令行窗口输入defaults write com.apple.finder AppleShowAllFiles -bool true,然后退出所有的Finder,重启Finder程序,即可看到隐藏文件;如果希望恢复隐藏,在命令行窗口输入defaults write com.apple.finder AppleShowAllFiles -bool false,然后退出所有的Finder,并重启Finder程序即可


anchor.gif实例:绘图板


该实例将会实现一个绘图板,用户可以根据喜好随心所欲地在手机上“涂鸦”,涂鸦完成后,即可得到自己想要的图片——这张图片既可保存到手机本地,也可通过网络分享。


为了实现这个应用,仅仅通过重写UIViewdrawRect:方法并不适合——如果仅通过重写UIViewdrawRect:方法来实现绘图,用户每次绘图的时候就会丢失上一次绘图的内容。这显然不是本实例要实现的效果。


为了保证用户每次绘图的内容不会丢失,将会在内存中创建一张图片,当用户开始绘图时,程序会通过重写drawRect:方法进行实时绘制,当用户想要绘制的图形确定下来后,将该图形绘制到内存中的图片上。


举例来说,当用户想要在屏幕上绘制直线时,程序会把用户开始触碰屏幕的第一个点作为绘图的起始点,当用户手指不离开屏幕而是在屏幕上拖动时,程序会不断地获取拖动点的坐标,并调用该UIViewdrawRect:方法,该方法会从起始点绘制到当前拖动点——由于drawRect:每次重绘都只绘制起始点到当前拖动点的直线,因此,用户可以实时看到拖动绘制的直线。当用户松开手指时(表明用户确定了最终绘制点),在内存中的图片上绘制从起始点到手指松开点的直线即可。


需要指出的是,为了保证用户能看到之前绘制的图形,重写UIViewdrawRect:方法时一定要先把内存中的图片绘制到UIView上。





首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。在Interface Builder中打开界面设计文件,将该界面设计文件中最大的UIView改为使用自定义的FKDrawView类。在界面上方放置一个UISegmentedControl控件,该控件用于控制绘图颜色;在界面下方放置一个工具条,并向工具条中添加一个UISegmentedControl控件,该控件用于控制绘图形状。本应用的界面设计如图12.16所示。



095701_e883_262659.jpg
为了让界面上的两个UISegmentedControl控件能控制用户绘制的颜色和形状,需要在Interface Builder中为这两个UISegmentedControl控件分别绑定changeColor:changeShape:两个IBAction方法。


为了能记录该应用当前需要绘制的图形,本程序先创建一个头文件,该文件中仅定义一个枚举类型,代码如下。


程序清单:codes/12/12.2/HandDraw/HandDraw/Constant.h

wKioL1L5iVbTKTDSAADAf3b2p2k525.jpg

本应用的视图控制器类比较简单,主要就是实现changeColor:changeShape:两个IBAction方法。下面是该视图控制器类的实现代码。
程序清单:codes/12/12.2/HandDraw/HandDraw/FKViewController.m

wKioL1L5iXSQySS8AAMG25-wKZg393.jpg

从上面程序中的两行粗体字代码可以看出,该视图控制器类的view并不是UIView,而是自定义的FKDrawView,这个FKDrawView就是实现本应用的关键,FKDrawView不仅要重写drawRect:方法,该方法还完成两件事情:将内存中的图片绘制出来;用户手指拖动进行“实时”绘制。


FKDrawView类的接口代码比较简单,只是定义currentColorshape两个属性即可,这两个属性用于接收控制器传入的绘制颜色和绘制形状。FKDrawView类的实现代码如下。


程序清单:codes/12/12.2/HandDraw/HandDraw/FKDrawView.

wKioL1L5iaqwI-qmAAUF41-W1To278.jpgwKiom1L5id3C5Dh9AAPppKRjeK4758.jpgwKioL1L5icmQF21LAAWaJ2ZNAPY999.jpg

wKiom1L5ib2Qlc7AAABTn-nw9cY470.jpg

该程序的关键是上面的粗体字draw:方法,该方法会根据想要绘制的图形类型,绘制不同的形状。需要读者留意的是,该方法在两个地方被调用过——当用户拖动手指时,激发touchesMoved: withEvent:方法,该方法中会通知该控件重绘自己,该控件会调用drawRect:方法进行重绘,drawRect:方法中调用了这个粗体字draw:方法执行实时绘制。除此之外,当用户手指结束触碰时,激发touchesEnded: withEvent:方法,该方法中调用了[self draw:buffCtx];代码,这行代码将会把起始点到结束触碰点的形状绘制在内存中的图片上。


程序中还有一个稍微有点复杂的地方,就是所谓的“自由绘制”。即当用户手指在屏幕上拖动时,程序需要绘制手指拖动的轨迹,这条轨迹表面上看是一条不规则的曲线,但实际上它由很多很短的线段组成——当用户手指在屏幕上拖动时,程序会不断地从上一个触碰点绘制到当前触碰点,由于这些线段都非常短,因此,用户会以为我们绘制了一条光滑的曲线。程序为了完成这个绘制过程,使用prevPoint保存上一个触碰点的坐标,并不断地绘制从prevPointlastPoint的线段,每次绘制完成后,使用prevPoint保存当前触碰点的坐标,这对下一次绘制而言,当前触碰点就变成了上一个触碰点。


编译、运行该程序,用户即可在屏幕上绘制任意的形状,绘制效果如图12.17所示。
095925_eDtJ_262659.jpg


——————本文节选自《疯狂ios讲义(上)》
095954_7cDA_262659.jpg 

前面介绍的都是通过扩展UIView、重写drawRect:方法进行绘图,这种绘图方式是直接在UIView控件上绘制所有的图形——由于每次该控件显示出来时,drawRect:方法都会被调用,这意味着每次该控件显示出来时,程序都需要重绘所有的图形,很明显,这种方式的性能并不好。除此之外,总有些时候需要在内存中绘制图片,这样既可导出到手机本地,也可上传到网络上。


与直接在UIView控件上绘图不同,在内存中绘图时,需要开发者自己准备绘图环境,Quartz 2D提供了一个非常便捷的函数:UIGraphicsBeginImageContext(CGSize size),该函数用于准备绘图环境。当图形绘制完成后,可调用UIGraphicsEndImageContext()函数结束绘图和关闭绘图环境。


总结来说,在内存中绘图的步骤如下。




调用UIGraphicsBeginImageContext(CGSize size)函数准备绘图环境。


调用UIGraphicsGetCurrentContext()函数获取绘图CGContextRef


用前面介绍的绘制集合图形、使用路径等方式进行绘图。


调用UIGraphicsGetImageFromCurrentImageContext()函数获取当前绘制的图形,该方法返回一个UIImage对象。


调用UIGraphicsEndImageContext()函数结束绘图,并关闭绘图环境。


除了使用前面介绍需要CGContextRef参数的方法绘图之外,还可调用如下函数进行绘图。


UIRectFill(CGRect rect):向当前绘图环境所创建的内存中的图片上填充一个矩形。


UIRectFillUsingBlendMode(CGRect rect , CGBlendMode blendMode):向当前绘图环境所创建的内存中的图片上填充一个矩形,绘制使用指定的混合模式。


UIRectFrame(CGRect rect):向当前绘图环境所创建的内存中的图片上绘制一个矩形边框。


UIRectFrameUsingBlendMode(CGRect rect , CGBlendMode blendMode):向当前绘图环境所创建的内存中的图片上绘制一个矩形边框,绘制使用指定的混合模式。


上面4个方法都是直接绘制在当前绘图环境所创建的内存中的图片上,因此,这些方法都不需要传入CGContextRef作为参数。


下面的程序示范了在内存中绘图,并将图片输出到手机本地。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制


器和配套的Storyboard界面设计文件。本程序无须修改界面设计文件,也无须自定义UIView控件,该程序只是向该UIView上添加一个UIImageView,并控制该UIImageView显示内存中绘制的图片即可。因此,该程序只要修改视图控制器类,该视图控制器类的实现代码如下。


程序清单:codes/12/12.2/DrawImage/DrawImage/FKViewController.m

wKiom1L5iUfzd_3vAANfrjqqbto100.jpg

wKioL1L5iSKSc67yAALB0z9bMtg880.jpg

程序中的第一行粗体字代码用于创建内存中图片的绘制环境,接着调用UIGraphics- GetCurrentContext()方法获取绘图的CGContextRef,剩下的绘图操作与前面介绍的各种绘图代码完全相同。


图形绘制完成后,第二行粗体字代码调用UIGraphicsEndImageContext()方法结束绘图,绘图结束后,即可调用UIGraphicsGetImageFromCurrentImageContext()函数获取当前绘制的图片,程序既可使用UIImageView显示该图片(如程序的viewDidLoad方法所示),也可将图片输出到手机本地。


最后一行粗体字代码调用了UIImagePNGRepresentation()函数获取UIImage图像的数据。该方法返回NSData对象,接下来就可利用NSData的方法执行输出了。





提示:
UIImagePNGRepresentation()函数的作用是获取UIImage图片转换为PNG格式的图片数据,还有一个与之类似的UIImageJPEGRepresentation()函数,其作用是获取UIImage图片转换为JPEG格式的图片数据,UIImageJPEGRepresentation()函数多一个参数,用于指定图片的压缩质量。




094318_FUru_262659.jpg




编译、运行该程序,即可看到如图12.13所示的效果。


该程序还将绘制的图片输出到该应用程序沙盒的Documents文件夹下。对模拟器而言,该Documents文件夹位于OS X系统的一个隐藏文件夹下。为了查看iOS应用程序沙盒的文件夹,请按如下步骤进行。



提示:
为了保证系统安全,iOS应用程序只能在系统为该应用所分配的文件区域下读、写文件,该文件区域被称为该应用程序的沙盒。iOS应用的所有非代码文件都要保存在此,例如,图像、图标、声音、映像、属性列表、文本文件等。iOS的每个应用程序都有自己独立的沙盒,该应用程序不能访问其他应用的沙盒。


094416_eJmK_262659.jpg





1)打开Mac OS X系统的Finder


2)单击Finder应用中主菜单的“前往”→“前往文件夹”菜单项,或单击command+Shift+G快捷键,系统弹出如图12.14所示的对话框。


3)在图12.14所示的对话框中输入/users/登录用户名/library/application suport/iPhone Simulator,然后单击“前往”按钮进入该文件夹,即可看到5.05.16.06.17.0等文件夹,这就是该电脑上已经安装过的iOS模拟器文件夹——不同的文件夹对应不同版本的iOS模拟器。



4)进入当前iOS模拟器版本对应的文件夹,比如当前使用iOS 7.0模拟器,则经过7.0文件夹进入该模拟器,接下来即可看到该模拟器目录下包括ApplicationsMediaRoottmp、资源库等文件夹,其中,Applications文件夹下保存了该模拟器上安装的所有iOS应用。


5)进入Applications文件夹,即可在该文件夹下看到大量的子文件夹,每个子文件夹对应一个应用程序,找到本应用对应的子文件夹,并进入该子文件夹,即可看到包含DocumentsDrawImage.appLibrarytmp内容,在Documents文件夹下即可看到刚刚创建的newPng.png图片,如图12.15所示。



094604_qWqK_262659.jpg


12.15  查看模拟器沙盒下的文件


提示:
读者可能会想,如果可以直接查看OS X系统的隐藏文件,是不是就可以直接通过Finder进入该模拟器沙盒目录?实际上,OS X并没有提供图形化界面来设置显示隐藏文件,不过可以通过命令行进行修改,在OS X系统命令行窗口输入defaults write com.apple.finder AppleShowAllFiles -bool true,然后退出所有的Finder,重启Finder程序,即可看到隐藏文件;如果希望恢复隐藏,在命令行窗口输入defaults write com.apple.finder AppleShowAllFiles -bool false,然后退出所有的Finder,并重启Finder程序即可


anchor.gif实例:绘图板


该实例将会实现一个绘图板,用户可以根据喜好随心所欲地在手机上“涂鸦”,涂鸦完成后,即可得到自己想要的图片——这张图片既可保存到手机本地,也可通过网络分享。


为了实现这个应用,仅仅通过重写UIViewdrawRect:方法并不适合——如果仅通过重写UIViewdrawRect:方法来实现绘图,用户每次绘图的时候就会丢失上一次绘图的内容。这显然不是本实例要实现的效果。


为了保证用户每次绘图的内容不会丢失,将会在内存中创建一张图片,当用户开始绘图时,程序会通过重写drawRect:方法进行实时绘制,当用户想要绘制的图形确定下来后,将该图形绘制到内存中的图片上。


举例来说,当用户想要在屏幕上绘制直线时,程序会把用户开始触碰屏幕的第一个点作为绘图的起始点,当用户手指不离开屏幕而是在屏幕上拖动时,程序会不断地获取拖动点的坐标,并调用该UIViewdrawRect:方法,该方法会从起始点绘制到当前拖动点——由于drawRect:每次重绘都只绘制起始点到当前拖动点的直线,因此,用户可以实时看到拖动绘制的直线。当用户松开手指时(表明用户确定了最终绘制点),在内存中的图片上绘制从起始点到手指松开点的直线即可。


需要指出的是,为了保证用户能看到之前绘制的图形,重写UIViewdrawRect:方法时一定要先把内存中的图片绘制到UIView上。





首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。在Interface Builder中打开界面设计文件,将该界面设计文件中最大的UIView改为使用自定义的FKDrawView类。在界面上方放置一个UISegmentedControl控件,该控件用于控制绘图颜色;在界面下方放置一个工具条,并向工具条中添加一个UISegmentedControl控件,该控件用于控制绘图形状。本应用的界面设计如图12.16所示。



095701_e883_262659.jpg
为了让界面上的两个UISegmentedControl控件能控制用户绘制的颜色和形状,需要在Interface Builder中为这两个UISegmentedControl控件分别绑定changeColor:changeShape:两个IBAction方法。


为了能记录该应用当前需要绘制的图形,本程序先创建一个头文件,该文件中仅定义一个枚举类型,代码如下。


程序清单:codes/12/12.2/HandDraw/HandDraw/Constant.h

wKioL1L5iVbTKTDSAADAf3b2p2k525.jpg

本应用的视图控制器类比较简单,主要就是实现changeColor:changeShape:两个IBAction方法。下面是该视图控制器类的实现代码。
程序清单:codes/12/12.2/HandDraw/HandDraw/FKViewController.m

wKioL1L5iXSQySS8AAMG25-wKZg393.jpg

从上面程序中的两行粗体字代码可以看出,该视图控制器类的view并不是UIView,而是自定义的FKDrawView,这个FKDrawView就是实现本应用的关键,FKDrawView不仅要重写drawRect:方法,该方法还完成两件事情:将内存中的图片绘制出来;用户手指拖动进行“实时”绘制。


FKDrawView类的接口代码比较简单,只是定义currentColorshape两个属性即可,这两个属性用于接收控制器传入的绘制颜色和绘制形状。FKDrawView类的实现代码如下。


程序清单:codes/12/12.2/HandDraw/HandDraw/FKDrawView.

wKioL1L5iaqwI-qmAAUF41-W1To278.jpgwKiom1L5id3C5Dh9AAPppKRjeK4758.jpgwKioL1L5icmQF21LAAWaJ2ZNAPY999.jpg

wKiom1L5ib2Qlc7AAABTn-nw9cY470.jpg

该程序的关键是上面的粗体字draw:方法,该方法会根据想要绘制的图形类型,绘制不同的形状。需要读者留意的是,该方法在两个地方被调用过——当用户拖动手指时,激发touchesMoved: withEvent:方法,该方法中会通知该控件重绘自己,该控件会调用drawRect:方法进行重绘,drawRect:方法中调用了这个粗体字draw:方法执行实时绘制。除此之外,当用户手指结束触碰时,激发touchesEnded: withEvent:方法,该方法中调用了[self draw:buffCtx];代码,这行代码将会把起始点到结束触碰点的形状绘制在内存中的图片上。


程序中还有一个稍微有点复杂的地方,就是所谓的“自由绘制”。即当用户手指在屏幕上拖动时,程序需要绘制手指拖动的轨迹,这条轨迹表面上看是一条不规则的曲线,但实际上它由很多很短的线段组成——当用户手指在屏幕上拖动时,程序会不断地从上一个触碰点绘制到当前触碰点,由于这些线段都非常短,因此,用户会以为我们绘制了一条光滑的曲线。程序为了完成这个绘制过程,使用prevPoint保存上一个触碰点的坐标,并不断地绘制从prevPointlastPoint的线段,每次绘制完成后,使用prevPoint保存当前触碰点的坐标,这对下一次绘制而言,当前触碰点就变成了上一个触碰点。


编译、运行该程序,用户即可在屏幕上绘制任意的形状,绘制效果如图12.17所示。
095925_eDtJ_262659.jpg


——————本文节选自《疯狂ios讲义(上)》
095954_7cDA_262659.jpg 


 

本文转自  

fkJava李刚 51CTO博客,原文链接:http://blog.51cto.com/javaligang/1357924 ,如需转载请自行联系原作者

相关文章
|
3月前
|
Swift iOS开发
iOS开发-属性的内存管理
【8月更文挑战第12天】在iOS开发中,属性的内存管理至关重要,直接影响应用性能与稳定性。主要策略包括:`strong`(强引用),不维持对象生命期,可用于解除循环引用;`assign`(赋值),适用于基本数据类型及非指针对象属性;`copy`,复制对象而非引用,确保对象不变性。iOS采用引用计数管理内存,ARC(自动引用计数)自动处理引用增减,简化开发。为避免循环引用,可利用弱引用或Swift中的`[weak self]`。最佳实践包括:选择恰当的内存管理策略、减少不必要的强引用、及时释放不再使用的对象、注意block内存管理,并使用Xcode工具进行内存分析。
|
iOS开发 异构计算
如何增加 iOS APP 虚拟地址空间及内存上限?XNU 内核源码解读
1. 引言 最近一段时间在做钉钉 iOS 内存专项治理,解决内存不足时的 jetsam 事件及 malloc 的异常崩溃。在进程创建时系统会为每个 app 设定内存最大使用上限,内核会维护一个内存阈值优先级列表,当设备内存不足时低优先级的 app 会首先被内核中止进程。在阅读 XNU 内核源码过程中我们发现提供系统了两种能力可以扩展 App 的虚拟地址空间(com.apple.developer.kernel.extended-virtual-addressing)和增加内存使用上限(com.apple.developer.kernel.increased-memory-limit)。
2109 0
如何增加 iOS APP 虚拟地址空间及内存上限?XNU 内核源码解读
|
存储 算法 iOS开发
|
存储 算法 Java
iOS开发 - 穿针引线之内存管理(二)
iOS开发 - 穿针引线之内存管理
226 0
|
存储 程序员 C语言
iOS开发 - 穿针引线之内存管理(一)
iOS开发 - 穿针引线之内存管理
188 0
|
存储 监控 Unix
iOS-底层原理36:内存优化(一) 野指针探测
iOS-底层原理36:内存优化(一) 野指针探测
639 0
iOS-底层原理36:内存优化(一) 野指针探测
|
存储 安全 API
iOS-底层原理 33:内存管理(三)AutoReleasePool & NSRunLoop 底层分析
iOS-底层原理 33:内存管理(三)AutoReleasePool & NSRunLoop 底层分析
214 0
iOS-底层原理 33:内存管理(三)AutoReleasePool & NSRunLoop 底层分析
|
iOS开发
iOS-底层原理 33:内存管理(二)强引用分析
iOS-底层原理 33:内存管理(二)强引用分析
197 0
iOS-底层原理 33:内存管理(二)强引用分析
|
存储 编译器 Serverless
iOS-底层原理 33:内存管理(一)TaggedPointer/retain/release/dealloc/retainCount 底层分析
iOS-底层原理 33:内存管理(一)TaggedPointer/retain/release/dealloc/retainCount 底层分析
256 0
iOS-底层原理 33:内存管理(一)TaggedPointer/retain/release/dealloc/retainCount 底层分析
|
存储 安全 程序员
iOS-底层原理 24:内存五大区
iOS-底层原理 24:内存五大区
155 0
iOS-底层原理 24:内存五大区