The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

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
// : Playground - noun: a place where people can play

import UIKit

//: 析构过程(Deinitialization)

//析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字deinit来标示,类似于构造器要用init来标示。

//有点像 UIViewController 的 func ViewWillDisappear

class Bank {
static var coinsInBank = 10_000
static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
static func receiveCoins(coins: Int) {
coinsInBank += coins
}
}

class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins)
}
func winCoins(coins: Int) {
coinsInPurse += Bank.vendCoins(coins)
}
deinit {
Bank.receiveCoins(coinsInPurse)
}
}

var playerOne: Player? = Player(coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
print("There are now \(Bank.coinsInBank) coins left in the bank")

playerOne?.winCoins(300)

print("The player has \(playerOne!.coinsInPurse) coins")
print("There are now \(Bank.coinsInBank) coins left in the bank")

playerOne = nil
print("The player has \(playerOne?.coinsInPurse) coins")
print("There are now \(Bank.coinsInBank) coins left in the bank")


The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// : Playground - noun: a place where people can play

import UIKit

//: 构造过程(Initialization)

//: 通过定义构造器(Initializers)来实现构造过程,这些构造器可以看做是用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

//: 类的实例也可以通过定义析构器(deinitializer)在实例释放之前执行特定的清除工作。想了解更多关于析构器的内容,请参考析构过程。

//: 类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。

struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}

struct Fahrenheit1 {
var temperature = 32.0
}

//: 如果你在定义构造器时没有提供参数的外部名字,Swift 会为构造器的每个参数自动生成一个跟内部名字相同的外部名。

struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}

let green = Color(red: 1, green: 1, blue: 1)

struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)

//: 可选类型的属性将自动初始化为nil,表示这个属性是有意在初始化时设置为空的。

//: 如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器(default initializers)。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。

class ShoppingListItem {
var name: String?
var quantity = 1
}

var item = ShoppingListItem()

//: 结构体的逐一成员构造器

struct Size {
var width = 0.0, height = 0.0
}

let size = Size(width: 3, height: 4)

//: 构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。

struct Point {
var x = 0.0, y = 0.0
}

struct Rect {
var origin = Point()
var size = Size()

init() { }

init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}

init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}

let rect = Rect(origin: Point(x: 3, y: 3), size: Size(width: 5, height: 6))
let rect1 = Rect(center: Point(), size: Size(width: 4, height: 4))

//: 如果你想用另外一种不需要自己定义init()和init(origin:size:)的方式来实现这个例子,请参考扩展。

//: Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。

//: 两段式构造过程: Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性被引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步定制它们的存储型属性。

//: Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值0或空值(比如说0或nil)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以0或nil作为合法默认值的情况。

//阶段 1
//
//某个指定构造器或便利构造器被调用。
//完成新实例内存的分配,但此时内存还没有被初始化。
//指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
//指定构造器将调用父类的构造器,完成父类属性的初始化。
//这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部。
//当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。
//阶段 2
//
//从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
//最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。

//: 构造器的继承和重写

//跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误地用来创建子类的实例。
//你在子类中“重写”一个父类便利构造器时,不需要加override前缀。
//当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上override修饰符。

//: 构造器的自动继承

//如上所述,子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且可以在安全的情况下以最小的代价继承父类的构造器。
//
//假设你为子类中引入的所有新属性都提供了默认值,以下 2 个规则适用:
//
//规则 1
//
//如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
//
//规则 2
//
//如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。
//
//即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
//对于规则 2,子类可以将父类的指定构造器实现为便利构造器。

//简单总结就是:None or All

//: 指定构造器和便利构造器实践

class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}

//All
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}

//None
class ShoppingListItem1: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}

//: 可失败构造器

//创建自定义的可选类型
//如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。

struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}

let someCreature = Animal(species: "Gigg")
print(someCreature)

//枚举类型的可失败构造器

enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}

//带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),该可失败构造器有一个名为rawValue的参数,其类型和枚举类型的原始值类型一致,如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构造相应的枚举成员,否则构造失败。

enum TemperatureUnit1: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}

let fahrenheitUnit1 = TemperatureUnit1(rawValue: "K")
if fahrenheitUnit1 != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}

//: 类的可失败构造器

//值类型(也就是结构体或枚举)的可失败构造器,可以在构造过程中的任意时间点触发构造失败。比如在前面的例子中,结构体Animal的可失败构造器在构造过程一开始就触发了构造失败,甚至在species属性被初始化前。

//而对类而言,可失败构造器只能在类引入的所有存储型属性被初始化后,以及构造器代理调用完成后,才能触发构造失败。

//这个很好理解因为继承和构造器代理调用的原因,只能在最后一步判断是否构造失败

class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}

//可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,你可以增加一个可能的失败状态到现有的构造过程中。

//如同其它的构造器,你可以在子类中重写父类的可失败构造器。或者你也可以用子类的非可失败构造器重写一个父类的可失败构造器。这使你可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败。
//你可以用非可失败构造器重写可失败构造器,但反过来却不行。

class Document {
var name: String?

init() { }

init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}

class AutomoticallyNamedDocument: Document {

override init() {

super.init()
self.name = "[Untitiled]"
}

override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitiled]"
} else {
self.name = name
}
}
}

let newDoc = AutomoticallyNamedDocument()
newDoc.name

//你可以在init?中代理到init!,反之亦然。你也可以用init?重写init!,反之亦然。你还可以用init代理到init!,不过,一旦init!构造失败,则会触发一个断言

//: 必要构造器

//在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器

//: 通过闭包或函数设置属性的默认值

//提供了一种便利

class SomeClass {
let someProperty: String = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return "Lee"
}()
}

let some = SomeClass()
some.someProperty

struct Checkerboard {
let boardColors: [Bool] = {

var tempColors = [Bool]()
var isBlack = false

for i in 1 ... 10 {

for j in 1 ... 10 {
tempColors.append(isBlack)
isBlack = !isBlack
}

isBlack = !isBlack
}

return tempColors
}()

func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 10) + column]
}
}

let board = Checkerboard()
board.squareIsBlackAtRow(0, column: 10) //bug 此处仅为示范代码

The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

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
// : Playground - noun: a place where people can play

import UIKit

//: 继承(Inheritance)

//: Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。

//: 子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或下标(subscript)提供自己定制的实现(implementation)。我们把这种行为叫重写(overriding)。


class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// 什么也不做-因为车辆不一定会有噪音
}
}

class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}


class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}


//: 你可以通过把方法,属性或下标标记为final来防止它们被重写,只需要在声明关键字前加上final修饰符即可(例如:final var,final func,final class func,以及final subscript)。


The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

写了一段时间的 Swift 后,又来恶补基础知识了。

Playground ->

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
// : Playground - noun: a place where people can play

import UIKit

//: 下标(Subscripts)

//: 下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。

struct Matrix {
let rows: Int, columns: Int
var grid: [Double]

init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: 0.0)
}

func indexIsValidForRow(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}

subscript(row: Int, column: Int) -> Double {
get {
precondition(indexIsValidForRow(row, column: column), "Index out of range")
return grid[(row * columns) + column]
}

set {
precondition(indexIsValidForRow(row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}

var matrix = Matrix(rows: 2, columns: 3)
print(matrix[1, 2])
matrix[1, 1] = 3
print(matrix)

使用代码自动布局,需求还是有的,虽然很习惯了 IB 来做。参看 Programming iOS 9。

一共三个方法:

  • Anchor notation
  • Creating constraints in code
  • Visual format notation

1. Anchor notation

感觉 anchor 一个折中的方案,语法比 constraints 简洁,符合 IB 设计和添加约束的思路。美中不足是仅支持 iOS 9。

The NSLayoutAnchor class is a factory class for creating NSLayoutConstraint objects using a fluent API. Use these constraints to programatically define your layout using Auto Layout.

具体的使用语法都很简单,贴一个书中的 Demo:

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
let v1 = UIView(frame: CGRectMake(100, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView()
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)

mainview.addSubview(v1)
v1.addSubview(v2)

v2.translatesAutoresizingMaskIntoConstraints = false

var which: Int { return 3 }
switch which {
case 1:
// the old way, and this is the last time I'm going to show this
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Leading,
relatedBy: .Equal,
toItem: v1,
attribute: .Leading,
multiplier: 1, constant: 0)
)
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Trailing,
relatedBy: .Equal,
toItem: v1,
attribute: .Trailing,
multiplier: 1, constant: 0)
)
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Top,
relatedBy: .Equal,
toItem: v1,
attribute: .Top,
multiplier: 1, constant: 0)
)
v2.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Height,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1, constant: 10)
)

case 2:
// new API in iOS 9 for making constraints individually
// and we should now be activating constraints, not adding them...
// to a specific view
// whereever possible, activate all the constraints at once
NSLayoutConstraint.activateConstraints([
v2.leadingAnchor.constraintEqualToAnchor(v1.leadingAnchor),
v2.trailingAnchor.constraintEqualToAnchor(v1.trailingAnchor),
v2.topAnchor.constraintEqualToAnchor(v1.topAnchor),
v2.heightAnchor.constraintEqualToConstant(10),
])

case 3:

// NSDictionaryOfVariableBindings(v2,v3) // it's a macro, no macros in Swift

// let d = ["v2":v2,"v3":v3]
// okay, that's boring...
// let's write our own Swift NSDictionaryOfVariableBindings substitute (sort of)
let d = dictionaryOfNames(v1, v2, v3)
NSLayoutConstraint.activateConstraints([
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[v2]|", options: [], metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[v2(10)]", options: [], metrics: nil, views: d),
].flatten().map { $0 })
default: break
}

func dictionaryOfNames(arr: UIView...) -> [String: UIView] {
var d = [String: UIView]()
for (ix, v) in arr.enumerate() {
d["v\(ix + 1)"] = v
}
return d
}

2. Creating constraints in code

兼容 iOS 8 以下的,但是超级啰嗦。

  • iOS 6 可以全部使用最外面的视图添加约束,下面 Demo 中的:container.addConstraint(s) / removeConstraints
  • iOS 8 直接使用:NSLayoutConstraint.activateConstraints / deactivateConstraints
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

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let container = UIView(frame: self.view.bounds)
container.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.252461163294798)
self.view.addSubview(container)

let aLine = UIView()
aLine.frame = CGRect(x: 0, y: 0, width: 5, height: 5)
aLine.backgroundColor = UIColor(red: 0.6667, green: 0.0742, blue: 0.6667, alpha: 1.0)
container.addSubview(aLine)

aLine.translatesAutoresizingMaskIntoConstraints = false

let i = 3

switch (i) {
case 0:
// MARK:- superview addConstraint
container.addConstraint(NSLayoutConstraint(item: aLine, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1, constant: 0))
container.addConstraint(NSLayoutConstraint(item: aLine, attribute: .Trailing, relatedBy: .Equal, toItem: container, attribute: .Trailing, multiplier: 1, constant: 0))
container.addConstraint(NSLayoutConstraint(item: aLine, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1, constant: 0))
aLine.addConstraint(NSLayoutConstraint(item: aLine, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 20))
case 1:
// MARK:- iOS 6
container.addConstraints([
NSLayoutConstraint(item: aLine, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Trailing, relatedBy: .Equal, toItem: container, attribute: .Trailing, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 20),
])
case 2:
// MARK:- active = true
if #available(iOS 8.0, *) {
NSLayoutConstraint(item: aLine, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: aLine, attribute: .Trailing, relatedBy: .Equal, toItem: container, attribute: .Trailing, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: aLine, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: aLine, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 20).active = true
} else {
// Fallback on earlier versions
}

case 3:
// MARK:- NSLayoutConstraint.activateConstraints
if #available(iOS 8.0, *) {
NSLayoutConstraint.activateConstraints([
NSLayoutConstraint(item: aLine, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Trailing, relatedBy: .Equal, toItem: container, attribute: .Trailing, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 20),
])
} else {
// Fallback on earlier versions
}
default:
break
}
}

}


3. Visual format notation

这个语法支持 iOS 6,而且语法最为简洁直观。也是
Programming iOS 9 书中推荐的方案。实际项目尝试了上面两种两种方法后,想要更短的代码量的话,还是 Visual format notation 最为合适。这里也推荐大家,还很容易理解其语法,而且 console debugging 也会优先显示该语法。

1
2
3
4
5
6
7
8
9
10
11
12
<NSLayoutConstraint:0x7f855ad1bb00 H:[UIButton:0x7f855ad1bba0'Button'(46@188)] priority:188>

<NSLayoutConstraint:0x7f855ad1e130 UIButton:0x7f855ad1bba0'Button'.leading == UIView:0x7f855ad1ca20.leadingMargin + 127 priority:999>

Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x15c5ab880 V:[UIView:0x15c63d860(20)]>",
"<NSLayoutConstraint:0x15c5abb60 V:[UIView:0x15c63d860(10)]>"

当然这些都是不用 IB 和 View Debugging / Reveal 情况下的选择。

还有个优点就是类 ASCII-art,可视化的样式描述。

The Visual Format Language lets you use ASCII-art like strings to define your constraints. This provides a visually descriptive representation of the constraints.

下面是一个 CustomToolBar 的 Demo:

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
import UIKit

/**
Swift NSDictionaryOfVariableBindings substitute

- parameter arr: UIView array: (view1, view2)

- returns: return ["v1": UIView, "v2": view2]
*/
func dictionaryOfNames(arr: UIView ...) -> [String: UIView] {
var d = [String: UIView]()
for (ix, v) in arr.enumerate() {
d["v\(ix+1)"] = v
}
return d
}

class CustomToolBar: UIView {

var textField: UITextField!
var commentCountButton: UIButton!
var commentImageButton: UIButton!

override init(frame: CGRect) {
super.init(frame: frame)

self.backgroundColor = UIColor.whiteColor()
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func drawRect(rect: CGRect) {

let topLine = UIView()
topLine.backgroundColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1.0)
self.addSubview(topLine)

textField = UITextField()
textField.placeholder = "Write some in your deep mind."
self.addSubview(textField)

commentImageButton = UIButton(type: .Custom)
commentImageButton.setBackgroundImage(UIImage(named: "comment"), forState: .Normal)
self.addSubview(commentImageButton)

commentCountButton = UIButton(type: .Custom)
commentCountButton.titleLabel?.font = UIFont.systemFontOfSize(14)
commentCountButton.setTitle("8888888888", forState: .Normal)
commentCountButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
self.addSubview(commentCountButton)

topLine.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
commentImageButton.translatesAutoresizingMaskIntoConstraints = false
commentCountButton.translatesAutoresizingMaskIntoConstraints = false

// NSLayoutConstraintsHelper.swift
let d = dictionaryOfNames(topLine, textField, commentImageButton, commentCountButton)

self.addConstraints([
NSLayoutConstraint.constraintsWithVisualFormat("H:|[v1]|", options: [], metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat("V:|[v1(0.5)]", options: [], metrics: nil, views: d),

NSLayoutConstraint.constraintsWithVisualFormat("H:|-15-[v2]-[v3(14)][v4]-|", options: .AlignAllCenterY, metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat("V:|-[v2]-|", options: [], metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat("V:[v3(14)]", options: [], metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat("V:[v4(14)]", options: [], metrics: nil, views: d),
].flatten().map { $0 })
}
}


总结

经过几次项目的实践,发现还是 Visual Format 最好用,简洁直观。

参考

在 Swift 大行其道,Objective-C 渐行渐远的今天,这本书看起来还是获益匪浅。按照52条方法目录摘录一些笔记。

1. 了解 Objective-C 语言的起源

Objective-C 为 C 语言添加了面向对象的特性,是其超集。Objective-C 使用动态绑定的消息结构,运行时才会检查对象的类型。接受一条消息后,究竟执行何种代码,由运行期环境而非编译器决定。

5. 用枚举表示状态、选项、状态吗

6. 理解 “属性”可以概念

自动合成属性:@property,指定实例变量名称:@synthesize,属性特质:原子性/读写权限/读写方法名/内存管理,都做有详细的解释,就不在此赘述了。

7. 在对象内部尽量直接访问实例变量

直接访问实例变量:

  • 不经过方法派发(method dispatch)
  • 不调用设置方法(access methods)
  • 不触发键值观测(KVO)
  • 惰性初始化(lazy initialization)无法完成

11. 理解 objc_msgSend 的作用

例如:

1
id returnValue = [someObject messageName: parameter];

someObject 叫做接受者(receiver),messageName 叫做选择子(selector),选择子和参数合起来称为消息(message)。编译器看到消息后将其转换为一条标准的 C 语言函数调用,所调用函数 objc_msgSend,原型如下:

1
void objc_msgSend(id self, SEL cmd, ...)

编译器会把消息转换为如下函数:

1
2
3
id returnValue = objc_msgSend(someObject, 
@selector(messageName:),
parameter);

objc_msgSend 函数会依据接受者与选择子的类型,来搜寻接受者所属的类中方法列表(list of methods ),找到就跳至实现代码。若找不到就沿着继承体系向上查找。最终找不到就执行消息转发(message forwarding)。

未完

几乎都是两个人的对话,从双方羞涩的看向对方,到因为一对德国夫妻吵架而对视。男主鼓起勇气就此打开话题,聊天聊地,就此很愉快的旅程开始了。不管现实生活和情感是否顺利,但是两人总有新的话题和趣闻。当 Jesse 邀请下车游玩时候故事才真正开始。

开始的找有趣的地方,到后来随意的逛街道,酒吧,看手相,看日落,餐厅里互打电话,晚上草地聊天,次日火车道别。两人不太想落入俗套的留电话地址的偶遇,固执的开始时声明不留电话。因此格外珍惜相遇的这一天,玩的尽兴,聊得深入心灵。酒吧里算是爱情观的吐槽:人总是寻求被爱的多一点。也都是害怕重新承受之前恋爱分手之痛。看手相部分引起了一些争执,Jesse 理性的戳穿手相师和填词诗人的把戏,Céline 的满足和感动都搅合了。餐厅里的打电话很有意思,感觉 Céline 的很多时候也是主动地吐露情愫。

最后火车送别明明很不舍,但双方还是很坚持之前的想法不把这次邂逅变成俗套的艳遇。不过最后也做了妥协,约定六个月后此地在相见,还因为从今天还是昨天算起而争执了一番。说明内心是多么的喜欢对方,却又害怕伤害的顾忌而妥协。

电影里面男女主角都是普通的人的性格特点和际遇,但因为相互吸引和聚到一起,一天虽短,但却异常有趣。或者这样的爱情很奢侈,但是或者这就是不讲婚姻时纯粹的爱情。

Auto Layout Guide: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/index.html#//apple_ref/doc/uid/TP40010853-CH7-SW1

  • 优先使用 stack view,可简化设置(个人认为还是有时候还是非常复杂的布局还是不用,增加复杂度)
  • Constraint等式表示相等,而非赋值,表示两个 view 的关系
  • From the view’s intrinsic content size, it’s easy to understand: Compression Resistance & Content Hugging.
  • 尽量和最邻居的 view 建立 Constraint

参考文章 iOS开发 - 处理不等高TableViewCell的小花招,实践出真知,也是费了一番力气才把文章 Cell 使用 Auto Layout自动算高。眼高手低总是有的,所以以后不仅要学习开发的思路方法论,重要是在实践一遍。

Auto Layout设置好高度约束。不要实现- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

1
2
3
4
5
6
(void)viewDidLoad {
[super viewDidLoad];

self.tableView.estimatedRowHeight = 100; // 随便设个不那么离谱的值
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
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
- (TableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"
forIndexPath:indexPath];
cell.avatar.image = [UIImage imageNamed:@"avatar"];
cell.name.text = [NSString stringWithFormat:@"%ld", indexPath.row % 2];
cell.time.text = [NSString stringWithFormat:@"%@", [NSDate date]];
cell.source.text = @"Weibo.com";

NSMutableString *str = [NSMutableString new];
for (long i = 1; i < indexPath.row; i++) {
[str appendString:@"The 1989 World Tour (Live)"];
}
[str appendString:@"END!!!"];
cell.text.text = str;

//移除多余边框,如果是图片是 Aspect Fill 或者 Aspect Fit
cell.pic.layer.borderColor = [UIColor clearColor].CGColor;
cell.pic.layer.borderWidth = 0.1;
cell.pic.layer.masksToBounds = YES;

if (indexPath.row % 2 == 1) {
cell.pic.image = [UIImage imageNamed:@"pic"];
} else {
cell.pic.image = nil;
}

return cell;
}

源码:https://github.com/gewill/test-projects/tree/master/test%20auto%20height%20cell

Swift中的内存指针讲解的很清楚:http://onevcat.com/2015/01/swift-pointer/
Apple期望在Swift中指针能够尽量减少登场几率,因此在Swift中指针被映射为了一个泛型类型,并且还比较抽象。Swift中,指针都使用一个特殊的类型来表示,那就是UnsafePointer。总的来说还是为了方便处理 C API,平时并不需要深入了解。

还有苹果官方博客:Interacting with C Pointers

下面代码是数组指针用法:

    // Swift Memory UnsafePointer
    var array = [1, 2, 3, 4, 5]
    var arrayPtr = UnsafeMutableBufferPointer<Int>(start: &array, count: array.count)
    // baseAddress 是第一个元素的指针
    var basePtr = arrayPtr.baseAddress as UnsafeMutablePointer<Int>
    print(basePtr.memory) // 1
    basePtr.memory = 10
    print(basePtr.memory) // 10
    //下一个元素
    var nextPtr = basePtr.successor()
    print(nextPtr.memory) // 2
    print(array) // [10, 2, 3, 4, 5]
    
0%