最近在學習 SwiftUI ,我一般都是先去學習界面佈局,所以就想著仿寫一下經常使用的軟體的界面,所以先拿微信開刀。因為不想一次性發太多的內容,所以只好將主題分解,一部分一部分地去講,接下來我們一起來學習吧。 ...
簡介
最近在學習 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)
開始編寫代碼
編寫列表行
我們來先把頭像添加進來。
Image("1")
.resizable() // 1
.frame(width: 46, height: 46) // 2
.cornerRadius(6)// 3
1 - 在 SwiftUI 中,如果需要控製圖像的大小,則必須先調用resizable修飾
2- 設置圖像大小
3 - 設置圓角大小,四個角的大小都相同
因為佈局是橫向的,所以我們在外層使用HStack
包裹起來,然後添加聯繫人名字和最後發消息時間。
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 包裹起來。
VStack(alignment: .leading, spacing: 6) { // 1
// 名稱和時間
Text("對不起,你是個好人")
.font(.callout)
.foregroundColor(Color.gray)
}
// 1 - 設定
VStack
裡子控制項居左對齊,預設是居中對齊。再設定子控制項的間隙為 6 個像素,這樣比較符合微信上面的設計。
現在樣子已經出來了,我們先預覽下效果。
我們給最外層的HStack
增加padding
,使它更美觀一些,參數填寫.all
代表四周都需要邊框。經過我的眼力觀察,它的預設是 16px 的樣子。
HStack {
// 頭像、名稱、時間、消息內容
}
.padding(.all)
有了間距,好看多了。
接下來創建一個視圖,它負責裝載行視圖,起名為GCMainRow
。
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()
,這樣代碼就好看很多了。
struct ContentView: View {
var body: some View {
GCMainRow()
}
}
編寫列表 List
好了,現在讓我們來編寫列表視圖吧。我們在最外層使用List
包裹GCMainRow
,迴圈 20 個視圖,數據多點才可以讓我們滾動。
List(0 ..< 20) { _ in // 1
GCMainRow()
}
1 - 因為我們不需要用到迴圈的一些數據,所以我們使用 _ 去忽略它。
List
控制項預設的都會有邊距,下圖黃色是GCMainRow
的大小,可以看得出來旁邊有空白的填充,這對我們當前的設計來說不太友好,因此我們需要想辦法去掉這些邊距填充。
List
提供了listRowInsets
來控制行的邊距(上下左右),我們來試著使用一下。
List(0 ..< 20) { item in
GCMainRow()
.listRowInsets(EdgeInsets())
}
我們發現,這樣寫是沒有作用的,listRowInsets
的生效條件是“不能直接在List
中使用,需要配合For Each
語句才能生效”,我們再修改一下代碼。
List {
ForEach(0 ..< 20) { item in
GCMainRow()
.listRowInsets(EdgeInsets())
}
}
好了,這次生效了,這就是我們要的結果。
自定義分隔線
我們仔細觀察一下分隔線,在微信里分隔線是左對齊在名稱和消息內容的,所以我們需要把現有的分隔線隱藏掉,然後再實現它。
在這裡講解一下,List
是基於UITableView
去實現的,這意味著我們可以通過appearance
全局修改它的所有屬性,正如我們現在需要取消它預設的分隔線,將separatorStyle
設置為.none
即可。
init() {
UITableView.appearance().separatorStyle = .none
}
因為分隔線是貼著右邊緣的,所以我們需要在包裹著名稱、時間、消息內容的VStack
外層再包裹一層VStack
,在其中再添加分隔線Divider
,並將裡層的VStack
設定右邊距,最後將最外層的HStack
的padding
改為上和左邊距。是不是聽得有點懵?沒關係,看看代碼就很容易理解了。
HStack(alignment: .top) { // edit
// 頭像
VStack { // new
VStack(alignment: .leading, spacing: 6) {
// 名稱、時間、消息內容
}
.padding(.trailing) // new
Divider() // new
}
}
.padding(.top) // edit
.padding(.leading) // edit
我們現在來看一下效果。
未讀消息小紅點
未讀消息在頭像的右上方,小紅點的中心點是位於頭像的右上頂端。我們可以使用overlay
疊加一個視圖,來製作小紅點吧。
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
視圖就可以了。
Color.red
.overlay( // 1
Text("1")
.font(.caption)
.foregroundColor(.white)
)
.frame(width: 16, height: 16)
.cornerRadius(8)
.offset(x: 23, y: -23)
// 1- 值得註意的是,因為文本也是需要偏移到右上角的,所以必須放在前面,不然它預設就是居中的
至此,代碼演示結束,預覽一下靜態圖,文章開頭有 gif 動態圖效果。
總結
好了,這篇文章就到這裡,篇幅有點長了。不過沒關係,我們來總結一下關鍵點:
List
預設是有行邊距的,要取消或修改它的行邊距,我們必須通過For Each
再配合上listRowInsets
才能實現。List
是基於UITableView
去實現的,這意味著我們可以通過appearance
全局修改它的所有屬性。- 使用
overlay
可以給視圖疊加一個視圖。
Demo 源碼下載
我已經把 Demo 上傳至 GitHub 上面,項目名字是 SwiftUI-Tutorials,目錄名為GCWechatList
,有需要的朋友可以去下載運行一下,當然你也可以跟著文章去做一遍,這樣更有利於你掌握此方面的知識。
文章篇幅有點長,雖然教的東西也挺簡單,但概述得比較詳細。任何東西都是先從簡單入手的,才不會造成勸退不是嗎?哈哈,此文章針對於新手而言還是很友好的,對於已經會的人來講就可能廢話有點多了,如果必須要噴,請輕噴,我比較玻璃心。
如果本文章對你有幫助,請關註我,你的關註就是我後續寫文章的動力,下期會更精彩噢!
關於作者
博文作者:GarveyCalvin
微博:https://weibo.com/feiyueharia
博客園:https://www.cnblogs.com/GarveyCalvin
本文版權歸作者,歡迎轉載,但必須保留此段聲明,並給出原文鏈接,謝謝合作!
公眾號
作者第一次運營公眾號,請你們一定要關註我的公眾號,給我點動力,後期主要運營公眾號為主。這是第二篇發佈的文章,需要你們的支持,謝謝你們!
QQ群
一起討論 SwiftUI,群主喜歡看熱鬧,當吃瓜人員。進來時填寫你在哪裡看到此文章的,並介紹下自己,一句話就行。