本文使用常見的View和Modifiers進(jìn)行布局,實(shí)現(xiàn)應(yīng)用商城的界面。通過具體的案例實(shí)現(xiàn),可以更好的熟悉SwiftUI的使用
效果:

效果示意
簡(jiǎn)單介紹:
實(shí)現(xiàn)應(yīng)用商城的游戲界面,分為三部分:標(biāo)題欄、推薦游戲、周邊游戲。
標(biāo)題欄會(huì)顯示標(biāo)題、頭像、更新數(shù)字
推薦游戲:標(biāo)題、描述、游戲圖片??梢宰笥一瑒?dòng)
周邊游戲:標(biāo)題、描述、多個(gè)游戲選項(xiàng)??梢陨舷禄瑒?dòng)
分析如何實(shí)現(xiàn):
整體布局:
- 界面可以實(shí)現(xiàn)導(dǎo)航效果,所以需要使用NavigationView
- 界面可以上下滑動(dòng),所以需要使用ScrollView
- 界面需要上下布局,所以使用VStack進(jìn)行布局
- 三部分使用Divider()實(shí)現(xiàn)分割效果
標(biāo)題欄:
- 頭像是給Divider視圖上方進(jìn)行覆蓋
- 更新數(shù)字覆蓋在頭像上方
推薦游戲:
- 左右滑動(dòng)通過TabView實(shí)現(xiàn)。
- 每一個(gè)界面使用VStack來實(shí)現(xiàn)每行顯示
- 游戲圖片中通過overlay覆蓋其他文字和圖片
周邊游戲:
- 標(biāo)題和描述不需要滑動(dòng),因此直接進(jìn)行設(shè)置
- 下方的多個(gè)游戲選項(xiàng)可以左右滑動(dòng),使用TabView
- 下方的多個(gè)游戲選項(xiàng)可以上下滑動(dòng),使用List實(shí)現(xiàn)
主要學(xué)習(xí):
- 布局
- NavigationView
- Divider
- TabView
- List
- overlay
實(shí)現(xiàn)過程:
整體布局
NavigationView {
ScrollView {
VStack {
}
}
}.navigationTitle("游戲")
說明:
- NavigationView用來實(shí)現(xiàn)界面導(dǎo)航效果。使用navigationTitle實(shí)現(xiàn)游戲標(biāo)題
- ScrollView可以讓界面view上下滑動(dòng),就和OC中的UIScrollView一樣
- 在界面中使用VStack實(shí)現(xiàn)垂直布局。
標(biāo)題欄
Divider().padding(EdgeInsets.init(top: 20, leading: 20, bottom: 0, trailing: 20))
.overlay {
AsyncImage(url: URL.init(string: img2), content: { img in
img.resizable()
}, placeholder: {
ProgressView()
}).frame(width: 50, height: 50).cornerRadius(25)
.overlay {
Text("83").frame(width: 32, height: 20).background(.red).cornerRadius(10).foregroundColor(.white).padding(EdgeInsets.init(top: -30, leading: 30, bottom: 0, trailing: 0))
}.padding(EdgeInsets.init(top: -64, leading: 300, bottom: 0, trailing: 0))
}
說明:
- Divider()是一個(gè)分割線視圖
- 使用padding來填充空白到視圖的周圍,使用你指定的邊和填充量來進(jìn)行設(shè)置,這里使用了EdgeInsets來設(shè)置上下左右的填充量。
- 在一個(gè)視圖上面想要覆蓋一個(gè)視圖,就需要使用overlay來實(shí)現(xiàn)。這里給Divider來覆蓋一個(gè)頭像視圖
- AsyncImage用來異步加載圖片,因?yàn)橛锌赡軟]有加載成功,所以使用placeholder設(shè)置加載失敗后的占位(可以設(shè)置圖片、可以設(shè)置文字、也可以像這里設(shè)置加載視圖)
- 給頭像覆蓋一個(gè)視圖,設(shè)置一個(gè)文字,使用
Text("83")進(jìn)行設(shè)置 - Text的設(shè)置很簡(jiǎn)單,就不詳細(xì)說明了。
推薦游戲
TabView {
ForEach($items, id: \.self) {_ in
VStack {
HStack {
Text("重磅更新").foregroundColor(.blue).fontWeight(.bold)
Spacer()
}
HStack {
Text("地下城堡3:魂之詩(shī)").font(.title)
Spacer()
}
HStack {
Text("新地圖“廢械陣”上線").font(.title2).foregroundColor(.gray)
Spacer()
}
AsyncImage(url: URL.init(string: img1)) { img in
img.resizable()
} placeholder: {
ProgressView()
}
.frame(height: 220)
.cornerRadius(10)
.overlay {
VStack {
Spacer()
HStack {
AsyncImage(url: URL.init(string: img2)) { img in
img.resizable()
} placeholder: {
ProgressView()
}
.frame(width: 50, height: 50).cornerRadius(12)
VStack {
HStack {
Text("地下城堡3:魂之詩(shī)").foregroundColor(.white).font(.title3)
Spacer()
}
HStack {
Text("暗黑文字地牢探險(xiǎn)").foregroundColor(.gray)
Spacer()
}
}
Spacer()
VStack {
Button("獲取") {
print("get")
}.foregroundColor(.white).font(Font.system(.title2).bold()).frame(width: 94, height: 40).background(.gray.opacity(0.8)).cornerRadius(20)
Text("App內(nèi)購(gòu)買").foregroundColor(.gray).font(.footnote)
}.padding(.top, 15)
}
.padding(EdgeInsets.init(top: 0, leading: 16, bottom: 6, trailing: 16))
.background(LinearGradient(colors: [.black.opacity(0.5), .clear], startPoint: .bottom, endPoint: .top)).cornerRadius(12)
}
}
}.padding(20)
}
}
.frame(width: ScreenWidth, height: 320)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
說明:
- TabView可以看做OC中的tabbar,可以實(shí)現(xiàn)左右滑動(dòng)(不要誤以為是UITableView)
- ForEach設(shè)置多行,以此實(shí)現(xiàn)左右滑動(dòng)
- 為了縱向可以給Text設(shè)置特定位置,使用HStack
- Spacer()用來設(shè)置空視圖
- cornerRadius用來設(shè)置邊框圓角角度
- 在這里為了方便調(diào)整視圖位置,都使用到了Spacer()。并且因?yàn)樗旧硪彩且粋€(gè)視圖,所以需要放到Stack中
- 此處按鈕的響應(yīng)事件只進(jìn)行了打印。在實(shí)際開發(fā)中這里應(yīng)該是要跳轉(zhuǎn)界面
-
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))設(shè)置TabView的類型,這里是設(shè)置了分頁(yè)查看,設(shè)置成這種類型就可以左右滑動(dòng)查看。而且還設(shè)置了不顯示小點(diǎn)點(diǎn)
周邊游戲:
代碼:
VStack {
HStack {
Text("我們都在玩").font(.title).bold()
Spacer()
Button("查看全部") {
print("all")
}.font(.title2)
}
HStack {
Text("探索本周游戲熱點(diǎn)").foregroundColor(.gray).font(.title2)
Spacer()
}
}.padding(EdgeInsets.init(top: 0, leading: 20, bottom: 0, trailing: 20))
TabView {
ForEach($items, id: \.self) {_ in
List(0..<3) {_ in
HStack {
AsyncImage(url: URL.init(string: img1)) { img in
img.resizable()
} placeholder: {
ProgressView()
}
.frame(width: 68, height: 68).cornerRadius(16)
VStack {
HStack {
Text("地下城堡3:魂之詩(shī)").font(.title3)
Spacer()
}
HStack {
Text("暗黑文字地牢探險(xiǎn)").foregroundColor(.gray)
Spacer()
}
}
Spacer()
VStack {
Button("獲取") {
print("get")
}.foregroundColor(.blue).font(Font.system(.title2).bold()).frame(width: 94, height: 40).background(.gray.opacity(0.2)).cornerRadius(20)
Text("App內(nèi)購(gòu)買").foregroundColor(.gray).font(.footnote)
}
}
}.listStyle(InsetListStyle())//.listRowSeparator(.hidden)
}
}
.frame(width: ScreenWidth, height: 270)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
說明:
- 這里其實(shí)分成了兩部分,第一部分是標(biāo)題描述,第二部分是游戲顯示
- 第一部分很簡(jiǎn)單,只使用到了Stack、Text、Button。上面已經(jīng)講過這些視圖的使用了
- 依然使用TabView實(shí)現(xiàn)左右滑動(dòng)
- List可以實(shí)現(xiàn)列表顯示,
.listStyle(InsetListStyle())設(shè)置list的類型 - 每個(gè)列表都顯示各自的游戲圖片和說明