咱们不直接在HappinessViewController(根视图控制器) 中实现,而是将FaceView彻底独立出来,这正是遵循了MVC的设计原则。咱们主要经过贝塞尔曲线来实现表情的绘制。git
注:@IBDesignable:Xcode6 的发布,苹果为开发者构建自定义控件推出了新功能IBDesignable和IBInspectable,容许在IB中实时预览设计成果。这会给实际开发提高很高效率。IBDesignable做用在于可使改视图在IB中实时预览。github
//能够在IB中预览 @IBDesignable class FaceView: UIView { }
定义线宽、线颜色、缩放系数swift
特别的:我另外定义了一个自定义参数——幸福指数。他的范围是-1~1。 app
注:@IBInspectable:将自定义的属性在IB中显示,以便咱们更方便的控制其属性,达到动态预览的效果。ide
@IBInspectable //线的宽度 var lineWidth : CGFloat = 3 { didSet {setNeedsDisplay() } } @IBInspectable //线的颜色 var color : UIColor = UIColor.blueColor() { didSet {setNeedsDisplay() } } @IBInspectable //表情缩放系数 var scale : CGFloat = 0.90 {didSet { setNeedsDisplay() } } @IBInspectable //微笑程度(幸福指数) var smiliness : Double = 0.75 {didSet { setNeedsDisplay() } }
咱们采用结构体来规定咱们想要的参数。以下所示:spa
private struct Scaling { static let FaceRadiusToEyeRadiusRatio:CGFloat = 10//眼睛弯曲半径比例 static let FaceRadiusToEyeOffsetRatio:CGFloat = 3//眼睛偏移比例 static let FaceRadiusToEyeSeparationRatio:CGFloat = 1.5//眼睛间隙比例 static let FaceRadiusToMouthWidthRatio:CGFloat = 1//嘴宽度比例 static let FaceRadiusToMouthHeightRatio:CGFloat = 3//嘴高度比例 static let FaceRadiusToMouthOffsetRatio:CGFloat = 3//嘴便宜比例 }
private enum Eye { case Left, Right }
//在父视图上的中心坐标 var faceCenter:CGPoint { get{ return convertPoint(center, fromView: superview) } }
var faceRadius:CGFloat { get{ return scale * min(bounds.size.width,bounds.size.height) / 2 } }
固然是先画脸(圆),经过UIBezierPath类方法来画(建立路径对象)一个圆(路径)。设计
//arcCenter: 圆心 //radius:半径 //startAngle:起点角度 //endAngle:终点角度 let facePath = UIBezierPath(arcCenter: faceCenter, radius: faceRadius, startAngle: 0, endAngle: CGFloat(2*M_PI), clockwise: true)
设置颜色宽度。code
//线的宽度 facePath.lineWidth = lineWidth //线的颜色 color.set()
最后别忘了调用stroke(),将这个路径画出来。对象
//最终将它"画"出来 facePath.stroke()
咱们封装一下画眼睛这部分代码,经过传入眼睛左右的枚举值来画。 眼睛实质仍是画圆,只是否是完整的圆,咱们只须要根据左右找到其中心、半径及起始角度就行了。开发
//眼睛 private func bezierPathForEye(whichEye: Eye) -> UIBezierPath { //计算眼睛的半径 let eyeRadius = faceRadius/Scaling.FaceRadiusToEyeRadiusRatio //计算眼睛垂直的偏移量 let eyeVerticalOffset = faceRadius/Scaling.FaceRadiusToEyeOffsetRatio //计算眼睛水平的距离 let eyeHorizontalSeparation = faceRadius/Scaling.FaceRadiusToEyeSeparationRatio //眼睛中心 var eyeCenter = faceCenter //y值是一致的 eyeCenter.y -= eyeVerticalOffset //根据左右来计算 眼的圆心x坐标 switch whichEye { case .Left: eyeCenter.x -= eyeHorizontalSeparation / 2 case .Right: eyeCenter.x += eyeHorizontalSeparation / 2 } //调用上面用过的UIBezierPath画圆的类方法来画圆 let path = UIBezierPath(arcCenter: eyeCenter, radius: eyeRadius, startAngle: 0, endAngle: CGFloat(2*M_PI), clockwise: true) //线的宽 path.lineWidth = lineWidth //最后返回路径 return path }
嘴能够用曲线来画。使用UIBezierPath对象的方法:addCurveToPoint,来画曲线。须要计算的值有、起始点、控制点。
//微笑 private func bezierPathForSmile(fractionOfMaxSmile : Double) -> UIBezierPath { //嘴宽 let mouthWidth = faceRadius/Scaling.FaceRadiusToMouthWidthRatio //嘴高 let mouthHeight = faceRadius/Scaling.FaceRadiusToMouthHeightRatio //嘴垂直偏移 let mouthVerticalOffset = faceRadius/Scaling.FaceRadiusToMouthOffsetRatio //微笑高度 let smileHeight = CGFloat(max(min(fractionOfMaxSmile, 1), -1)) * mouthHeight //开始点 let start = CGPoint(x: faceCenter.x - mouthWidth / 2, y: faceCenter.y + mouthVerticalOffset) //结束点 let end = CGPoint(x: start.x + mouthWidth, y: start.y) //第一个控制点 let cp1 = CGPoint(x: start.x + mouthWidth / 3, y: start.y + smileHeight) //第二个控制点 let cp2 = CGPoint(x: end.x - mouthWidth / 3, y: cp1.y) let path = UIBezierPath() //路径移至起点 path.moveToPoint(start) //增长路径的终点、控制点 path.addCurveToPoint(end, controlPoint1: cp1, controlPoint2: cp2) path.lineWidth = lineWidth return path }
其中控制点cp一、cp2能够经过下图来理解:
在重绘中调用以上方法,具体以下:
override func drawRect(rect: CGRect){ //圆圆的脸 let facePath = UIBezierPath(arcCenter: faceCenter, radius: faceRadius, startAngle: 0, endAngle: CGFloat(2*M_PI), clockwise: true) facePath.lineWidth = lineWidth color.set() facePath.stroke() //一双大眼 bezierPathForEye(.Left).stroke() bezierPathForEye(.Right).stroke() //嘴 let smilePath = bezierPathForSmile(smiliness) smilePath.stroke() }
打开SB(故事版)、点击FaceView,能够在右侧属性栏中看到,咱们自定义的属性已经绑定在右边了,咱们能够手动更改,在IB中实时预览输出效果!像下面这样:
试着更改一下宽度,我改成20,发下脸皮真的变厚了呢?
试着更改颜色,我改成黑色,好吧咱们都不喜欢黑脸!
试着改一下缩放比例,改成0.5,好吧脸变小了:
最后咱们试着调一下,幸福指数:
咱们发现幸福指数为1的时候是这样的:
0变成了直线,而-1变成了难过的表情:
Github: