【Swift】使用貝塞爾曲線繪製表情

一、建立一個基於UIView名爲FaceView的類

        咱們不直接在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 }

五、兩個get屬性

       faceCenter:計算在父視圖的中心座標。

//在父視圖上的中心座標
var faceCenter:CGPoint {
        get{
            return convertPoint(center, fromView: superview)
        }
}

        faceRadius:表情的縮放係數

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變成了難過的表情:

最後但願你們幸福指數都是1 !!!!

Github:

https://github.com/ly918/Demos

相關文章
相關標籤/搜索