斯坦福IOS开发第五课(第二部分)

转载请注明出处

http://blog.csdn.net/pony_maggie/article/details/27845257

作者:小马

五 代码示例

上面讲到的知识点在这个示例都有涉及。另外我这里也只是分析部分重要的代码,更多的知识了解请自行下载代码(文章最下面有地址)并结合公开课一起看。

新建一个single view的工程,然后新增一个视图类,叫FaceView,如下图所示:

然后我们在storyboard里拖进来一个通用的视图控件,作为上面那个视图类对应的视图,如下图所示:

     

接着要做视图控制器类里增加这个FaceView的oulet以便我们可以操作视图,如下:

开始关注代码了。

我们打算在FaceView画一个笑脸,来反映幸福的程序,一个笑脸由下面几部分组成:

脸的轮廓(一个大圆)

眼睛(两个小圆)

嘴巴(贝塞尔曲线)

好,代码如下:

- (void)drawCircleAtPoint:(CGPoint)p withRadius:(CGFloat)radius inContext:(CGContextRef)context
{
    //设置成当前的context
    //使用UIKit来进行任意绘图,你会希望保存当前的UIKit上下文,包括所有已经绘制的内容,
    //接着切换到一个全新的绘图上下文中
    UIGraphicsPushContext(context);
    CGContextBeginPath(context);
    CGContextAddArc(context, p.x, p.y, radius, 0, 2*M_PI, YES);
    CGContextStrokePath(context);
    UIGraphicsPopContext();
}

//This method is called when a view is first displayed or when an event occurs that invalidates a visible part of the view
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    //draw face (circle)
    //draw eyes (2 circles)
    //no nose
    //mouth (Bézier curve)
    CGPoint midPoint;

    //注意这里,因为画圆本身是基于faceView自己,所以计算坐标也是相对于faceView,所以要用bounds下的坐标
    //当我们把faceView在storyboard里拉下一些,然后用frame对比看效果就很明显了
    midPoint.x = self.bounds.origin.x + self.bounds.size.width/2;
    midPoint.y = self.bounds.origin.y + self.bounds.size.height/2;

    CGFloat size = self.bounds.size.width/2; //大圆半径
    if (self.bounds.size.height < self.bounds.size.width) {
        size = self.bounds.size.height/2;
    }
    size *= self.scale;

    //设置线条宽度和颜色等
    CGContextSetLineWidth(context,5.0);
    [[UIColor blueColor] setStroke];
    [self drawCircleAtPoint:midPoint withRadius:size inContext:context];

#define EYE_H 0.35
#define EYE_V 0.35
#define EYE_RADIUS 0.10

    CGPoint eyePoint;
    eyePoint.x = midPoint.x - size * EYE_H;
    eyePoint.y = midPoint.y - size * EYE_V;
    [self drawCircleAtPoint:eyePoint withRadius:size*EYE_RADIUS inContext:context];
    eyePoint.x += size * EYE_H * 2;
    [self drawCircleAtPoint:eyePoint withRadius:size*EYE_RADIUS inContext:context];

#define MOUTH_H 0.45
#define MOUTH_V 0.45
#define MOUTH_SMILE 0.25 //弯曲的比例,微笑的程度

    CGPoint mouthStart;
    mouthStart.x = midPoint.x - size * MOUTH_H;
    mouthStart.y = midPoint.y + size * MOUTH_V;
    CGPoint mouthEnd = mouthStart;
    mouthEnd.x += size * MOUTH_H * 2;

    CGPoint mouthCP1 = mouthStart;
    mouthCP1.x += size * MOUTH_H * 2/3;
    CGPoint mouthCP2 = mouthEnd;
    mouthCP1.x -= size * MOUTH_H * 2/3;

    float smile = [self.dataSource smileForFaceView:self];
    if (smile < -1)
    {
        smile = -1;
    }
    else if (smile > 1)
    {
        smile = 1;
    }

    CGFloat smileOffset = MOUTH_SMILE * size * smile;
    mouthCP1.y += smileOffset;
    mouthCP2.y += smileOffset;

    CGContextBeginPath(context);
    CGContextMoveToPoint(context, mouthStart.x, mouthStart.y);
    CGContextAddCurveToPoint(context, mouthCP1.x, mouthCP1.y, mouthCP2.x, mouthCP2.y, mouthEnd.x, mouthEnd.y);
    CGContextStrokePath(context);

}

先上个图看一下效果:

这里有一点要注意,我们需要考虑横屏的情况,因为我是在xcode5环境下写的代码,就不用老师进的Struts and springs了,直接加约束,让storyboard帮我自动计算,如下所示:

  

然后再来看一下效果,

这个似乎也不是我们想要的,它自动拉伸了,我们还需要加一些代码调整,横屏时要重绘笑脸。

- (void)setup
{
    //UIViewContentModeRedraw可以使屏幕旋转时调用drawRect
    self.contentMode = UIViewContentModeRedraw;
}

- (void)awakeFromNib
{
    [self setup];
}

这样再来看看效果:

现在我们来添加手势识别,让这个笑脸支持缩放功能。

需要在facView和根控制器里加入相应的代码,facView里:

//手势识别,缩放功能
- (void)pinch:(UIPinchGestureRecognizer *)gesture
{
    if ((gesture.state == UIGestureRecognizerStateChanged) ||
        (gesture.state == UIGestureRecognizerStateEnded))
    {
        /*
         下面两行代码其实和这一行效果是一样的,所以注意理解第二行置1的作用.可以查一下gesture.scale是怎么取值的
         self.scale = gesture.scale;
         */
        self.scale *= gesture.scale;
        NSLog(@"scale:%f", gesture.scale);
        gesture.scale = 1;
    }
}

然后在根控制器里:

[self.faceView addGestureRecognizer:[[UIPinchGestureRecognizer alloc] initWithTarget:self.faceView action:@selector(pinch:)]];

看看效果:

     

接下来继续添加功能,我们用代理和上下滑动的手势识别来实现通过上下滑动来控制笑脸的微笑程度。

手势识别好理解,为什么要用代理呢?可以这样理解,FaceView里的这个笑脸,是通过一个幸福指数(controller里的happiness)来控制,这个幸福指数是一个数据源,那FaceView是一个视图,视图本身是不能拥有数据源的,所以要把controller作为代理管理数据源。

下面是代码。

添加手势识别

添加一个上下滑动的手势,注意target不是faceView而是controller,所以处理函数也在controller里实现, UIPanGestureRecognizer主要用于拖动,就是捕捉手指的位移

[self.faceView addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleHappinessGesture:)]];
self.faceView.dataSource = self; //把控制器设为代理

注意下面inView后面都是指定self.faceView作为参数,这个参数的意义是你打算让这个手势转换在哪个坐标系工作,当然是我们的FaceView了

- (void)handleHappinessGesture:(UIPanGestureRecognizer *)gesture
{
    if ((gesture.state == UIGestureRecognizerStateChanged) ||
        (gesture.state == UIGestureRecognizerStateEnded))
    {
        CGPoint translation = [gesture translationInView:self.faceView];//转换成坐标系中的点位移变化
        self.happiness -= translation.y / 2; //除2的作用时,减少变化的幅度
        [gesture setTranslation:CGPointZero inView:self.faceView];//置0可以让变化幅度不累加
    }
}

接下来实现协议中定义的函数,

- (float)smileForFaceView:(FaceView *)sender
{
    return (self.happiness - 50)/50.0;//happiness是0~100, 微笑程度是-1~1,需要转换
}

使用代理的位置

float smile = [self.dataSource smileForFaceView:self];
    if (smile < -1)
    {
        smile = -1;
    }
    else if (smile > 1)
    {
        smile = 1;
    }

模拟器下运行,会发现随着鼠标的上下拖动,笑脸的微笑程度会变化。

代码下载地址:

https://github.com/pony-maggie/Happiness

斯坦福IOS开发第五课(第二部分),布布扣,bubuko.com

时间: 2024-12-18 14:22:39

斯坦福IOS开发第五课(第二部分)的相关文章

斯坦福IOS开发第五课(第一部分)

转载请注明出处 http://blog.csdn.net/pony_maggie/article/details/27706991 作者:小马 由于第五课的内容比较多,分两部分来写. 一 屏幕旋转基本操作 控制当前的view是否支持旋转,如果是,是支持哪些方向的,有四个方向,分别是home键在下,上,左右. 在当前的viewcontroller里,实现shouldAutorotateToInterfaceOrientation方法,告诉系统你支持的旋转方向,如下: - (BOOL)shouldA

从零开始学ios开发(五):IOS控件(2),Slider

下面继续学习ios的其他控件,这次会使用到的控件有Slider,当然还有一些之前已经使用过的控件Label. 这次我们不新建一个project了,当然如果你愿意重新创建一个新的项目也完全可以,我们还是使用上一篇的项目Control Fun. 上一篇中,我们最后的成果如下图所示我们添加了一个ImageView,2个Label和2个TextField,现在我们继续在此基础上添加其他的控件. 1)添加Slider和LabelSlider类似于一个滑块,左右(或者上下)滑动来改变数值,在object l

IOS开发之国际化篇第二章:文本信息国际化

本文是以XCode7.2.1为开发平台 在第一章中,我们基本了解了国际化是怎么一会事,介绍到有3种国际化,接下来的几章就会分别介绍这3种国际化,第一个是文本信息国际化 文本信息国际化包含一下几点 系统按钮和信息国际化 应用名称国际化 程序代码中输出的静态文本国际化 -系统按钮和信息国际化 系统本身会自带一些系统的信息,系统按钮等,这一些系统自带的信息提示,按钮文本都是由Cocoa本身自带的,他们都是统一又系统管理,例如Done在英文环境中是Done,还有系统提示信息,如下图,在不同语言环境系统会

iOS开发 ReactiveCocoa入门教程 第二部分

ReactiveCocoa 是一个框架,它允许你在你的iOS程序中使用函数响应式(FRP)技术.加上第一部分的讲解,你将会学会如何使用信号量(对事件发出数据流)如何替代标准的动作和事件处理逻辑.你也会学到如何转换.分离和组合这些信号量. 在这里,也就是第二部分里,你将会学到更多先进的ReactiveCocoa特性,包括: 1.另外两个事件类型:error和completed 2.Throttling(节流) 3.Threading 4.Continuations 5.更多... 是时候开始了.

【ios开发学习 - 第一课】页面跳转

第一种 [self.navigationController pushViewController:subTableViewController animated:YES]; //描述:通过 NSNavigationBar 进行跳转 [self.navigationController popViewControllerAnimated:YES]; //描述:在子视图返回到上级视图 第二种 UIViewController *control = [[UIViewController alloc]

第五课-第二讲05_02_bash脚本编程之一 变量、变量类型等

grep 搜索结果是浪费和占用大量的CPU资源fgrep : fast grep 不支持正则表达式,所以速度快 1.shell脚本编程编译器,解释器 编程语言:机器语言(01代码)汇编语言(机器可以识别人不可以)高级语言(接近人类的思考特性,但不是人类的自然语言,也不是机器语言.所以需要编译器)静态语言(编译型语言,需要编译器编译在执行前全部转换成可用执行格式,强变量类型语言.如 C C++ java)动态语言(解释型语言,通常是弱类型语言,需要解释器,解释器本身是静态语言开发的.执行时在转换,

iOS开发-UI (五)UITextField

UITextField使用 1.创建方式 例: UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20, 20, 130, 30)]; 2.常用方法和属性 1)边框样式 @property(nonatomic)  UITextBorderStyle   borderStyle; UITextBorderStyleNone                       没有边框,背景默认为透明 UITextBorderS

斯坦福大学IOS开发课程笔记(第七课第二部分)

转载请注明出处 http://blog.csdn.net/pony_maggie/article/details/32163347 作者:小马 本篇是demo演示,程序其实就是上节课的心理学家那个demo, 不过在这个demo的基础上,把它作成一个通用版,可以自动识别当前运行的设备是iphone还是ipad,然后有不同的显示效果.所以,还是打开原来的Psychologist工程,在开始之前,我会用第五课讲的自动布局知识,让视图能在横屏模式下也可以正常显示.这一部分不细讲,参考第五课. 下面就可以

IOS学习之斯坦福大学IOS开发课程笔记(第六课)

转载请注明出处 http://blog.csdn.net/pony_maggie/article/details/28398697 作者:小马 这节课主要讲述多个MVC是如何协同工作的.到目前为止,所有的课程都只是涉及到一个视图.从这节课开始,将会持到多视图应用的例子. 多视图开发这里用navigationController举例子.这个也是比较常用的.如上图所示,一个 navigation Controller(MVC)会指向一个rootViewController(另一个MVC),这个是第一