SwiftUI - 一起來仿寫微信APP之一首頁列表檢視
阿新 • • 發佈:2020-05-12
## 簡介
最近在學習 SwiftUI ,我一般都是先去學習介面佈局,所以就想著仿寫一下經常使用的軟體的介面,所以先拿微信開刀。因為不想一次性發太多的內容,所以只好將主題分解,一部分一部分地去講,接下來我們一起來學習吧。
如果你嘗試過使用 SwiftUI 編寫介面,你會發現是如此地舒心,我已深深地愛上了它。當然它的坑並不少,畢竟才剛出來,最低支援系統是 iOS13,估計還得等個幾年才會慢慢在公司裡使用上吧。但是這並不妨礙我們的學習。
在這篇文章裡,我會一步一步編寫微信的首頁列表檢視,一步一步將程式碼呈現上來,並仔細地講解,我相信你們都可以看懂的,先來看看效果圖。
很簡單吧?是很簡單,但是在編寫的時候還是有些技巧在裡面,畢竟,簡單才不容易勸退嘛。在開始前先講一下這篇文章將會用到的一些佈局與元件,先給大家一個印象,方便後面的閱讀理解。
HStack - 水平佈局
VStack - 垂直佈局
Text - 文字控制元件
Spacer - 擴充套件空間,使容器填滿布局空間
Image - 影象控制元件
List - 列表控制元件
Divider - 分隔線控制元件
## 工作環境
Xcode - Version 11.3.1 (11C504)
Swift - version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15)
## 開始編寫程式碼
### 編寫列表行
我們來先把頭像新增進來。
```swift
Image("1")
.resizable() // 1
.frame(width: 46, height: 46) // 2
.cornerRadius(6)// 3
```
> 1 - 在 SwiftUI 中,如果需要控制影象的大小,則必須先呼叫resizable修飾
>
> 2- 設定影象大小
>
> 3 - 設定圓角大小,四個角的大小都相同
因為佈局是橫向的,所以我們在外層使用`HStack`包裹起來,然後新增聯絡人名字和最後發訊息時間。
```swift
HStack {
// 頭像
HStack {
Text("女神")
.font(.body) // 1
Spacer()
Text("下午 2:55")
.font(.caption)
.foregroundColor(Color.gray.opacity(0.5)) // 2
}
}
```
> 1 - 使用 font 修飾字體,這裡使用了蘋果提供的標準字型,蘋果還提供了 largeTitle, title, headline, subheadline, body, callout, footnote, caption。
>
> 2 - 使用 foregroundColor 修飾字體顏色,因為 gray 的灰色還是太黑了,所以這裡又使用了 opacity 去修飾透明度為50%,使它顯得更淡一點。
名字下方顯示的是是最後傳送或接收的訊息內容,因此我們在外層使用 VStack 包裹起來。
```swift
VStack(alignment: .leading, spacing: 6) { // 1
// 名稱和時間
Text("對不起,你是個好人")
.font(.callout)
.foregroundColor(Color.gray)
}
```
> // 1 - 設定`VStack`裡子控制元件居左對齊,預設是居中對齊。再設定子控制元件的間隙為 6 個畫素,這樣比較符合微信上面的設計。
現在樣子已經出來了,我們先預覽下效果。
我們給最外層的`HStack`增加`padding`,使它更美觀一些,引數填寫`.all`代表四周都需要邊框。經過我的眼力觀察,它的預設是 16px 的樣子。
```swift
HStack {
// 頭像、名稱、時間、訊息內容
}
.padding(.all)
```
有了間距,好看多了。
接下來建立一個檢視,它負責裝載行檢視,起名為`GCMainRow`。
```swift
struct GCMainRow: View {
var body: some View {
HStack {
Image("1")
.resizable()
.frame(width: 46, height: 46)
.cornerRadius(6)
VStack(alignment: .leading, spacing: 6) {
HStack {
Text("女神")
.font(.body)
Spacer()
Text("下午 2:55")
.font(.caption)
.foregroundColor(Color.gray.opacity(0.5))
}
Text("對不起,你是個好人")
.font(.callout)
.foregroundColor(Color.gray)
}
}
.padding(.all)
}
}
```
然後在`ContentView`改為呼叫`GCMainRow()`,這樣程式碼就好看很多了。
```swift
struct ContentView: View {
var body: some View {
GCMainRow()
}
}
```
### 編寫列表 List
好了,現在讓我們來編寫列表檢視吧。我們在最外層使用`List`包裹`GCMainRow`,迴圈 20 個檢視,資料多點才可以讓我們滾動。
```swift
List(0 ..< 20) { _ in // 1
GCMainRow()
}
```
> 1 - 因為我們不需要用到迴圈的一些資料,所以我們使用 _ 去忽略它。
`List`控制元件預設的都會有邊距,下圖黃色是`GCMainRow`的大小,可以看得出來旁邊有空白的填充,這對我們當前的設計來說不太友好,因此我們需要想辦法去掉這些邊距填充。
`List`提供了`listRowInsets`來控制行的邊距(上下左右),我們來試著使用一下。
```swift
List(0 ..< 20) { item in
GCMainRow()
.listRowInsets(EdgeInsets())
}
```
我們發現,這樣寫是沒有作用的,`listRowInsets`的生效條件是“**不能直接在`List`中使用,需要配合`For Each`語句才能生效**”,我們再修改一下程式碼。
```swift
List {
ForEach(0 ..< 20) { item in
GCMainRow()
.listRowInsets(EdgeInsets())
}
}
```
好了,這次生效了,這就是我們要的結果。
### 自定義分隔線
我們仔細觀察一下分隔線,在微信裡分隔線是左對齊在名稱和訊息內容的,所以我們需要把現有的分隔線隱藏掉,然後再實現它。
在這裡講解一下,`List`是基於`UITableView`去實現的,這意味著我們可以通過`appearance`全域性修改它的所有屬性,正如我們現在需要取消它預設的分隔線,將`separatorStyle`設定為`.none`即可。
```swift
init() {
UITableView.appearance().separatorStyle = .none
}
```
因為分隔線是貼著右邊緣的,所以我們需要在包裹著名稱、時間、訊息內容的`VStack`外層再包裹一層`VStack`,在其中再新增分隔線`Divider`,並將裡層的`VStack`設定右邊距,最後將最外層的`HStack`的`padding`改為上和左邊距。是不是聽得有點懵?沒關係,看看程式碼就很容易理解了。
```swift
HStack(alignment: .top) { // edit
// 頭像
VStack { // new
VStack(alignment: .leading, spacing: 6) {
// 名稱、時間、訊息內容
}
.padding(.trailing) // new
Divider() // new
}
}
.padding(.top) // edit
.padding(.leading) // edit
```
我們現在來看一下效果。
### 未讀訊息小紅點
未讀訊息在頭像的右上方,小紅點的中心點是位於頭像的右上頂端。我們可以使用`overlay`疊加一個檢視,來製作小紅點吧。
```swift
Image("1")
// ...
.overlay(
Color.red // 1
.frame(width: 16, height: 16)
.cornerRadius(8)
.offset(x: 23, y: -23) // 2
)
```
> // 1 - Color 本身也是一個檢視元件,這是官方的定義 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) extension Color : View { }
>
> // 2 - 設定檢視的偏移量,那麼23是怎樣得出來的呢?很簡單,因為預設的`overlay`檢視是位於父檢視的中心,那麼我們要將它放置在右上角,那麼只需要寬高都除以2就可以了,那麼這裡的結果就是,x軸增加23px,y軸減少23px
接下來是未讀數量,和上面的相似,在`Color`檢視上利用`overlay`疊加`Text`檢視就可以了。
```swift
Color.red
.overlay( // 1
Text("1")
.font(.caption)
.foregroundColor(.white)
)
.frame(width: 16, height: 16)
.cornerRadius(8)
.offset(x: 23, y: -23)
```
> // 1- 值得注意的是,因為文字也是需要偏移到右上角的,所以必須放在前面,不然它預設就是居中的
至此,程式碼演示結束,預覽一下靜態圖,文章開頭有 gif 動態圖效果。
## 總結
好了,這篇文章就到這裡,篇幅有點長了。不過沒關係,我們來總結一下關鍵點:
1. `List`預設是有行邊距的,要取消或修改它的行邊距,我們必須通過`For Each`再配合上`listRowInsets`才能實現。
2. `List`是基於`UITableView`去實現的,這意味著我們可以通過`appearance`全域性修改它的所有屬性。
3. 使用`overlay`可以給檢視疊加一個檢視。
## Demo 原始碼下載
我已經把 Demo 上傳至 GitHub 上面,專案名字是 [SwiftUI-Tutorials](https://github.com/GarveyCalvin/SwiftUI-Tutorials),目錄名為`GCWechatList`,有需要的朋友可以去下載執行一下,當然你也可以跟著文章去做一遍,這樣更有利於你掌握此方面的知識。
文章篇幅有點長,雖然教的東西也挺簡單,但概述得比較詳細。任何東西都是先從簡單入手的,才不會造成勸退不是嗎?哈哈,此文章針對於新手而言還是很友好的,對於已經會的人來講就可能廢話有點多了,如果必須要噴,請輕噴,我比較玻璃心。
**如果本文章對你有幫助,請關注我,你的關注就是我後續寫文章的動力,下期會更精彩噢!**
## 關於作者
博文作者:GarveyCalvin
微博:https://weibo.com/feiyueharia
部落格園:https://www.cnblogs.com/GarveyCalvin
本文版權歸作者,歡迎轉載,但必須保留此段宣告,並給出原文連結,謝謝合作!
### 公眾號
作者第一次運營公眾號,請你們一定要關注我的公眾號,給我點動力,後期主要運營公眾號為主。這是第二篇釋出的文章,需要你們的支援,謝謝你們!
### QQ群
一起討論 SwiftUI,群主喜歡看熱鬧,當吃瓜人員。進來時填寫你在哪裡看到此文章的,並介紹下自己,一句話就行。
![](https://tva1.sinaimg.cn/large/007S8ZIlgy1gdy28h6twhj306a082t92.jpg)