TabView
开发者手册
说明
TabView is a view that switches between multiple child views using interactive user interface elements.
TabView是一个使用交互式用户界面元素在多个子视图之间切换的视图。
申明
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)
可以创造一个最基本的TabView
!!!NOTICE
Tab里的内容只能包含文字,或图片,或文字和图片的组合
Tab一般来说最多只能显示5个,多余的Tab(包括第5个)会被省略到一个...
中
今日目标
1. 以Logo+文字的形式创建5个TabView
2. 封装TabView到一个TabContent
的func中,并添加选中Tab能改变Logo样式
的功能
1. 创建5个TabView
外观
上面是一个图标,下面是文字,所以才用VStack
以第一个lightbulb💡+'饭乎'为例
VStack{
Image(systemName: "lightbulb")
Text("饭乎")
}
于是得到了一个:
构造TabView核心代码
TabView(selection: $selection){
VStack{
Text("现在选中的Tab:\(selection)")
.font(.title)
Button(action: {
withAnimation{
self.selection = 1
}
})
{
Text("Change Tab to 1")
.font(.largeTitle)
} //用按钮手动切换到第二个Tab
}
.tabItem {
VStack{
Image(systemName: "lightbulb")
Text("饭乎")
}
}
.tag(0)
// 其他几个tab以此类推
解释
selection: $selction
用于获取当前选中的Tab编号,也就是最后tag
里的值
tag
用于给每一个Tab标记一个独特的编号
systemName
是个好东西,能调用Apple全套的图标。可以下载SF Symbols
查阅图标名称
2. 创造一个TabContent
的func,输入Tab的编号、是否选中,并返回some View
先把每个Tab的名称/图标名称加到Array里
let TabName: Array<String> = ["饭乎","饭厅","饭迹","饭桶","饭具","垃圾桶"]
let TabLogoName: Array<String> = ["lightbulb","tray.full","clock","person.circle","hammer","trash"]
TabContent
返回一个VStack,包含Logo和文字
选中时更改图标:如果选中,就图标名称末尾加上.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
的这个奇怪用法,俺也不是完全懂。
这里有一篇文章仿佛可以参考https://zhuanlan.zhihu.com/p/105213050
some View
的用法使得SwiftUI支持接受一些opaque return types
——即我们并不需要非常明确的告诉SwiftUI这个东西是啥,他只要遵循conform View
就可以了。
简而言之,some View
是指,这个东西长得像一个View
,遵循View
的法则。
因此在SwiftUI的语法下,以下代码是会报错的:
func WrongExample() -> View {
return Text("This code is wrong!")
}
错误信息:Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements
,这是因为明明我们想要它return一个View,但是程序里return了一个Text!
这样是正确的代码;
func RightExample() -> Text {
return Text("Ohhhhhhh!")
}
但是这样并不是很方便——这个东西只能return一个Text,应用不是非常广泛;
因此我们选择使用some View
。
func RightExample() -> some View {
return Text("Ohhhhhhhh!")
}
引用斯坦福那位教授的奇妙比喻:
-
View
就像乐高。 -
Text
就像一块乐高——他是乐高的基本元件之一,他是乐高。 - 把很多乐高元件组合起来,形成一个大东西,他还是乐高。
- Lego Bricks -> Lego Furniture -> Lego House -> Lego Neighbourhood -> Lego World!他们都是乐高。
- 和乐高不同的是,有些特殊的
View
用来组合像Text
一样的基本元件,他们叫View Combiner
。 - 因此,
some View
也可以返回一些View Combiner
,他们之中有很多的View
。
然后主程序中调用下就可以了
最终代码&运行效果
//
// 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("现在选中的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("现在选中的Tab:\(selection)")
.font(.title)
}
.tabItem {
TabContent(TabIndex: 1, Selected: selection == 1)
}
.tag(1)
// MARK: - 饭迹
ZStack{
Text("现在选中的Tab:\(selection)")
.font(.title)
}
.tabItem{
ZStack{
TabContent(TabIndex: 2, Selected: selection == 2)
}
}
.tag(2)
// MARK: - 饭桶
ZStack{
Text("现在选中的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()
}
}
当然,如果觉得一遍一遍输tag很烦的话,用个ForEach就完事儿了:
ForEach (1..<3) {Index in
ZStack{
Text("现在选中的Tab:\(self.selection)")
.font(.title)
}
.tabItem {
TabContent(TabIndex: Index, Selected: self.selection == Index)
}
.tag(Index)
}