1. 程式人生 > >SwiftUI - 一起來仿寫微信APP之一首頁列表檢視

SwiftUI - 一起來仿寫微信APP之一首頁列表檢視

## 簡介 最近在學習 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)