從上週末開始到週三,除了學習老師教的觀察者模式(Observer Pattern)和幾種排序方法外,其他的時間多數在利用Xcode的Playground,來練習Swift的基本語法。此外也將專案做了版本控制,和拆分了大致的專案資料夾架構(用MVVM的模式)。
以下附上目前整理完成的Swift筆記:
基本資料型態大致有 Int 、 Double 、 Float 、 Bool
查看型別:
var str
str = "abc"
print(type(of:str))
Optional 本質是一個 enum 型別,裡面定義了兩種狀況。
任何型別只要有加上可選型別(optional type
)都可以設置成nil
。使用方法為在型別標註後面加上一個問號?
,如下:
// 在宣告變數時 型別標註後面加上一個問號 ?
var someScore: Int? // 因為目前尚未指派 所以目前 someScore 會被設置成 nil , 也就是沒有值的狀態
someScore = 100
print(someScore!) //如果確定可選型別的變數內有值,加上驚嘆號可取出值
someScore = nil
如果沒有經過這個步驟,不能直接把變數設成 nil , 比如以下這樣寫會報錯:
var someScore: nil
guard let 和 if let 的作用大同小異,同樣可讓 let 後的常數名和 optional 同名,主要的差別在以下幾個地方:
// newName 可在 guard let else { } 後繼續使用
func showName(name: String?) {
guard let newName = name else { return }
print("my name is \(newName)")
}
//newName 不可在 if let else { } 後繼續使用
func showName2(name: String?) {
if let newName = name {
print("my name is \(newName)")
}
print("my name is \(newName)")
}
App 的表單輸入頁面很適合搭配 guard let 檢查欄位。
guard let 也可以串接多個 optional,以下為檢查表單欄位新增資料的例子:
func createBook(title: String?, price: Double?, pages: Int?) {
guard let title = title, let price = price, let pages = pages else {
return
}
// 以下區塊可加入新增 Book 的程式碼
print("\(title) costs $\(price) and has \(pages) pages.")
}
字串相加可以用 + 號 ,也可以用 .append()
字串相等判斷用 ==
用 == "" 或屬性 .isEmpty判斷是否為空字串
屬性 .count取得字串長度
分割字串 : split(separator) ,分割後以陣列型態傳回
let str = "ab,cd,ef"
let arr = str.split(separator:",") //用逗號分割
var a = "a";
//字串中用 " \(變數、常數或表達式) " 來串接其他型態變數,其他型態與字串不能直接用加號串接!
var b = "ddd\(a)";
print(b) // 印出 ddda
var c = "cc"
var e = "e"
print(c+e); //印出cce
// u{24}的值為$這個符號, 代表 Unicode 純量 U+0024
let dollarSign = "\u{24}"
let str2 = "What a lovely day !"
print(str2.count) // 印出字元數量:19
if str4.hasPrefix("It is") { //如果前綴字串相同
print("Prefix")
}
if str4.hasSuffix("Sunday") { //如果後綴字串相同
print("Suffix")
}
()
前後包起來,每個值以逗號分隔,範例如下: let myInfo = ("Kevin Chang", 25, 178.25)
let myHeight = myInfo.2 //這邊依照順序的索引值,取出178.25
// 將上面宣告的 myInfo 分解成三個常數
let (myName, myAge, myRealHeight) = myInfo
print("My name is \(myName) .") // 印出:My name is Kevin Chang .
//把不需要的用底線 _ 標記
let (_, _, myTrueHeight) = myInfo
print("My height is \(myTrueHeight) .") // 印出:My height is 178.25 .
//在宣告元組時就個別給裡面的值一個名稱
let herInfo = (name:"Jess", age:24, height:160.5)
print("Her name is \(herInfo.name) . ") // 印出:Her name is Jess .
元組比較大小會依序由左到右逐個比較,直到有兩個值不相等為止。而如果所有值都相等,則會將這一對元組稱為相等的。
Swift 在比較元組的成員時,限制最多只能比較六個成員,如果有七個或七個以上成員則無法比較。
// true 因為 3 等於 3,但是 apple 小於 bird (依字串的各字元由左至右逐個比較)
(3, "apple") < (3, "bird")
Swift 提供三種基本的集合型別:Array
、Set
、Dictionary
來儲存集合資料。
Array 陣列:按順序儲存資料。
Set 集合:沒有順序、不能重複儲存資料。
Dictionary 字典:沒有順序,鍵值對 key : value
,也就是可以經由唯一的識別鍵找到需要的值。
var a = 1.2
var b = Int(a) // b強轉後等於1
即使變數或常數是 nil
也能變出預設值
a ?? b , 先判斷a
是否為nil
,如果a
有值,不是nil
,就會解析a
並返回,但如果a
為nil
,則返回預設值b
let defaultColor = "red"
var userDefinedColor: String? // 未指派值 所以預設為 nil
var colorToUse = userDefinedColor ?? defaultColor // 未指派值給 userDefinedColor ,所以會返回 defaultColor
print(colorToUse) // 這邊即印出:red
閉區間運算子: a...b,定義一個包含從a
到b
(包括a
和b
)的所有值的區間。b
必須大於等於a
。
半開區間運算子: a..<b,定義一個從a
到b
但不包括b
的區間。b
必須大於等於a
,但當a
等於b
時,則不會有值。
單側區間運算子: a... 或 ...a,代表一個只有以一邊a
為起點或終點,另一邊則是無限延伸的區間
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names[2...] { // 從陣列索引值為 2 的值開始依序印出 Brian Jack
print(name)
}
var arr3 = [Int]() // 宣告一個型別為 Int 的空陣列
var anotherArr: [Int] = [] // 宣告另一個型別為 Int 的空陣列
var arr : Array<Int> = Array<Int>()
合併陣列內容
var threeInts = Array(repeating: 0, count: 3) // repeating是指定預設值,這邊代表陣列為 [0, 0, 0]
// 接著再創建一個 [2,2,2] 的陣列
var anotherThreeInts = Array(repeating: 2, count: 3)
// 最後將兩個陣列合併
var SixInts = threeInts + anotherThreeInts
// 會變成 [0,0,0,2,2,2]
新增 / 插入 / 刪除陣列內容
var arr6 = ["Apples", "Eggs"]
arr6.append("Milk") //新增。變成 ["Apples", "Eggs", "Milk"]
arr6.insert("Rice" ,at: 0) //插入。變成 ["Rice" ,"Apples", "Eggs", "Milk"]
arr6.remove(at:1) //移除。變成 ["Rice", "Eggs", "Milk"]
用 for in 遍歷 Array 中的值(類似for each)
var arr7 = ["Rice" ,"Apples", "Eggs", "Milk"]
for item in arr7 {
print(item)
}
//當同時也需要獲得陣列值時 可以使用 enumerated() 方法
for (index, value) in arr7.enumerated() {
print("Item \(index + 1): \(value)")
}
// 會依序印出:
// Item 1: Rice
// Item 2: Apples
用 sort 排序。 sort() 會影響原本的陣列內容; sorted() 則會將排序結果回傳成另一陣列,不影響原內容。
arr.sort() //[1,2,5,7]
用 sort(by:) 設定排序方式。
arr.sort(by:>) //[7,5,2,1]
用 reverse()、 reversed() 將陣列反置。
arr.reverse() //[1,2,5,7]
用 contains(_:) 檢查某元素是否存在於陣列中,並回傳 boolean 值。
arr.contains(2) // true
key(鍵) 必須是唯一且不可重複
用來儲存多個相同型別的值。每個值(value
)都屬於一個唯一的鍵(key
),鍵作為字典中這個值的識別符號,所有鍵的型別也必須相同(鍵與值的型別不一定要相同)。
字典內的值沒有順序,所以需要根據這個鍵(key
)來找到需要的值(value
)。宣告字典型別時,使用 Dictionary<Key, Value>
// 宣告一個字典型別
var someDict: Dictionary<String, String>
// 或是這樣也可以
var someAnotherDict: [String:String , String:a, String:b]
guard else跟 if else一樣,後面都是接結果為 true 或 false 的條件,但 guard 卻有以下幾點不同之處:
guard 喜歡依賴別人,不能沒有 else。
guard 後專門描述我們希望成立的條件。當條件成立時,程式將離開 guard 搭配的 else { },繼續往下執行我們希望條件成立時做的事。
當 guard 的條件不成立時,將執行 else { } 的程式。
else { } 的程式執行後,必須離開 guard 所在區塊,如此才不會繼續往下執行條件成立時要做的事。就像剛剛的例子,我們利用 return 離開 function motherSay。
guard age > 18, weight > 40 else { //多重條件
print("年紀不夠大,體重太輕,只能乖乖唸書")
return
}
使用for-in
遍歷一個集合內的所有元素,像是一個數字區間、陣列、字典中的值或是字串內的字元,例子如下:
for index in 1...3 {
print(index)
}
swift 的 switch case 不需要寫break,執行完第一個成功的case後,就會自動跳出。如果在特殊情況下需要執行緊接著的下一個case
內的程式,就要用到 fallthrough。加上 fallthrough後進入到的下一個case,不會對其條件做比對,而是直接執行其內的程式。 比如以下程式碼:
let number7 = 5
var str4 = ""
switch number7 {
case 2,3,5,7,11,13,17,19:
str4 += "It is a prime number. "
fallthrough
case 100,200:
str4 += "Fallthrough once. "
}
print(str4) //印出 It is a prime number. Fallthrough once.
case
中比對的情況也可以是一個區間:
let number5 = 0
var str3: String
switch number5 {
case 0...10:
str3 = "幾"
case 11...100:
str3 = "很多"
default:
str3 = "超級多"
}
print("我有\(str3)顆蘋果") //印出:我有很多顆蘋果
當不同版本的作業系統提供的API不一樣時,在程式中必須判斷作業系統版本,確保app可以順利運作,程式碼範例如下:
//平台名稱可以是iOS、macOS或watchOS。版本號可以是大版本號像是iOS 10,或是較小的版本像是macOS 10.12
if #available(平台名稱 版本號, ..., *) {
// 在某個平台或版本下使用特別的 API
} else {
// 而其他的平台或版本則使用其他的 API
}
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API
// 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}
從寬鬆到嚴格依序為:
open : 不同的模組可以繼承也能夠存取。例如一個定義在framework中的open類別,可以在 APP 中 import framework 然後繼承、覆寫它
public: 模組內可以繼承、覆寫;模組外可以看到與使用,但不能繼承、覆寫。
internal (預設) : 模組內可以繼承、覆寫;模組外看不到。
fileprivate: 同一個檔案內可以存取。
private: 同一類可以存取。
swift 4 開始,允許 extension 中的程式可以存取原本類別中的 private 等級變數或函式,但 extension 必須與原類別在同一個檔案中
func
關鍵字,函式格式如下: func 函式名稱(參數: 參數型別) -> 返回值型別 {
內部執行的程式
return 返回值
}
實際寫成如下:
func addOne(number: Int) -> Int {
// number 即為被指派參數的常數 只能在函式內部範圍內使用
let n = number + 10
print(n)
return n
}
addOne(number: 12) // 呼叫函式傳入整數 12 印出 13
如果在函式呼叫時,不想要加上 argument label,可以用 _ 代替。範例如下:
// ... 宣告一個參數數量不固定的函式,傳進函式中的參數被轉為陣列型態
// func addOne(_number: Int ...)
// = 後為呼叫時沒給值時的預設值
// func addOne(_number: Int = 5)
func addOne(_number: Int ) -> Int {
// number 即為被指派參數的常數 只能在函式內部範圍內使用
let n = number + 10
print(n)
return n
}
addOne(12)
// inout關鍵字,傳址, 取出時變數前加 &
swift中的enum可以寫function,容易實現狀態模式
列舉使用case
關鍵字定義成員值,例子如下:
enum CompassPoint {
case north
case south
case east
case west
}
var directionToHead = CompassPoint.west
// 這時已經可以自動推斷這個變數的型別為 CompassPoint
directionToHead = .south // 如果要再指派新的值 可以省略列舉的型別名稱
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins") // 這行會被印出
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
value type
)與參考型別(reference type
)。值型別會儲存實際的值,而參考型別只是儲存其在記憶體空間中配置的位置。本週剩下的時間會繼續練習後面的語法,並且開始摸索 ARKit 的使用方式,因為我們的 APP 將會使用到 AR 的技術。