關(guān)于斯坦福的這個系列課程的所有筆記本缠,都在這個文集里:Developing Apps for iOS9
一稀颁、MVC 模式
MVC模式詳細(xì)的解釋,講了15分鐘楣黍。
定義:
Model = What your application is (but not how it is displayed)
Controller = How your Model is presented to the user (UI logic)
View = Your Controller's minions
三大陣營的交流:
Controllers can always talk directly to their Model and their View. Controller interpret/format Model information for the View.
The Model and View should never speak to each other.
controller可以對view和model說話匾灶,controller當(dāng)然也可以把model的信息翻譯給view。不過model和view直接永遠(yuǎn)不說話租漂。
那么view能對controller說話嗎阶女?只能在某些情況下:action,outlet哩治,delegate秃踩,data source。
那么model能對controller說話嗎业筏?不能憔杨。那么model數(shù)據(jù)有了更新,怎么告訴controller數(shù)據(jù)有更新呢驾孔?通過使用 Notification&KVO 模式廣播數(shù)據(jù)更新的事件芍秆。
二惯疙、計算器Demo應(yīng)用
繼續(xù)代碼,增加功能妖啥。
1. 讓讀取寫入數(shù)據(jù)更容易的方法
在 viewController 中優(yōu)化一個方法:
var displayValue:Double {
get {
return Double(display.text!)!
}
set {
display.text = String(newValue)
}
}
2. 點(diǎn)擊運(yùn)算符的方法
計算器App上有數(shù)字Button霉颠,點(diǎn)擊數(shù)字創(chuàng)建Action連接方法是@IBAction private func touchDigit(sender: UIButton)
,有π荆虱、e蒿偎、加減乘除、等于這樣的運(yùn)算符怀读,當(dāng)點(diǎn)擊這些運(yùn)算符時诉位,觸發(fā)了@Action 方法:
@IBAction private func performOperation(sender: UIButton) {
//1. 把用戶輸入的數(shù)字傳給 Model
//2. 把用戶點(diǎn)擊選中的運(yùn)算符(比如加減乘除中的某一個)傳給Model
//3. 讀取把 Model 計算出來的結(jié)果,然后將結(jié)果顯示到App的label上
//注意:這里都是和Model交互菜枷,因?yàn)镃ontroller只是翻譯苍糠,記得嗎,這些計算啤誊,實(shí)際上應(yīng)該是MVC中的M來做的事情
}
3. 創(chuàng)建 Model
新建 CalculatorBrain.swift 文件岳瞭。
根據(jù)上面2中的3個注釋,我們可以知道至少需要三個東西:
import Foundation
class CalculatorBrain {
//1. 把用戶輸入的數(shù)字傳給 Model
func setOperand(operand: Double) { }
//2. 把用戶點(diǎn)擊選中的運(yùn)算符(比如加減乘除中的某一個)傳給Model
func performOperation(symbol: String) { }
//3. (讀取把 Model 計算出來的結(jié)果蚊锹,然后將結(jié)果顯示到App的label上) = 也就是要有結(jié)果瞳筏,可讀的結(jié)果
var result: Double {
get {
return 0.0
}
}
}
有了這三個方法,點(diǎn)擊某個運(yùn)算符后的方法就可以擴(kuò)充一下了:
var brain = CalculatorBrain()
@IBAction private func performOperation(sender: UIButton) {
if userIsInTheMidlleOfTyping {
// 把用戶輸入的數(shù)字傳給 Model
brain.setOperand(displayValue)
// 正在輸入數(shù)字的狀態(tài)設(shè)為false
userIsInTheMidlleOfTyping = false
}
if let mathematicalSymbol = sender.currentTitle {
// 把用戶點(diǎn)擊選中的某個運(yùn)算符(比如加減乘除中的某一個)傳給Model
brain.performOperation(mathematicalSymbol)
}
// 讀取把 Model 計算出來的結(jié)果牡昆,然后將結(jié)果顯示到label里
displayValue = brain.result
}
4. API 知識
讓 View controller 中的屬性成為 private姚炕,在 view controller 文件里將變量和方法變成 private。
5. setOperand 方法 + 結(jié)果
點(diǎn)擊運(yùn)算符后已經(jīng)將數(shù)字傳給 Model 了丢烘,接下來在 Model 里實(shí)現(xiàn)具體的要求:
// 創(chuàng)建局部變量柱宦,記錄用戶需要計算的數(shù)字
private var accumulator = 0.0
func setOperand(operand: Double) {
accumulator = operand
}
最后的結(jié)果可以返回剛剛創(chuàng)建的局部變量了:
var result: Double {
get {
return 0.0
}
}
6. 完善 performOperation 方法之善用枚舉
這個可是 CalculatorBrain 里最核心的部分了,進(jìn)行計算的地方铅协。那么不寫代碼捷沸,先理順一下思路。
func performOperation(symbol: String) { }
首先要做的事情就是狐史,確認(rèn)傳過來的到底是哪個運(yùn)算符痒给。App上的運(yùn)算符除了數(shù)字,都可能是這個symbol骏全。那么可以用 switch 語句來切換苍柏。
確認(rèn)了是哪個運(yùn)算符之后就可以進(jìn)行計算了,π還好說姜贡,?這樣的也容易试吁,只需要一個數(shù)字就能計算了,加減乘除可怎么辦呢?這需要首先要記住傳入的第一個參數(shù)熄捍,然后記住運(yùn)算符是哪個烛恤,然后知道第二個運(yùn)算符,才能進(jìn)行計算的余耽。哦對了缚柏,別忘了還有等號這個按鈕。
把運(yùn)算符分分類發(fā)現(xiàn)碟贾,也就常量π币喧、一元運(yùn)算符cos、二元運(yùn)算符+袱耽、等號這四類了杀餐。那么我們可以用枚舉列出這四種情況:
swift private enum Operation { //常量 case Constant //一元運(yùn)算符 case UnaryOperation //二元運(yùn)算符 case BinaryOperation //等號 case Equals }
7. 完善 performOperation 方法之善用詞典
也就是說,任何一個運(yùn)算符都是這四個之一朱巨。每個運(yùn)算符都對應(yīng)著四個中的一個史翘,可以用詞典表示這樣的情況,是的冀续,詞典的值可以是枚舉恶座。
private var operations: Dictionary<String,Operation> = [
"cos" : Operation.UnaryOperation,
"sin" : Operation.UnaryOperation,
"×" : Operation.BinaryOperation,
"÷" : Operation.BinaryOperation,
"+" : Operation.BinaryOperation,
"-" : Operation.BinaryOperation,
"π" : Operation.Constant,
"e" : Operation.Constant,
"=" : Operation.Equals
]
但是這樣還不能進(jìn)行計算。
8. 完善 performOperation 方法之 associated value
常量的話沥阳,就是用戶點(diǎn)擊π,傳入的數(shù)字就是π自点,不存在計算的問題桐罕。一元運(yùn)算符,傳入一個數(shù)字都就可以進(jìn)行計算了桂敛,二元運(yùn)算符需要進(jìn)行計算功炮,計算的方法必須要有兩個參數(shù),同時要有返回結(jié)果术唬,當(dāng)然薪伏,二元運(yùn)算符需要有個容器能記住第一次傳入的參數(shù)和用戶點(diǎn)擊的運(yùn)算符。
讓枚舉有 associated value粗仓。(optional也是枚舉嫁怀,optional有 associated value)在Swift里,function也可以成為associated value借浊。
private enum Operation {
// 如果是常量塘淑,只需Double類型的數(shù)值
case Constant(Double)
// 如果是一元運(yùn)算符,則需要一個方法蚂斤,這個方法獲知1個參數(shù)存捺,返回結(jié)果
case UnaryOperation((Double) -> Double)
// 如果是二元運(yùn)算符,需要一個方法曙蒸,這個方法是獲知2個數(shù)值捌治,返回結(jié)果
case BinaryOperation((Double,Double) -> Double)
case Equals
}
9.完善 performOperation 方法之閉包Closure
枚舉這里改了岗钩,詞典那邊會出現(xiàn)閉包,如下:
利用閉包默認(rèn)參數(shù)$0$1可以省去一些代碼:
private var operations: Dictionary<String,Operation> = [
"cos" : Operation.UnaryOperation(cos),
"sin" : Operation.UnaryOperation(sin),
"tan" : Operation.UnaryOperation(tan),
"±" : Operation.UnaryOperation({-$0}),
"?" : Operation.UnaryOperation(cbrt),
"√" : Operation.UnaryOperation(sqrt),
"X2": Operation.UnaryOperation({$0*$0}),
"X3": Operation.UnaryOperation({$0*$0*$0}),
"×" : Operation.BinaryOperation({$0 * $1}),
"÷" : Operation.BinaryOperation({$0 / $1}),
"+" : Operation.BinaryOperation({$0 + $1}),
"-" : Operation.BinaryOperation({$0 - $1}),
"%" : Operation.UnaryOperation({$0/100}),
"π" : Operation.Constant(M_PI),
"e" : Operation.Constant(M_E),
"=" : Operation.Equals
]
10. 完善 performOperation 方法之使用 Associated Value
再回到核心代碼上來肖油,這些枚舉兼吓、詞典什么,都是為了實(shí)現(xiàn) performOperation 方法的
func performOperation(symbol: String) {
if let operationH = operations[symbol] {
switch operationH {
// 這里的 let associatedValue 只是一個局部常量而已构韵,可任意命名
case .Constant(let associatedValue): accumulator = assosicatedValue
// 這里的 function 只是一個局部常量而已周蹭,可任意命名
case .UnaryOperation(let function): accumulator = fuction(accumulator)
case .BinaryOperation: break
case .Equals: break
}
}
}
11. 完善 performOperation 方法之結(jié)構(gòu)體
二元運(yùn)算符的方法沒有寫,為啥呢疲恢?因?yàn)槲覀冞€沒有創(chuàng)建能夠存儲第一次傳入的值和選中的運(yùn)算符凶朗,那就創(chuàng)建一個容器存儲這兩個東西吧,我們用結(jié)構(gòu)體來存儲:
private var pending: PendingBinaryOperationInfo?
struct PendingBinaryOperationInfo {
var binaryFunction:(Double, Double) -> Double
var firstOperand: Double
}
這樣就可以繼續(xù) performOperation 方法了(把第一個傳入的參數(shù)和用戶選中的運(yùn)算符存放到了 pending 中):
func performOperation(symbol: String) {
if let operationH = operations[symbol] {
switch operationH {
case .Constant(let associatedValue): accumulator = assosicatedValue
case .UnaryOperation(let function): accumulator = fuction(accumulator)
case .BinaryOperation(let function):
// 把第一個傳入的參數(shù)和用戶選中的運(yùn)算符存放到了 pending 中
// pending.binaryFunction 就是用戶選中的運(yùn)算方法显拳,pending.firstOperand 就是第一次傳入的參數(shù)了
pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator)
case .Equals:
// pending.binaryFunction 就是用戶選中的運(yùn)算方法棚愤,pending.firstOperand 就是第一次傳入的參數(shù)了
// 第一次傳入的參數(shù),和第二次傳入的參數(shù)accumulator杂数,在用戶選中的方法下進(jìn)行計算宛畦,將計算的結(jié)果賦值給accumulator
accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
}
}
}
12. 完善 performOperation 方法之解決小bug
OK,還有幾個小的bug要解決揍移,首先次和,連續(xù)二元運(yùn)算不能實(shí)現(xiàn),其次那伐,就是進(jìn)行過第一次二元運(yùn)算后踏施,之后再進(jìn)行計算,第一個數(shù)字永遠(yuǎn)是上次計算出的結(jié)果罕邀,而用戶輸入的畅形,則成了第二個參數(shù)。這個效果還不太好诉探!
解決方法如下:
func performOperation(symbol: String) {
if let operationH = operations[symbol] {
switch operationH {
case .Constant(let associatedValue): accumulator = assosicatedValue
case .UnaryOperation(let function): accumulator = fuction(accumulator)
case .BinaryOperation(let function):
executePendingBinaryOpration()
pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator)
case .Equals:
executePendingBinaryOpration()
}
}
}
private func executePendingBinaryOpration() {
if pending != nil {
accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
pending = nil
}
}
13. Stack View 優(yōu)化界面顯示日熬,兼容橫屏效果
每一行是一個Stack View,共五行Stack View(按鈕多的話可以六行七行)
然后五行組合起來是一個Stack View
對 Stack View 的設(shè)置如下圖:
五行組合起來的再和Label是一個Stack View肾胯,屬性設(shè)置如下:
三竖席、課后作業(yè)
課后作業(yè)就是看課堂的PPT課件,然后做 Project 1 的編程項(xiàng)目阳液。
好吧怕敬,Project 1的難度完全虐到我了,非常打擊我帘皿。
必做項(xiàng)目
Get the Calculator working as demonstrated in lectures 1 and 2.
Your calculator already works with floating point numbers (e.g. if you touch 3 ÷ 4 =,it will properly show 0.75), however, there is no way for the user to enter a floating pointnumber directly. Fix this by allowing legal floating point numbers to be entered (e.g.“192.168.0.1” is not a legal floating point number!). You will have to add a new “.”button to your calculator. Don’t worry too much about precision or significant digitsin this assignment (including in the examples below).
Add some more operations buttons to your calculator such that it has at least a dozenoperations total (it can have even more if you like). You can choose whateveroperations appeal to you. The buttons must arrange themselves nicely in portrait andlandscape modes on all iPhones.
Use color to make your UI look nice. At the very least, your operations buttons mustbe a different color than your keypad buttons, but otherwise you can use color inwhatever way you think looks nice.
Add a String property to your CalculatorBrain called description which returns adescription of the sequence of operands and operations that led to the value returnedby result. “=“ should never appear in this description, nor should “...”.
Add a Bool property to your CalculatorBrain called isPartialResult which returnswhether there is a binary operation pending (if so, return true, if not, false).
Use the two properties above to implement a UILabel in your UI which shows thesequence of operands and operations that led to what is showing in the display. IfisPartialResult, put . . . on the end of the UILabel, else put =. If theuserIsInTheMiddleOfTypingANumber, you can leave the UILabel showing whateverwas there before the user started typing the number. Examples ...
touching 7 + would show “7 + ...” (with 7 still in the display)
7 + 9 would show “7 + ...” (9 in the display)
7 + 9 = would show “7 + 9 =” (16 in the display)
7 + 9 = √ would show “√(7 + 9) =” (4 in the display)
7 + 9 √ would show “7 + √(9) ...” (3 in the display)
7 + 9 √ = would show “7 + √(9) =“ (10 in the display)
7 + 9 = + 6 + 3 = would show “7 + 9 + 6 + 3 =” (25 in the display)
7 + 9 = √ 6 + 3 = would show “6 + 3 =” (9 in the display)
5 + 6 = 7 3 would show “5 + 6 =” (73 in the display)
7 + = would show “7 + 7 =” (14 in the display)
4 × π = would show “4 × π =“ (12.5663706143592 in the display)
4 + 5 × 3 = would show “4 + 5 × 3 =” (27 in the display)
4 + 5 × 3 = could also show “(4 + 5) × 3 =” if you prefer (27 in the display)
- Add a C button that clears everything (your display, the new UILabel you addedabove, etc.). The Calculator should be in the same state as it is at application startupafter you touch this new button.
加分項(xiàng)目
Implement a “backspace” button for the user to touch if they hit the wrong digitbutton. This is not intended to be “undo,” so if the user hits the wrong operationbutton, he or she is out of luck! It is up to you to decide how to handle the case wherethe user backspaces away the entire number they are in the middle of typing, buthaving the display go completely blank is probably not very user-friendly. You willfind the Strings and Characters section of the Swift Reference Guide to be veryhelpful here.
Change the computed instance variable displayValue to be an Optional Doublerather than a Double. Its value should be nil if the contents of display.text cannotbe interpreted as a Double. Setting its value to nil should clear the display out. You’llhave to modify the code that uses displayValue accordingly.
Figure out from the documentation how to use the NSNumberFormatter class to formatyour display so that it only shows 6 digits after the decimal point (instead of showingall digits that can be represented in a Double). This will eliminate the need forAutoshrink in your display. While you’re at it, make it so that numbers that areintegers don’t have an unnecessary “.0” attached to them (e.g. show “4” rather than“4.0” as the result of the square root of sixteen). You can do all this for yourdescription in the CalculatorBrain as well.
Make one of your operation buttons be “generate a random number between 0 and1”. This operation button is not a constant (since it changes each time you invoke it).Nor is it a unary operation (since it does not operate on anything).
四东跪、做完P(guān)roject 1后的感想:人笨就要多努力
既然已經(jīng)笨了(我認(rèn)了),最好不要加上蠢,不然蠢笨加起來可沒救了虽填。(人可以笨丁恭,但不可以蠢)
既然這么笨,多看幾遍視頻好了斋日,看個十遍八遍的牲览,總能吸收點(diǎn)什么的,拿我來說恶守,我這么笨第献,看到第五遍的時候,都寫了這么長一篇筆記了兔港。
反脆弱庸毫、反脆弱、衫樊、飒赃、
哼
早晚我要完成這個 Project,雖然現(xiàn)在的水平還不行科侈,我先記下载佳,總有一天完成后,我要更新本文臀栈。
要是一年以后再看這篇文章蔫慧,還是沒有完成 Project,就尷尬了~