- 两指缩放
- 上下滑动可以改变笑脸弧度
@IBDesignable 使View可以在storyBoard中实时预览
@IBInspectable 使属性可以在storyBoard中设置
HappinessViewController.swift
import UIKit
public class HappinessViewController: UIViewController ,FaceViewDataSource{
var happiness:Int = 25 { //0 = vary sad,100 = ecstatic
didSet{
happiness = min(max(happiness,0),100)
println("happiness = \(happiness)")
updateUI()
}
}
private struct Constants{
static let HappinessGestureScale:CGFloat = 4
}
// 滑动的事件
@IBAction func changeHappiness(gesture: UIPanGestureRecognizer) {
switch gesture.state{
case .Ended:fallthrough
case .Changed:
let translation =
gesture.translationInView(faceView)
let happinessChange = -Int(translation.y / Constants.HappinessGestureScale)
if happiness != 0 {
happiness += happinessChange
gesture.setTranslation(CGPointZero, inView: faceView)
}
default :break
}
}
@IBOutlet weak var faceView: FaceUIView!{
didSet{
faceView.dataSource = self
// 添加缩放事件,调用的是daceView里面的scale方法
faceView.addGestureRecognizer(UIPinchGestureRecognizer(target: faceView, action: "scale:"))
// faceView.addGestureRecognizer(UIPanGestureRecognizer(target: faceView, action: "scale:"))
}
}
private func updateUI(){
faceView.setNeedsDisplay()
}
func smilinessForFaceView(sender:FaceUIView) -> Double?{
return Double(happiness-50)/50
}
}
FaceUIView.swift
import UIKit
protocol FaceViewDataSource :class{
func smilinessForFaceView(sender: FaceUIView) -> Double?
}
@IBDesignable
class FaceUIView: UIView {
// @IBInspectable 使属性可以在storyBoard中设置
@IBInspectable
var lineWidth:CGFloat = 3{didSet{setNeedsDisplay()}}
@IBInspectable var color:UIColor = UIColor.blueColor() { didSet{setNeedsDisplay() } }
@IBInspectable var scale:CGFloat = 0.90{didSet { setNeedsDisplay() } }
var faceCenter:CGPoint{return convertPoint(center,fromView:superview)}
// 笑脸的半径
var faceRadius:CGFloat {
return min(bounds.size.width, bounds.size.height) / 2 * scale
}
weak var dataSource: FaceViewDataSource?
// 缩放
func scale(gesture:UIPinchGestureRecognizer){
if gesture.state == .Changed{
scale *= gesture.scale
gesture.scale = 1
}
}
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 smiliness = dataSource?.smilinessForFaceView(self) ?? 0.0
let smilePath = bezierPathForSmile(smiliness)
smilePath.stroke()
}
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}
// 画眼睛
private func bezierPathForEye(whichEye:Eye) -> UIBezierPath{
let eyeRadius = faceRadius / Scaling.FaceRadiusToEyeRadiusRatio
let eyeVerticaloffset = faceRadius / Scaling.FaceRadiusToEyeOffsetRatio
let eyeHorizontalSeparation = faceRadius / Scaling.FaceRadiusToEyeSeparationRatio
var eyeCenter = faceCenter
eyeCenter.y -= eyeVerticaloffset
switch whichEye {
case .Left: eyeCenter.x -= eyeHorizontalSeparation / 2
case .Right: eyeCenter.x += eyeHorizontalSeparation / 2
default :break
}
let path = UIBezierPath(arcCenter: eyeCenter, radius: eyeRadius, startAngle: 0, endAngle:CGFloat(2*M_PI), clockwise: true)
path.lineWidth = lineWidth
return path
}
// 画嘴角弧度
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
}
}