TabView
開(kāi)發(fā)者手冊(cè)
說(shuō)明
TabView is a view that switches between multiple child views using interactive user interface elements.
TabView是一個(gè)使用交互式用戶(hù)界面元素在多個(gè)子視圖之間切換的視圖。
申明
struct TabView<SelectionValue, Content> where SelectionValue : Hashable, Content : View
概述
To create a user interface with tabs, place views in a TabView and apply the [tabItem(_:)] modifier to the contents of each tab. The following creates a tab view with three tabs:
例如以下的代碼
TabView {
Text("The First Tab")
.tabItem {
Image(systemName: "1.square.fill")
Text("First")
}
Text("Another Tab")
.tabItem {
Image(systemName: "2.square.fill")
Text("Second")
}
Text("The Last Tab")
.tabItem {
Image(systemName: "3.square.fill")
Text("Third")
}
}
.font(.headline)
可以創(chuàng)造一個(gè)最基本的TabView

!!!NOTICE
Tab里的內(nèi)容只能包含文字,或圖片,或文字和圖片的組合
Tab一般來(lái)說(shuō)最多只能顯示5個(gè),多余的Tab(包括第5個(gè))會(huì)被省略到一個(gè)...中


今日目標(biāo)
1. 以L(fǎng)ogo+文字的形式創(chuàng)建5個(gè)TabView
2. 封裝TabView到一個(gè)TabContent的func中,并添加選中Tab能改變Logo樣式的功能
1. 創(chuàng)建5個(gè)TabView
外觀
上面是一個(gè)圖標(biāo),下面是文字,所以才用VStack
以第一個(gè)lightbulb??+'飯乎'為例
VStack{
Image(systemName: "lightbulb")
Text("飯乎")
}
于是得到了一個(gè):

構(gòu)造TabView核心代碼
TabView(selection: $selection){
VStack{
Text("現(xiàn)在選中的Tab:\(selection)")
.font(.title)
Button(action: {
withAnimation{
self.selection = 1
}
})
{
Text("Change Tab to 1")
.font(.largeTitle)
} //用按鈕手動(dòng)切換到第二個(gè)Tab
}
.tabItem {
VStack{
Image(systemName: "lightbulb")
Text("飯乎")
}
}
.tag(0)
// 其他幾個(gè)tab以此類(lèi)推
解釋
selection: $selction用于獲取當(dāng)前選中的Tab編號(hào),也就是最后tag里的值
tag用于給每一個(gè)Tab標(biāo)記一個(gè)獨(dú)特的編號(hào)
systemName是個(gè)好東西,能調(diào)用Apple全套的圖標(biāo)。可以下載SF Symbols查閱圖標(biāo)名稱(chēng)

2. 創(chuàng)造一個(gè)TabContent的func,輸入Tab的編號(hào)、是否選中,并返回some View
先把每個(gè)Tab的名稱(chēng)/圖標(biāo)名稱(chēng)加到Array里
let TabName: Array<String> = ["飯乎","飯廳","飯跡","飯桶","飯具","垃圾桶"]
let TabLogoName: Array<String> = ["lightbulb","tray.full","clock","person.circle","hammer","trash"]
TabContent返回一個(gè)VStack,包含Logo和文字
選中時(shí)更改圖標(biāo):如果選中,就圖標(biāo)名稱(chēng)末尾加上.fill
func TabContent (TabIndex: Int, Selected: Bool) -> some View {
return
VStack {
Image(systemName: Selected ? (TabLogoName[TabIndex] + ".fill"): TabLogoName[TabIndex])
Text(TabName[TabIndex])
}
}
解釋
Selected ? (TabLogoName[TabIndex]+".fill"):TabLogoName[TabIndex]
形如XXX ? A : B,如果XXX是真的就返回A,否則返回B
some View
some View的這個(gè)奇怪用法,俺也不是完全懂。
這里有一篇文章仿佛可以參考https://zhuanlan.zhihu.com/p/105213050
some View的用法使得SwiftUI支持接受一些opaque return types——即我們并不需要非常明確的告訴SwiftUI這個(gè)東西是啥,他只要遵循conform View就可以了。
簡(jiǎn)而言之,some View是指,這個(gè)東西長(zhǎng)得像一個(gè)View,遵循View的法則。
因此在SwiftUI的語(yǔ)法下,以下代碼是會(huì)報(bào)錯(cuò)的:
func WrongExample() -> View {
return Text("This code is wrong!")
}
錯(cuò)誤信息:Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements,這是因?yàn)槊髅魑覀兿胍黵eturn一個(gè)View,但是程序里return了一個(gè)Text!
這樣是正確的代碼;
func RightExample() -> Text {
return Text("Ohhhhhhh!")
}
但是這樣并不是很方便——這個(gè)東西只能return一個(gè)Text,應(yīng)用不是非常廣泛;
因此我們選擇使用some View。
func RightExample() -> some View {
return Text("Ohhhhhhhh!")
}
引用斯坦福那位教授的奇妙比喻:
-
View就像樂(lè)高。 -
Text就像一塊樂(lè)高——他是樂(lè)高的基本元件之一,他是樂(lè)高。 - 把很多樂(lè)高元件組合起來(lái),形成一個(gè)大東西,他還是樂(lè)高。
- Lego Bricks -> Lego Furniture -> Lego House -> Lego Neighbourhood -> Lego World!他們都是樂(lè)高。
- 和樂(lè)高不同的是,有些特殊的
View用來(lái)組合像Text一樣的基本元件,他們叫View Combiner。 - 因此,
some View也可以返回一些View Combiner,他們之中有很多的View。
然后主程序中調(diào)用下就可以了
最終代碼&運(yùn)行效果
//
// ContentView.swift
// WhereToEat?
//
// Created by QSD on 2020/7/2.
// Copyright ? 2020 QSDQSB. All rights reserved.
//
import SwiftUI
let TabName: Array<String> = ["飯乎","飯廳","飯跡","飯桶","飯具","垃圾桶"]
let TabLogoName: Array<String> = ["lightbulb","tray.full","clock","person.circle","hammer","trash"]
struct ContentView: View {
@State private var selection = 0
var focused = 0
var body: some View {
TabView(selection: $selection){
VStack{
Text("現(xiàn)在選中的Tab:\(selection)")
.font(.title)
Button(action: {
withAnimation{
self.selection = 1
}
})
{
Text("Change Tab to 1")
.font(.largeTitle)
}
}
.tabItem {
TabContent(TabIndex: 0, Selected: selection == 0)
}
.tag(0)
// MARK: - 飯廳
ZStack{
Text("現(xiàn)在選中的Tab:\(selection)")
.font(.title)
}
.tabItem {
TabContent(TabIndex: 1, Selected: selection == 1)
}
.tag(1)
// MARK: - 飯跡
ZStack{
Text("現(xiàn)在選中的Tab:\(selection)")
.font(.title)
}
.tabItem{
ZStack{
TabContent(TabIndex: 2, Selected: selection == 2)
}
}
.tag(2)
// MARK: - 飯桶
ZStack{
Text("現(xiàn)在選中的Tab:\(selection)")
.font(.title)
}
.tabItem{
TabContent(TabIndex: 3, Selected: selection == 3)
}
.tag(3)
Text("飯具")
.tabItem{
TabContent(TabIndex: 4, Selected: selection == 4)
}
.tag(4)
}.accentColor(.pink)
}
}
func TabContent (TabIndex: Int, Selected: Bool) -> some View {
return
VStack {
Image(systemName: Selected ? (TabLogoName[TabIndex] + ".fill"): TabLogoName[TabIndex])
Text(TabName[TabIndex])
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
當(dāng)然,如果覺(jué)得一遍一遍輸tag很煩的話(huà),用個(gè)ForEach就完事兒了:
ForEach (1..<3) {Index in
ZStack{
Text("現(xiàn)在選中的Tab:\(self.selection)")
.font(.title)
}
.tabItem {
TabContent(TabIndex: Index, Selected: self.selection == Index)
}
.tag(Index)
}
