持續更新中......
swift官方文檔
協議
swift主要基于協議編程的,所以協議無處不在。
先看看這篇文章:Swift學習:協議
官方的一些常用協議:
1. Equtable
在Swift中可以通過實現Equatable協議使自定義類型支持==以及!=這兩種運算符。
2. Comparable
Comparable協議繼承于Equatable,實現Comparable協議可以在Equatable的基礎上使類型支持>,>=,<,<=四種運算符。
3. CustomStringConvertible
當類實現這個協議,可使用print
打印類的自定義信息。
4. CustomDebugStringConvertible
當類實現這個協議,可使用debugPrint
打印類的自定義信息。
5. Hashable
繼承于Equatable。
一個類型為了存儲在集合中,該類型必須是可哈?;?該類型必須提供一種方法計算它的哈希值,一個哈希值為Int類型,相等的對象哈希值必須相同。
Swift的所有基本類型(形如String,Int,Double,Bool)默認是可哈?;?,可以作為集合的值的類型或者字典的鍵的類型。沒有關聯值的枚舉成員值默認也是可哈?;?。
用我的話來說:當想把自定義對象當做key存入set和dict中時,那么這個key必須是哈希的,并且要重載等號來對比key之間的不同。
官方的文檔說的比較清楚,這里有篇中文翻譯:
Swift之Hashable協議
6. Codable
Codable = Decodable & Encodable
顧名思義,即編解碼序列化。
具體參看以下文章:
Swift 4.0: Codable
Swift 4 踩坑之 Codable 協議
這里的“&”符號的意思是協議合成:
協議合成并不會生成新的、永久的協議類型,而是將多個協議中的要求合成到一個只在局部作用域有效的臨時協議中。
協議合成的意思是都羅列的協議都需要遵循。
其實不僅僅是協議可以合成,類也可以跟協議一起使用“&”符號合成。
7. CaseIterable
一句話:CaseIterable被用于合成簡單枚舉類型的 allCases 靜態屬性。
Swift 4.2 新特性詳解 CaseIterable.allCases
Swift--enum枚舉,協議CaseIterable
8. Identifiable
這個協議只需要你定義一個 id 屬性,這個屬性必須是一個 Hashable 類型。
協議和泛型
加粗的這句話非常重要:
針對Class和Function,都是通過<Type>來定義。而當我們需要給協議實現一個泛型的時候,需要使用associatedtype關鍵字來定義泛型:這里需要注意,泛型協議并不能像普通協議那樣作為一個類型使用,這是因為 GenericProtocol 表示一組類型,并不是一個單一類型。比如你有一個關于 GenericProtocol 的隨機數組,并不能確定每個元素的 magic() 方法返回的類型到底是什么,因為數組中每個元素可能都是不同的。
泛型最終在使用時都要被推斷出來,一種情況是實現的時候指定了類型,另一種在調用時明確類型。
參考:
元類型.Type 與 .self
一開始看到官方例子中有一行這樣的代碼:
func load<T: Decodable>(_ filename: String, as type: T.Type = T.self) -> T {
...
}
//調用方法
let landmarkData: [Landmark] = load("landmarkData.json")
對于as type: T.Type = T.self
這一部分不理解,搞懂這個需要先學習元類型。
元類型就是類型的類型。
let n : Int = 5
變量n
的類型是Int
類型,這個很容易理解。
那么Int
的類型又是什么呢?
Int
的類型就是:Int.Type
,類型的類型,即元類型。
說完類型來說值:
n
的類型是Int
,值是5。
let m: Int.Type = Int.self
那么變量m的類型是元類型Int.Type
,m的值是Int.self
。
到這兒我們再回頭來看上面的代碼:
func load<T: Decodable>(_ filename: String, as type: T.Type = T.self) -> T {
...
}
as
在這兒是標簽名字,type
是參數名,它的類型是:T.Type
元類型,默認值是T.self
。
所以我們也可以這樣調用方法,跟不寫第2個參數是等價的:
let landmarkData: [Landmark] = load("landmarkData.json", as: [Landmark].self)
class和static的區別
class和static用于修飾方法和計算屬性使其成為靜態方法或者靜態屬性。
最大的區別:
- class只能在類中使用,static可以在類和結構中使用。
- class修飾的方法可以被子類重寫,而static不能被重寫。
其他細節的區別參看:
swift3.0 中class和static
ForEach和\.self
官方例子:
static var previews: some View {
ForEach(["iPhone SE", "iPhone XS Max"], id: \.self) { deviceName in
LandmarkList()
.previewDevice(PreviewDevice(rawValue: deviceName))
.previewDisplayName(deviceName)
}
.environmentObject(UserData())
}
需要先學習這篇文章:
SwiftUI 和 Swift 5.1 新特性(3) Key Path Member Lookup
enum: if case,guard case,for case
模式匹配第四彈:if case,guard case,for case
關鍵字
associatedtype:
從字面上來理解,就是相關類型。意思也就是被associatedtype關鍵字修飾的變量,相當于一個占位符,而不能表示具體的類型。具體的類型需要讓實現的類來指定。
typealias:
用來為已經存在的類型重新定義名字的,通過命名,可以使代碼變得更加清晰。使用的語法也很簡單,使用typealias 關鍵字像使用普通的賦值語句一樣,可以將某個已經存在的類型賦值為新的名字。
Combine
Combine可以看作是簡化版的RxSwift,Combine目前學習資料不多,可以先學RxSwift了解基本概念,再使用下面的對照速查表快速學習Combine。
RxSwift中文文檔
RxSwift 使用詳解系列
Swift Combine 入門導讀
RxSwift到Apple的Combine速查表(Cheat Sheet)
Apple Combine 的開源實現:
有空可以看看,對于加深Combine的理解有好處。
有兩個項目:
OpenCombine
項目地址:broadwaylamb/OpenCombineApple Combine 的開源實現 CombineX 的第一個 beta 發布啦!
項目地址:luoxiu/CombineX
SwiftUI學習資料
這個是官方Demo的中文翻譯文檔
建議先從這里開始學,結合源碼學的更快。
WillieWangWei/SwiftUI-Tutorials基本控件、狀態流、手勢等知識的簡單說明和示范
Jinxiansen/SwiftUI
3.SwiftUI之屬性包裝
講解了以下屬性包裝:
@State
ObservableObject
@Published
@ObservedObject
@EnvironmentObject
@Environment
@Binding
@GestureState
學習SwiftUI遇到的問題和bug
- SwiftUI update navigation bar title color
- 滾動ScrollView到指定位置:
SwiftUI ScrollView: How to modify .content.offset aka Paging? - 鍵盤彈出/隱藏時修改布局
How to show complete List when keyboard is showing up in SwiftUI - SecureField
- 只要在頁面中使用了SecureField那么該頁面的第三方輸入法就被禁用,在其他TextField上也不能調出。
在切換到另外頁面中可以調出第三方鍵盤,不過因為在此頁面中已經被切換到系統輸入法,所以在別的頁面需要手動切換第三方鍵盤。 - 運行時焦點切換到SecureField會打印以下log:
[AutoFill] Cannot show Automatic Strong Passwords for app bundleID: xxx.xxx due to error: Cannot save passwords for this app. Make sure you have set up Associated Domains for your app and AutoFill Passwords is enabled in Settings
可以忽略不管。
參看此篇文章:WWDC18 iOS 自動生成強密碼和自動填充驗證碼/密碼
- 去掉導航頭
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
.navigationBarTitle("注冊", displayMode:.inline)//tmd,必須設置這個才能真使.navigationBarHidden(true)有效
- xcode的一個bug
在一個文件中SwiftUI預覽時右邊工具欄的屬性欄始終出不來,另一個文件可以看到屬性欄,折騰了半天把文件名一改屬性欄居然出來了,再把文件名改回去也一切正常。 - 去掉List或者Form的左邊空白
加在List/Form的子級中:
.listRowInsets(EdgeInsets())
以上還會留一點點空白,使用以下把空白完全去除:
.listRowInsets(EdgeInsets(top: 0, leading: -8, bottom: 0, trailing: 0))
或者
.padding(.horizontal, -24)
以上的-8,-24是自己對著界面調出來的,不一定通用(可能在不同平臺有不同表現)。一般問題不用太精確,湊合用著可以了。
- 不要隨便用Button
在Form的Section底下用了Button,當點擊一欄中不被其他控件比如TextField完全填充的空白區域時都會激活Button的action事件,把Button換成Image并且使用onTapGesture事件就正常了。
Image點擊事件代碼例子:
Image(systemName: "xmark.circle.fill")
.foregroundColor(.gray).opacity(0.5)
.imageScale(.small)
.onTapGesture {
print("image click")
}
TextField在系統自帶的簡體拼音輸入法時有bug
首先是輸入時鍵盤上的候選區閃動,另外輸入會莫明其妙的被自動刪除或者多出字符,總之該輸入法在TextField中基本不能用。
然后我換成UITextField后就沒問題了,shit!
目前iOS13.2.3依然有此bug。TextField和SecureField回車后無法自動切換到下一個輸入框。
根據上面第9和第10條,TextField和SecureField最好還是別用,用UIKit版封裝一下替代。
最好不要自定義previewDevice
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ForEach("iPhone SE","iPhone XS Max", "iPhone 11 ", "iPhone 11 Pro"], id: \.self) { deviceName in
ContentView()
.previewDevice(PreviewDevice(rawValue: deviceName))
.previewDisplayName(deviceName)
}
.environmentObject(UserData())
}
}
- 以上
"iPhone 11 "
在我機器上一定要加后面那個空格,否則preview時一直出不來。 - 我實際上只使用一個設備比如
"iPhone XS Max"
或者"iPhone 11 Pro"
,當使用Xcode一段時間后,Xcode的子進程:SourceKitService會瘋狂占用內存,占用的內存多達幾十G,機器變得非常慢,最后只好強制殺死SourceKitService進程。
不使用以上自定義previewDevice,只使用以下默認代碼則一切正常:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
- 改變全屏顏色包括SafeArea區域
// Color實際是個View,所以可以使用edgesIgnoringSafeArea來忽略安全區達到全屏效果
.background(Color(red: 0.9, green: 0.9, blue:0.9).edgesIgnoringSafeArea(.all))
- 當在xcode中打開多個Tab窗口時,只在一個Tab中開啟Preview。如果在多個Tab中開啟Preview的話可能會預覽失敗。
Font Icon和SF Symbols
Font Icon:
開源庫好幾個,我先選擇了這個:
ranesr/SwiftIcons
不過后來發現蘋果官方有SF Symbols。
SF Symbols:
ios13以上系統自帶SF Symbols,應該也是Font Icon,可以直接調用。
官方文檔說明:
SF Symbols
要想知道所有符號名字需要下載上面頁面提供的一個mac軟件,不過直接下載很慢,使用迅雷快很多。
所以使用SwiftUI開發APP的話可以不使用第三方FontIcon直接使用SF Symbols。
調用例子:
Image(systemName: "arkit").foregroundColor(.blue)