斯坦福iOS9公开课总结四:Views

说来惭愧,距上次更新笔记有一段时间了,本来计划是用两个月的时间完成本课程。结果,年前公司任务比较多,双休变单休,没能静下下心来继续学习。(PS: 都是借口,没完成就是没完成 →_→)。也罢,接下来挤时间完成余下的课程。加油,少年!

理论知识

本节课程前半部分讲解一些基本理论知识,这里只是给出罗列,不会一一讲解,如有不理解的请参考官网文档。

  1. Views: 说白了就是一个区域。
  2. Hierarchical: 一个View只有一个superView,多个subViews
  3. UIWindow
  4. View初始化
  5. 坐标系统数据结构: CGFloat, CGPoint,CGSize, CGRect
  6. Bounds VS frame
  7. Creating Views
  8. CustomViews
  9. UIBezierPath使用
  10. NSAttributedString/NSMutableAttributedString
  11. Fonts
  12. Images

FaceDemo(画笑脸)

课程44分钟开始讲解FaceIt例子。本文使用的是Swift3.0编写, 代码实现跟视频有所不同,不影响学习。

效果图.png

笑脸分为三个部分:

  1. 最外侧大圆
  2. 两个眼睛(两个小圆)
  3. 嘴巴(弧)

画圆

创建一个FaceView,添加到ViewController中。 在FaceViewdraw()方法中编写。

1
2
3
4
5
6
7
8
override func draw(_ rect: CGRect) {
let skullCenter = CGPoint(x: bounds.midX, y: bounds.midY)
let skullRadius = min(bounds.size.width, bounds.size.height) / 2
let path =  UIBezierPath(arcCenter: skullCenter, radius: skullRadius, startAngle: 0.0, endAngle: CGFloat(2 * M_PI), clockwise: false)
   path.lineWidth = 5.0
  UIColor.blue.set()
path.stroke()
    }

效果图:
圆.png

上面实现了一个圆,如果想复用,我们可以进行封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var scale : CGFloat = 0.90  //设置比例
private var skullCenter : CGPoint {
        return CGPoint(x: bounds.midX, y: bounds.midY)
    }
    private var skullRadius : CGFloat {
        return min(bounds.size.width, bounds.size.height) / 2 * scale
    }
private func pathForCricleCenteredAtPoint(midPoint: CGPoint, withRadius: CGFloat) -> UIBezierPath {
        let path =  UIBezierPath(arcCenter: midPoint, radius: withRadius, startAngle: 0.0, endAngle: CGFloat(2 * M_PI), clockwise: false)
        path.lineWidth = 5.0
        UIColor.blue.set()
        return path
    }
override func draw(_ rect: CGRect) {
pathForCricleCenteredAtPoint(midPoint: skullCenter, withRadius: skullRadius).stroke()
}

画眼

下面我们来画眼睛也是圆,我们只要计算出两个圆的圆心。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//结构体,定义了眼睛,嘴的预设值
private struct Ratios {
        static let SkullRadiusToEyeOffset: CGFloat = 3
        static let SkullRadiusToEyeRadius: CGFloat = 10
        static let SkullRadiusToMouthWidth: CGFloat = 1
        static let SkullRadiusToMouthHeight: CGFloat = 3
        static let SkullRadiusToMouthOffset: CGFloat = 3
    }
// 枚举: 左右眼
private enum Eye {
        case Left
        case Right
    }
// 计算眼睛圆心
private func getEyeCenter(eye: Eye) -> CGPoint {
        let eyeOffset = skullRadius / Ratios.SkullRadiusToEyeOffset
        var eyeCenter = skullCenter
        eyeCenter.y -= eyeOffset
        switch eye {
        case .Left:
            eyeCenter.x -= eyeOffset
        case .Right:
            eyeCenter.x += eyeOffset
        }
        return eyeCenter
    }
// 绘制眼睛
private func pathForEye(eye: Eye) -> UIBezierPath {
        let eyeRadius = skullRadius / Ratios.SkullRadiusToEyeRadius
        let eyeCenter = getEyeCenter(eye: eye)
        return pathForCricleCenteredAtPoint(midPoint: eyeCenter, withRadius: eyeRadius)
    }

效果图:

眼.png

画嘴

下面画嘴,画一条弧线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let mouthCurvature: Double = -1.0 // 1. 笑脸,-1. 哭脸
private func pathForMouth() ->UIBezierPath {
        let mouthWidth = skullRadius / Ratios.SkullRadiusToMouthWidth
        let mouthHeight = skullRadius / Ratios.SkullRadiusToMouthHeight
        let mouthOffset = skullRadius / Ratios.SkullRadiusToEyeOffset
        
        let mouthRect = CGRect(x: skullCenter.x - mouthWidth / 2 , y: skullCenter.y + mouthOffset, width: mouthWidth, height: mouthHeight) //矩形区域

        let smileOffset = CGFloat(max(-1, min(mouthCurvature, 1))) * mouthRect.height
        let start = CGPoint(x: mouthRect.minX, y: mouthRect.minY)
        let end = CGPoint(x: mouthRect.maxX, y: mouthRect.minY)
        let cp1 = CGPoint(x: mouthRect.minX + mouthRect.width / 3, y: mouthRect.minY + smileOffset)
        let cp2 = CGPoint(x: mouthRect.maxX - mouthRect.width / 3, y: mouthRect.minY + smileOffset)
        
        let path = UIBezierPath()
        path.move(to: start)
        path.addCurve(to: end, controlPoint1: cp1, controlPoint2: cp2)
        path.lineWidth = 5.0
        return path
    }

// 最后
override func draw(_ rect: CGRect) {
        pathForCricleCenteredAtPoint(midPoint: skullCenter, withRadius: skullRadius).stroke()
        pathForEye(eye: .Left).stroke()
        pathForEye(eye: .Right).stroke()
        pathForMouth().stroke()
    }

最终效果:

苦脸.png

更新说明

时间:2017-12-28

Stanford iOS11 with Swift