还是@段松 老师,讲解了 Xcode 常用的设置。还有楼房设计和楼房建造对应软件设计框架和代码实现。

课程目标:

  • 会做 APP
  • APP 微架构、代码风格

课程大纲:

  • 熟悉 Xcode

  • 一个 iOS 应用的构成详解

  • iOS 应用的生命周期

  • 一款 iOS 应用是如何产生的?

    看了7遍需求文档,心中自然有了成了一个原型图,甚至找到需求中缺乏的组件。

    曾经辍学做过建筑,就有了下面比喻,很形象好理解:

楼房建造流程图
iOS 开发流程图

项目开发流程图:

项目开发流程图

@段松 老师有7年的iOS研发经验,一线大牛我喜欢,奇幻之旅就正式开始吧。

iOS 开发和盖房子差不多,那么我们就先学习一些基本建筑材料:数据类型与操作符。后面还有一些数组、运算符、流程控制语句、枚举、结构体和宏定义等。

虽然这些之前都有学过,也算温故知新吧。和 Swift 相比确实繁琐古老的一些。

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
NSInteger age = 18;
NSInteger testAge;
testAge = age >= 18 ? 1 : 0;


if (testAge) {
NSLog(@"你已经是个成年人了");
} else {
NSLog(@"你还是未成年人");
}

char name[] = "Will";
printf(name);

NSInteger price = 4;
switch (price) {
case 4: {
NSLog(@"four");
NSLog(@"more than one line.");
break;
}
case 3:
NSLog(@"three");
break;
case 2:
NSLog(@"two");
break;
default:
break;
}

bool isFemale = false;
BOOL isMale = YES;
if (isMale) {
if (!isFemale) {
NSLog(@"You are a ture man.");
}
}

// 循环
int sum = 0;
for (int i = 1; i <= 100; ++i) {
sum += i;
}
NSLog(@"%i", sum);

int sum1 = 0;
int j = 1;
for ( ; ; ) {
sum1 += j;
j++;
if (j > 100) {
break;
}
}
NSLog(@"%i", sum1);


// 枚举
enum Person {GoodGirl, GodBoy, BadGirl, BadBoy};
enum Person someone;
someone = GodBoy;
if (someone != BadBoy && someone != BadGirl) {
NSLog(@"Someone is a good person.");
}


// 结构体
typedef struct {CGFloat x; CGFloat y;} MyPoint;
MyPoint myPoint = {6, 8};
CGFloat result = myPoint.x + myPoint.y;
NSLog(@"result = %f", result);

// #define 宏:文本替换

#define PI 3.1415926
#define SQUARE(x) ((x)*(x))

NSLog(@"%f", PI * SQUARE(5 - 4)); // 3.141593
NSLog(@"%.7f", PI * SQUARE(5 - 4)); // 3.1415926
NSLog(@"%e", PI * SQUARE(5 - 4)); // 3.141593e+00

// #import 导入文件
// #import "example.h"

// 条件编译
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif

极客班线下翻转课堂第一周,坐标浦东软件园,时间7月19日。

一天下来学习不少关于开发和产品的干货,感觉获益匪浅,但是还是要以后的学习和项目中实践可能才会理解透彻。本来参加极客班,是因为一个人独自钻研太累进展缓慢,现在感觉豁然开朗,信心十足,最主要的是有了学习的精神头,希望这一份主动性能够持续下去。既然 iOS 是我兴趣爱好,完成线上课程的同时,完成自我管理自律,明天图书馆约起。

课程安排的是线上课堂和项目实践同时进行,我想也多少和 Tiny 老师提到的学习方法相通:先了解官方文档 Guidelines,Release note,Documentation,理清楚技术架构关系,项目实际使用技术重点学习突破,也就完成了项目(大意如此)。

今天最大缺憾是没有和老师同学很好的交流,无论是上台提创意,提问题,听课有疑问没有去请教。一则我一直性格害羞内向,二则因为一造成的缺乏想法表达能力和技巧。性格弱我早就知道,天天提醒自己最棒能做好,希望能一点点进步。

以下是课程安排:

自我介绍:

一部分同学是网络,开发,室内设计,摄影,大三,多是因为兴趣爱好报名的。但更多的都已经是几年的开发经验,还有几个是5-10年的,没有开发工作经验的我要努力了,不能落了后。

@李建忠

线下班推你下水,逼你学会游泳。真实做项目中学习技术。

推荐 iOS 样板项目供同学选择:

  • 移动的 Blog
  • 交互式白板
  • 多媒体相册

@Tinyfool

  • 全栈:建筑学-掌握全局

    实际项目需要技术和知识,如何组合运用到项目中,再去详细学习。培训目的,自主学习,遇到具体的难题可以提问。

  • 创业: Zero to One,先失败,项目火之前少有人认同,先做一个简版让客户评判

  • 项目管理:项目迭代去做,MVP,如:10行代码,成就感,

    PM 和程序员关系,需求优先级往往没说,程序员分功能评估时间。两者之间的如何协作好,进度表中的关键:细分技术评估时间,确定优先级。

  • 融资->招人->离职,半年才招齐人

  • 创业泥潭,产品不温不火,添加不靠谱功能

  • UX 优先级低于功能实现

  • 先站出来才有人认识你,占尽便宜

  • 以后课堂不说了,只接受提问交流

孔祥波

经历:Unix/SA->SP->Python->OC

写代码自我要求,优秀。不要用直接抄袭其他产品。

知识像产品迭代,每次看一个知识都会不一样的理解。能讲解一个知识或技术,说明真的懂了。例如:UIObject等的继承关系。

大家提创意项目:

  • 信贷员工具项目
  • 个人版历史上的今天
  • 局域网多屏互动
  • 摄影项目
  • 个人头像贴图

董飞

算法原理与实践 课程知识梳理。

投票分组选项目

互联网:UGC(User-generated content)、用户互动、公司基因创始人背景专业

组团学习重要性,学习是动词

证书不重要,重要的是真正学习成长

重分享:output 带动 input

TODO:第一周的功能列表,团队 GitHub账号

我选了信贷员工具项目。

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
}


}

上个星期五正式从公司离职,做了一年整。为什么辞职不做 IT 技术支持,一个原因工资太低。

记得当初进公司是在家自学了3个月 iOS 无果,准备一边上班一边继续自学,现在看来多么傻的一个决定,既然不想做 IT 技术支持,就不要做。因为实际上很累,一天下来,偶尔还要加班,完全没有精力晚上在学习。当然只是一部分原因,最主要的阻碍是我自律很差,完全不适合自学。早早的应该3年前和老爸去咨询 IT 开发培训的时候,就该入学的。也不至于毕业3年,一事无成。

最近几天我在浦东图书馆学习,效率很高,完全能够投入其中。报了博览网的 iOS 极客班,希望能够一反面改变我自己:提高自我管理能力,另一方面也能得到系统的学习和大神的指导。

选择极客班不仅仅是价格比传统线下培训学校便宜,主要看中老师都是一线的大牛。最初最初也是通过@tinyfool 老师了解到极客班,相信性情中人不会错。期待周末开班,当然还有顺利结业并找到 iOS 的工作。

Swifter - 100 个 Swift 必备 tips,作者[王巍](SWifter - 100个 Swift 必备 tips)。书买了很久了,25元比实体书便宜不少,而且收入基本上都归作者。学习完疯狂 Swift 讲义,再看看这个补充一下。

章节顺序可能与书不一致,全凭兴趣随意浏览,尤其是不太懂得方面。

##操作符
infix 表示一个中位操作符,前后都有输入。类似的还有 prefix 和 postfix。

associativity 定义了结合律

precedence 运算的优先级,Swift 中乘法和除法的优先级是150,加法和减法是140,这里定义160,就是高于普通的四则运算。

操作符定义在全局,不可滥用。

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
//  main.swift
// Swift-test

import Foundation

struct Vector2D {
var x = 0.0
var y = 0.0
}

func +(left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}

let v1 = Vector2D(x: 2.0, y: 3.0)
let v2 = Vector2D(x: 1.0, y: 5.0)
print(v1 + v2) // Swift_test.Vector2D(x: 3.0, y: 8.0)

infix operator +* {
associativity none
precedence 160
}

func +*(left: Vector2D, right: Vector2D) -> Double {
return left.x * right.x + left.y * right.y
}

print(v1 +* v2) // 17.0

随机数生成

创建一个 Range 的随机数的方法,方便以后复用。

Swift 支持 // MARK: 、// TODO:、// FIXME:等编译标记,方便分段,将一标签的形式显示在导航栏中,让程序更易读。

1
2
3
4
5
6
7
8
9
10
// MARK: 实现方法
func randomInRange(range: Range<Int>) -> Int {
let count = UInt32(range.endIndex - range.startIndex)
return Int(arc4random_uniform(count)) + range.startIndex
}

// TODO: 测试是否有效
for _ in 0...100 {
print(randomInRange(1...6))
}

断言

对于判断输入是否满足某种条件的运用情景,使用断言比 if 更合适。且在 Debug 编译是才有效,运行时不被编译执行,不会消耗运行时的性能。是非常合适的调试判断。

1
2
3
4
5
6
7
8
9
10
import Foundation

func convertToKelvin( celsius: Double) -> Double {
let AbsoluteZeroInCelsius = -273.15
assert(celsius > AbsoluteZeroInCelsius, "输入的摄氏温度不能低于绝对零度。")
return celsius - AbsoluteZeroInCelsius
}

let roomTemperature = convertToKelvin(27)
print(roomTemperature) // 300.15

自省

程序设计和人类哲学所面临的同一个重大课程就是解决“我是谁”。

isKindOfClass、isMemberOfClass、is

1
2
3
4
5
6
7
8
9
class ClassA: NSObject { }
class ClassB: NSObject { }

let obj1: NSObject = ClassA()
let obj2: NSObject = ClassB()

print(obj1.isKindOfClass(ClassA.self)) // true
print(obj2.isMemberOfClass(ClassA.self)) // false

属性观察

属性观察可以对当前类型内监视其属性的设定,并作出响应。

1
2
3
4
5
6
class MyClass {
    var date: NSDate {
        willSet {
        print("即将将日期从 \(date) 设定至 \(newValue)")
        }
        didSet {
        print("已经将日期从 \(oldValue) 设定至 \(date)")
        }
} init() { date = NSDate() }
}
let foo = MyClass() foo.date = foo.date.dateByAddingTimeInterval(10086)
// 输出 // 即将将日期从 2015-07-16 16:01:49 +0000 设定至 2015-07-16 18:49:55 +0000
// 已经将日期从 2015-07-16 16:01:49 +0000 设定至 2015-07-16 18:49:55 +0000

本节介绍了一些 Swift 和 Foundation Framework,基本是要求自己搜索学习完成的,最好是参考 Swift 教材系统看看。说实话老师一节课只是把大纲列一下,好多根本没有展开详细系统的讲,当然时间上也不允许。

  • 可选值和枚举
  • 数组、字典、区间
  • 数据类型
  • 方法
  • 属性
  • 初始化
  • AnyObject
  • 常用的方法和函数

看英文视频加中文字幕,效率也是蛮低的,下个星期六博览网的 iOS 极客班就开学了,希望能够认真高效投入更多时间去学习,一定要学好,找到开发的工作,圆毕业三年的一个梦。

Swift 学习还算顺利,MVC 模式也有所熟悉,就是独自开发和想法实现有困难,暂时停留在看懂和自己写一遍的水平。

还有就是玩手机,刷微博啥的超级浪费时间,因为这个不费脑,玩的也高兴,能够一天不吃饭,也不困,也停不下来。
但是学习或看书,就很费神,容易犯困,这个一定要克服,实在困了就休息10分钟,再继续。

New Calculator Demo

  • Applying MVC to the Calculator
  • enum
  • Simple initializer
  • Returning an Optional
  • Dicionary
  • Tuples

源码如下,待修改:

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
//
// CalculatorBrain.swift
// Calculator
//
// Created by Will Ge on 7/11/15.
// Copyright © 2015 gewill.org. All rights reserved.
//

import Foundation

class CauculatorBrain
{
// 定义一个枚举类型,来分类表示输入的:运算数、一元运算和二元运算
enum Op {
case Operand(Double)
case UnaryOperation(String, Double -> Double)
case BinaryOperation(String, (Double, Double) -> Double)
}

// 定义一个数组来储存所有输入
var opStack = [Op]()

// 定义已知运算符
var knownOps = [String:Op]()

// 初始化已知运算符
init() {
knownOps["+"] = Op.BinaryOperation("+", +)
knownOps["−"] = Op.BinaryOperation("−") {$1 - $0}
knownOps["×"] = Op.BinaryOperation("×", *)
knownOps["÷"] = Op.BinaryOperation("÷") {$1 / $0}
knownOps["√"] = Op.UnaryOperation("√", sqrt)
}

// 定义一个函数,把输入递归取出
func evaluate(ops: [Op]) -> (result: Double?, remainingOps:[Op]) {
if !ops.isEmpty {
var remainingOps = ops
let op = remainingOps.removeLast()
switch op {
case.Operand(let operand):
return (operand, remainingOps)
case .UnaryOperation(_, let operation):
let operandEvaluation = evaluate(remainingOps)
if let operand = operandEvaluation.result {
return (operation(operand), operandEvaluation.remainingOps)
}
case .BinaryOperation(_, let operation):
let op1Evaluation = evaluate(remainingOps)
if let operand1 = op1Evaluation.result {
let op2Evaluation = evaluate(remainingOps)
if let operand2 = op2Evaluation.result {
return (operation(operand1, operand2), op2Evaluation.remainingOps)
}


}

}
}
return (nil, ops)
}

func evaluate() -> Double? {
let (result, remainder) = evaluate(opStack)
print("\(opStack) = \(result) with \(remainder) left over")
return result

}

// 运算数
func pushOperand(operand: Double) -> Double? {
opStack.append(Op.Operand(operand))
return evaluate()
}

// 运算符
func performOperation(symbol: String) -> Double? {
if let operation = knownOps[symbol] {
opStack.append(operation)
}
return evaluate()
}
}
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
//
// ViewController.swift
// Calculator
//
// Created by Will Ge on 6/28/15.
// Copyright © 2015 gewill.org. All rights reserved.
//

import UIKit

class ViewController: UIViewController {


@IBOutlet weak var history: UILabel!

@IBOutlet weak var display: UILabel!


var userIsInTheMiddleOfTypingANumber: Bool = false

var brain = CauculatorBrain()

@IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber{
display.text = display.text! + digit
} else {
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}

print("digit = \(digit)")
}



@IBAction func enter() {
userIsInTheMiddleOfTypingANumber = false
if let result = brain.pushOperand(displayValue) {
displayValue = result
} else {
displayValue = 0
}
print("pushOperand = \(displayValue)")

}

@IBAction func operate(sender: UIButton) {

if userIsInTheMiddleOfTypingANumber {
enter()
}
if let operation = sender.currentTitle {
print("\(operation)")
}
}



var displayValue: Double{
get {
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set {
display.text = "\(newValue)"
userIsInTheMiddleOfTypingANumber = false
}
}
}


我的需求: www.gewill.org 跳转到 gewill.org。

使用万网域名服务提供的 DNS 解析需要备案才能实现301跳转,所以之前使用 DNSPOD 的服务,但是不知为何竟失效了,gewill.org 能打开,www.gewill.org打不开了。换了其他的也都不行,dig 也是对的。

Google Github Pages 的相关信息,发现官方帮助文档就有简单是实现方案:

Configuring a www subdomain

If you configure both an apex domain (e.g. example.com) and a matching www subdomain (e.g. www.example.com), GitHub’s servers will automatically create redirects between the two.

For example:

If your CNAME file contains example.com, then www.example.com will redirect to example.com.
If your CNAME file contains www.example.com, then example.com will redirect to www.example.com.”

DNS 同时 CNAME 顶级域名和 WWW,然后在 Github CNAME文件中添加偏好的一个。

虽然之前有看过,一则英文看了只解一二,二则当时对于 DNS 和网站都刚刚接触。我学习新鲜东西节奏还是太慢,以后还要多思考,多与人交流。官方文档还是最简洁正确的。

继续 DEMO: Calculator

Swift 能类型推断

自动布局:

  • Pin: Spacing to nearest neighbor, Equal Widths, Equal Heights
  • Resolve Auto Layout Issues: Clear Constraints

87果然是个神奇的数字,我之前没有对齐就开始 Pin,始终无法得到“Add 87 Constraints”,自然没有得到自动对齐的理想布局。

代码如下:

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
//
// ViewController.swift
// Calculator
//
// Created by Will Ge on 6/28/15.
// Copyright © 2015 gewill.org. All rights reserved.
//

import UIKit

class ViewController: UIViewController {


@IBOutlet weak var display: UILabel!

var userIsInTheMiddleOfTypingANumber: Bool = false

@IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber{
display.text = display.text! + digit
} else {
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}

print("digit = \(digit)")
}

var operandStack = Array<Double>()

@IBAction func enter() {
userIsInTheMiddleOfTypingANumber = false
operandStack.append(displayValue)
print("operandStack = \(operandStack)")

}

@IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
enter()
}
switch operation {
case "×": performOperation { $0 * $1 }
case "÷": performOperation { $1 / $1 }
case "+": performOperation { $0 + $1 }
case "−": performOperation { $1 - $0 }
case "√": performOperation1 { sqrt($0) }
default: break
}
}

func performOperation(operation: (Double, Double) ->Double) {
if operandStack.count >= 2 {
displayValue = operation(operandStack.removeLast(), operandStack.removeLast())
enter()
}
}

func performOperation1(operation: Double ->Double) {
if operandStack.count >= 2 {
displayValue = operation(operandStack.removeLast())
enter()
}
}


var displayValue: Double{
get {
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set {
display.text = "\(newValue)"
userIsInTheMiddleOfTypingANumber = false
}
}
}

MVC:

  • 各个模块之间的含义
  • 模块之间的通信有无
  • 模块之间的控制关系

MVC

0%