教你做自己的美图秀秀(iOS5新特性:强大的Core Image)

此篇文章转载于cocoaChina论坛,原作者:iimgal

iOS5给我们带来了很多很好很强大的功能和API。Core Image就是其中之一,它使我们很容易就能处理图片的各种效果,色彩啊,曝光啊,饱和度啊,变形啊神马的。
可惜苹果一直没能完善官方文档,也没有推出示例代码,所以国内很多同学可能还没有开始使用。
但国外的大神们已经证明这是个相当强悍的框架,不仅功能强大,而且可以直接使用GPU,效率奇高,甚至可以实时的对视频进行渲染。
下面让我们来看看,如何具体使用它:
首先你需要导入 CoreImage.framework 框架;进行Mac(不是iOS)开发的同学请导入 QuartzCore.framework 框架,包含在其中了。

然后我们先来看看3个主要的类:
CIContext:它与Core Graphics 和 OpenGL context类似,所有Core Image的处理流程都通过它来进行;
CIImage:它用来存放图片数据,可以通过UIImage,图片文件或像素数据创建;
CIFilter:通过它来定义过滤器的详细属性。

CIContext有两种初始化方法,分别对应GPU和CPU

// 创建基于GPU的CIContext对象 context = [CIContext contextWithOptions: nil];     // 创建基于CPU的CIContext对象 //context = [CIContext contextWithOptions: [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:kCIContextUseSoftwareRenderer]];

一般采用第一种基于GPU的,因为效率要比CPU高很多,但是要注意的是基于GPU的CIContext对象无法跨应用访问。
比如你打开UIImagePickerController要选张照片进行美化,如果你直接在UIImagePickerControllerDelegate的委托方法里调用CIContext对象进行处理,那么系统会自动将其降为基于CPU的,速度会变慢,所以正确的方法应该是在委托方法里先把照片保存下来,回到主类里再来处理。(代码里你将会看到)

CIImage的初始化方法有很多,常用的也是2种:

// 通过图片路径创建CIImage NSString *filePath = [[NSBundle mainBundle] pathForResource:@”image” ofType:@”png”]; NSURL *fileNameAndPath = [NSURL fileURLWithPath:filePath]; beginImage = [CIImage imageWithContentsOfURL:fileNameAndPath];  // 通过UIImage对象创建CIImage UIImage *gotImage = …; beginImage = [CIImage imageWithCGImage:gotImage.CGImage];

CIFilter初始化:

// 创建过滤器 filter = [CIFilter filterWithName:@”CISepiaTone”]; [filter setValue:beginImage forKey:kCIInputImageKey]; [filter setValue:[NSNumber numberWithFloat:slideValue] forKey:@”inputIntensity”];

第一行:指定使用哪一个过滤器,通过[CIFilter filterNamesInCategory: kCICategoryBuiltIn]能得到所有过滤器的列表
第二行:指定需要处理的图片
第三行:指定过滤参数,每个过滤器的参数都不一样,可以在官方文档里搜索“Core Image Filter Reference”查看

得到过滤后的图片并输出:

CIImage *outputImage = [filter outputImage]; CGImageRef cgimg = [context createCGImage:outputImage fromRect:[outputImage extent]]; UIImage *newImg = [UIImage imageWithCGImage:cgimg];     [imgV setImage:newImg]; CGImageRelease(cgimg);

第一行:通过[filter outputImage]可以得到过滤器输出的图片
第二行:通过CIContext的方法createCGImage: fromRect:得到CGImage
第三行:转化为UIImage,这样我们就可以跟据需要显示在界面上了

至此一个过滤周期就完成了,简单来说分以下几个步骤:
1 初始化CIContext,CIImage
2 初始化CIFilter并设置参数
3 得到输出的图片
4 将图片转化成能显示的UIImage类型
如果想一张图片有多种过滤效果就需要重复2,3两步,并且要将上一个过滤器输出的图片作为下一个过滤器的参数

简单吧!几行代码就可以得到丰富的效果哦

小技巧:有同学可能想换张图片试试,但使用模拟器的同学打开图片库时里面是空的吧,你在模拟器里打开Safari,在网上找到你想要的图片,长按会弹出问你要不要保存,选保存后再打开图片库里面就有图片了。

源代码下载:CoreImage

UINavigationController学习心得 ——导航控制器

                                UINavigationController学习心得

——导航控制器初探

UINavigationController是用于构建分层应用程序的主要工具,它在管理以及换入和患处多个内容视图方面与UITableBarController较为类似。两者之间的主要不同在于,UINavigationController是作为栈来实现的,这让它非常适合用户与处理分层数据。

     栈的性质

栈时一种常用的数据结构,采用后进先出的原则。不管你是否相信,Pez糖果盒是栈的一个极好的例子。第一步,打开糖果盒包装。第二步,直接双击糖果盒的顶部,打开它。第三部,抓紧糖果栈,在食指和拇指之间牢牢地握住它,然后将糖果栈插入到打开的糖果盒内。第四部,把散落在地上的糖果捡起来,因为说明书这东西,果然是没什么用啊。。。。。。

好吧,之前只是一段很冷很冷的冷笑话,接下来发生的事情会形象的说明栈的概念:当你捡起糖果并一次一个的把它们塞进糖果盒时,你所操作的就是一个栈。我们前面说过栈是后进先出的,也可以说是先进后出。放到糖果盒内的第一个糖果将是最后一个拿出来的,最后一个放入糖果盒内的糖果将是第一个被拿出来的,你应该想象成这样的情况糖果的面积大小和糖果盒地步一样,厚度较小,并且我们的糖果盒只支持平躺罗列这一个一个放入,这样看来我所说的是不是完全正确?好吧,虽然这有点牵强,不过,栈在计算机中就是这么工作的。

向栈中添加的对象操作称为入站(push),即把对象推到栈中

第一个入栈的对象叫做基栈

最后一个入栈的对象叫做栈顶

从栈中删除对象的操作称为出栈(pop)。要让一个对象出栈时,这个对象往往是最后一个入栈的,第一个入站的对象往往最后一个出栈。

导航控制器维护一个视图控制器的栈,任何类型的视图控制器都可以放入栈中。在设计导航控制器时,你需要指定用户看到的第一个视图。该视图时视图层次结构中最底层的视图,其控制器称为根视图控制器(root view controller),或者根控制器。当用户选择查看下一个视图时,栈中将加入一个新的视图控制器,它所控制的视图将展示给用户。我们把这些新的视图控制器称为子控制器

 

导航控制器的使用

首先我们需要打开Xcode并且新建一个IOS的空项目。

然后在AppDeleage.h中添加如下代码:

#import<UIKkit/UIKit.h>

@interface AppDelegate :UIResponder<UIApplicationDelegate>

@property (strong,nonatomic) UIWindow *window;

@property (strong,nonatomic) UINavigationController *navController;

@end

以上代码加粗部分为添加代码,其余为Xcode自动生成代码。

如此简单,我们就已经在我们的引用程序中成功声明了一个Navigation导航控制器,接下来我们去m文件,看看该如让导航控制在我们的程序中发挥作用并且显示在屏幕上

请看如下代码,为了节省章节,我们只将在.m文件中需要添加在implementation内部的代码列出。

//首先我们先要让我们的m文件中的类认识navController属性

@synthesize navController;

 

//或许你可以忽略这段话,它只是预编译命令针对于编译器设置了一

//个书签,对于程序而言,它没有任何的实际意义,没错,就像注释

#pragma mark –

#pragma mark Application lifecycle

 

-(BOOL)application: (UIApplication *) application

didFinishLaunchingWithOptions: (NSDictionary *)launchOptions

{

self.window =[ [UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

//我们来声明并定义一个列表视图,按照样式进行初始化

//你可以把他当做固定套路,因为我们今天不讲解表视图

UITableViewController *tableView = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];

//接下来我们把在.h文件中声明的属性navController进行定义

//通过指定根试图的方式,参数需要一个跟视图,我们将刚刚

//声明并定义号的表视图交给它,作为跟视图控制器

   Self.navController = [[UINavigationController alloc] initWithRootViewController:tableView];

[self.window addSubview:navController.view];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

 

一如之前,我们将需要添加的代码进行了加粗标示,并且在这里我们还进行了注释。好了,就是如简单的寥寥数行代码,接下来让我们command R运行一下看看。

没错我们看到了一个淡蓝渐变色标题框和一个空空如也的表视图。其中没有任何内容。

这就是使用navigation导航控制器构建一个应用程序的基础框架,现在它看起来让我们感觉那么的苍白,陌生,即使你使用过再多的IOS应用程序,我也敢打赌在之前你绝对没见到过如此让人感到茫然的界面,好吧,让我们花点事件来丰富一下它。

鉴于表视图的复杂性以及本文主旨等诸多元素,我们决定将演示导航页的方法换一种形式,我们不向通常的应用程序那样从UITableViewController上面设置选项别且点击之后进行视图的切换,毕竟我不想把这篇文章写成关于UITalbeView的讲解文章,要知道那个东西要比我们的Navigation复杂的多~~~

现在我们先要在.h文件中,也就是navController声明的的后面,加上一个返回值为IBAction的实例方法(也可以称为非静态方法)声明,凡是以此种类型为返回值并且需要一个id类型参数的实例方法,我们都可以把他称为事件处理函数,他们都是用来接受控件发出的各种事件的消息,例如点击等。通常我们会把它声明为如下这个样子:

        -(IBAction)NextPage:(id)sender

   然后我们回到m文件在implementation部分中对此函数进行实作,具体代码如下:

   -(IBAction)NextPage:(id)sender

{

//首先我们声明并定义了一个TableViewController的对象

UITableViewController *ContentView = [[UITableViewController alloc] nitWithStyle:UITableViewStylePlain];

//然后我们将ContentView推入到控制器栈中

//此方法有两个参数,第一个参数时要指定一个视图控制器

//第二个参数是一个BOOL值,用来指示是否开启切换视图的动画效果

    [self.navController pushViewController:ContentView animated:YES];

}

           还没完我们还需要一部工作,回到我们刚才将navController加入到视图的函数中,没错就是

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

在此函数中我们从将navController的视图加入到主窗口的语句后面添上两行代码,具体代码如下:

 

//首先我们声明一个按钮,然后将设置它的文本,样式

//并且制定事件处理对象为本对象,同事将点击事件处理函数设置为我们之前声明并定义的NextPage函数

UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithTitle:@”下一页” style:UIBarButtonItemStyleBordered target:self action:@selector(NextPage:) ];

//然后将我们刚创建出的按钮加入到tableView的导航索引中的右侧按钮

   tableView.navgationItem.rightBarButtonItem = buttonItem;

    好了,接下来我们可以运行程序看一下我们新的程序了~

   点击我们新添加的按钮,哎?好像没什么变化。。。仔细一看哦,原来按钮移动到了左侧并且上面写着“Back”,点一下,“下一页”的按钮又回来了。好吧,我必须跟大家承认一个错误,或者说我故意开了个小玩笑,其实现在页面已经在你点击按钮的时候切换,只不过两个页面的内容以及标题都是空白,并且都是列表视图,所以我们几乎看不出任何变化。。。

好吧让我们回到NextPage函数,将两个页面的差距变大一点,我们需要将第二页声明并定义的ContentView的类型更改为UIViewController并且添加一些其他元素来让两个界面显得更加不同,更改之后的完整代码如下

-(IBAction)NextPage:(id)sender

{

//我们首先更改了新视图的类型

//好吧,我真的错了,别抱怨了

    UIViewController *ContentView = [[UIViewController alloc] init];

    //接下来我们设置了一下ContentView的标题

    //请记住是ContentView的标题哦

    ContentView.title = @”其实我或许应该是列表视图中的某一项的详细视图”;

    //我们又声明label,并将其定义为x轴坐标为0y轴坐标也为0

    //尺寸为320*480(实际上我们就是沾满了整个屏幕)

    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];

    //我们设置一下背景色为深灰色

    label.backgroundColor = [UIColor darkGrayColor];

    //在设置一下label的文本

    label.text = @”其实我真的或许应该是列表视图中的某一项的详细视图”;

    //最后我们把label加入到ContectView的视图中

    [ContentView.view addSubview:label];

    [self.navController pushViewController:ContentView animated:YES];

}

    现在我们再运行一次程序。如何?页面的转换变的清晰了吧。如果有细心的人将会发现,我们在NextPage中设置的明明是ContentView的标题,为何导航条的标题会随之变换?其实这是导航条一个非常便捷的特性,就像我们可以通过设置tableView的属性去向导航条增加或设置按钮一样,NavgiationController会随着栈顶的控制器(也就是当前界面正在显示的视图控制器)的变换而更改当前的一些设置,这样,我们在设计每一个界面的时候就可以只考虑当前页面的设计而不必为导航页面会如何如何而担忧也不用害怕影响到其他页面。

   好了现在是说再会的时候了,当然关于导航控制器到此我们并不是完全掌握了,更多的应用方式以及特性等待着我们一起去探索,这仅仅是一个开始。如果有足够的空闲时间我们将在下一篇文章中详细的介绍我们今天接触到的UITalbeViewController。最后挪用一句一休大师的名人名言:好的,就到这里,再见吧!

Objective-C中的类、对象和方法

类、对象和方法

什么是对象

对象就是一个物件。汽车这个名称就是一个类。

汽车的每个实例都是一个对象。

实例和方法

类的独特存在就是一个实例,对实例执行的操作称为方法。

洗车这个动作,适用于一个实例。

而找出一个厂家制造了多少款汽车则适用于类,所以他是一个类方法。

 

类方法不需要一个特定的实例对象。只是针对于类别的操作,所以我们在使用类方法时并不需要构造一个实例对象。

这种方法在其他语言中又称之为静态方法。

@interface

在定义新类时,首先需要告诉Objective-C编译器该类的父类命名。其次,定义在处理该类的对象时,将要用到的各种操作或方法的类型。在@interface部分中还会列出一些元素,称为属性。

按照约定,类以大写字母开头,这种该约定能使其他人在阅读你的程序时,仅仅通过观察名称的第一个字母就能把类名和其他变量类型区分开来。

命名规则

sum$value   ———————— $非法字符

piece flag    ———————  名称中不允许插入空格

3Spencer    ———————— 名称不能以数字开头

int                ———————— 保留字

除了以上强制的命名的规则(指编译器无法通过的命名),通常为了让我们的代码阅读起来更加便于理解,我们还需要在命名上下一定功夫,有一定规则、规律,如类内属性我们可以统一使用m_开头,全局变量,我们都使用g_开头,这样的命名方式,让我们很容易在代码中区分开很多看起来相似,但是应用位置、作用都不同的变量和方法。

Objective-C中的大写字母和小写字母是有区别的,因此,变量名sum、SUM和Sum均表示不同的变量。

AddressBook————可能是一个类名称

currentEntry————可能是一个对象

current_entry————一些程序员还使用下划线作为单词的分隔符

addNewEntry————可能是一个方法名

确定名称时,要遵循同样的标准,千万不要偷懒,要找能反应变量或对象使用意图的名称,不要去使用中文的拼音代替,虽然这很方便,但是更多的时候这让他人无法理解你的意图,就像使用注释语句一样,富有意义的名称可以显著增强程序的可阅读性,并可在调试和文档编写阶段受益匪浅。事实上因为程序具有更强的自解释性,所以编写文档的任务将很可能大大减少。

类方法和实例方法

开头的负号(-),通知Objective-C编译器,该方法时一个实例方法。除此之外,只有一种选择,就是正号( ),它标示类方法。类方法时对类本身执行某些操作的方法,例如,创建一个新的实例。

在制造出一辆汽车后,引用这个汽车实例时,可能要执行给他加油的操作,这个加油的操作时对特定的汽车执行的,因此,它类似于实例方法。

  1. 返回值

声明新方法时,必须告诉Objective-C编译器该方法是否有返回值,如果有返回值,是哪种类型的值。

如:-(int) currentAge;

指定名为currentAge的实例方法将返回一个整型值。

如果方法没有返回值,则按照如下语句定义:

-(void) print;

这即是声明一个名为print且无返回值的实例方法。

2.方法的参数

-(void) setNumberator: (int) n;

-(void)setDenominator: (int) d;

以上两个方法,他们都是没有返回值的实例方法。每个方法都有一个整形参数,这是通过参数名前面的(int)指明的。就setNumerator来说,因此,setNumerator的声明制定向该方法传递一个名为n的整型参数,而且该方法没有要返回的值。这类似于setDenominator的声明,不同之处时后者的参数名是d。

声明方法的语法:每个方法都以冒号结束,并且这个冒号会告诉Ojbective-C编译器该方法会有参数。接下来,制定参数的类型并将其放入到一对圆括号中。最后,使用象征性的名称来确定方法所制定的参数名。整个声明以一个分号结束。详细函数声明的构造如下图:

[singlepic id=123]

然而实际情况中,我们需要定义的函数可能不只有一个参数,那么我们该如何定义一个多参数的方法呢?

通过列出每个连续的参数并用冒号将其连接起来,就可以定义一个接受多个参数的方法。用冒号连接的参数并用冒号将其连起来,就可以定义一个接受多个参数的方法。用冒号连接的参数将成为这个方法名的一部分。例如,方法名addEntryWithName:andEmail:表示接受两个参数的方法,这两个参数可能时姓名和电子邮件地址,它完整的声明应该如下:

-(void) addEntryWithName: (NSString) name

andEmail: (NSString) email;

在声明时,我们可以把整个函数声明写在同一行中,但是我们并不推荐这么做,因为那样会让你的代码看起来很长,并且很难看出参数的顺序以及参数之间的关系,所以通常,我们在声明函数时将每个参数独立一行,当声明到下一个参数时,则换新的一行,并且所有的参数之间,以冒号对其,这样,整个函数的声明就非常清晰明了了,冒号对其的左侧为函数的名称,而右侧一行为参数的类型以及参数名。这样的格式并不只局限于函数的声明或定义,在调用时我们依然可以这么做,这时一种被很多程序员们津津乐道的命名格式,或许刚开始你有点不适应,但相信我,你会爱上这种格式的。

@implementation

@implementation部分包含声明在@interface部分的方法的实际代码,且需要制定存储在类对象中的数据类型。在@interface声明方法,并在@implementation部分定义他们。

@implementation部分一般格式如下:

@implementation NewClassName

{

memberDeclarations;

}

methodDefinitions;

@end;

NewClassName表示的名称与@interface部分的名称相同。可以在父的名称之后使用冒号,如同在@interface部分使用冒号一样如:

@mplementation Fraction:NSObject

memberDeclarations部分指定了那种类型的数据将要存储到Fraction中,以及这些数据类型的名称。可以看到这一部分放到自己的一组花括号内,通常我们在其中声明一些类的成员变量、枚举等,但是不包括方法。对于类而言,其中的声明表示类的成员。如果你的类将构造出多个对象的话,那么你的每个对象都会拥有各自的成员。Objective-C系统将自动追踪这些实例变量。

因为Objective-C中并没有封装等级,如果硬要联系上的话,那么在此处声明的成员皆为私有成员,因为他没有声明在.h中,也就代表着无法被类外所认识。

 

methodDefinitions部分包含在@interface部分制定的每个方法中。此处的方法与@interface方法的声明使用相同的写法,但是我们并不会以分号作为结尾来结束函数的声明,因为此处我们定义(实作)这个函数——将你要该函数执行的的代码放入一对花括号中。

Program部分

Program部分并基于我个人的理解,他并不在某个类之中。它包含解决特定的问题的代码,如果有必要,它可以跨越多个文件。main函数我想对于学过其他高级语言(php除外- -)的人来说都不会陌生,它是程序开始执行的地方,也被称作程序入口点。main函数必须存在于Program部分的某个地方(个人观点,Program就是为了main函数而存在- -)。

 

 

以上是本人在学习Objective-C类时的一些心得体会,由于鄙人才疏学浅,如果有错误的地方还望看官多指教多批评。