首页 > 代码库 > 斯坦福IOS开发第五课(第二部分)

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

转载请注明出处

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


作者:小马


 

五 代码演示样例

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

 

新建一个single view的project,然后新增一个视图类。叫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开发第五课(第二部分)