@IBDesignable 使View能夠在storyBoard中實時預覽swift
@IBInspectable 使屬性能夠在storyBoard中設置app
HappinessViewController.swiftide
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.swiftspa
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 } }