CS193P 5. Objective-C Compatibility, Proper

1. Objective-C 兼容性

Bridging

Swift 可以无缝使用以前 Objective-C 的 API,但是一些特殊的数据类型需要桥接。桥接是指可以互换的使用。

NSString <-> String

NSArray <-> Array

NSDictionary <-> Dictionary<NSObject, AnyObject>

Int, Float, Double, Bool -> NSNumber (反之不行)

99%的桥接均是隐式完成的。

2. 属性列表

属性列表指 AnyObject 是一下六种类型的集合列表:
NSString, NSArray, NSDictionary, NSNumber, NSData, NSDate。

处理属性列表通常需要映射:如 is 和 as。

属性列表可以“盲”传递数据,也可以作为一种通用数据结构-类似泛型。

NSUserDefaults 作为一种使用属性列表的存储机制。本质上就是一个小的存储数据列表的数据库。通常用存储“设置”信息。
NSUserDefaults 可以通过名称或键来存储或检索整个属性列表。

以下用法摘自:NSUserDefaults — A Swift Introduction

存储 NSUserDefaults

1
2
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("Coding Explorer", forKey: "userNameKey")

NSUserDefaults 常用的写入方法:

  • func setBool(value: Bool, forKey defaultName: String)
  • func setInteger(value: Int, forKey defaultName: String)
  • func setFloat(value: Float, forKey defaultName: String)
  • func setDouble(value: Double, forKey defaultName: String)
  • func setObject(value: AnyObject?, forKey defaultName: String)
  • func setURL(url: NSURL, forKey defaultName: String)

iOS 8或更新版本不要使用 synchronize。

读取 NSUserDefaults

1
2
3
4
5
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey("userNameKey")
{
println(name)
}

对应的常用读取 NSUserDefaults 的方法:

  • func boolForKey(defaultName: String) -> Bool
  • func integerForKey(defaultName: String) -> Int
  • func floatForKey(defaultName: String) -> Float
  • func doubleForKey(defaultName: String) -> Double
  • func objectForKey(defaultName: String) -> AnyObject?
  • func URLForKey(defaultName: String) -> NSURL?
  • func dataForKey(defaultName: String) -> NSData?
  • func stringForKey(defaultName: String) -> String?
  • func stringArrayForKey(defaultName: String) -> [AnyObject]?
  • func arrayForKey(defaultName: String) -> [AnyObject]?
  • func dictionaryForKey(defaultName: String) -> [NSObject : AnyObject]?

使用常量做键

方便在一处修改,容易理解编译报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let userNameKeyConstant = "userNameKey"

@IBAction func writeButton(sender: UIButton)
{
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("Coding Explorer", forKey: userNameKeyConstant)
}

@IBAction func readButton(sender: UIButton)
{
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey(userNameKeyConstant)
{
print(name)
}
}

3. 视图

视图代表了一个长方形区域。

视图是等级,类似单继承:一个视图只可以有一个 superview,但可以有很多 subviews。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//
// FaceView.swift
// Happiness
//
// Created by Will Ge on 7/17/15.
// Copyright © 2015 gewill.org. All rights reserved.
//

import UIKit

class FaceView: UIView {

var lineWidth: CGFloat = 5 { didSet { setNeedsDisplay() } }
var color: UIColor = UIColor.blueColor() { didSet { setNeedsDisplay() } }
var scale: CGFloat = 0.9
var faceCenter: CGPoint {
return convertPoint(center, fromView: superview)
}

var faceRadius: CGFloat {
return min(bounds.size.width, bounds.size.height) / 2 * scale
}

// 覆写 drawRect,自定义视图
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 = 0.8
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
}

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
}


}