QSD的Swift妙妙屋1:TabView

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个)会被省略到一个...

5个Tab
6个Tab

今日目标

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!")
    }
引用斯坦福那位教授的奇妙比喻:
  1. View就像乐高。
  2. Text就像一块乐高——他是乐高的基本元件之一,他是乐高。
  3. 把很多乐高元件组合起来,形成一个大东西,他还是乐高。
  4. Lego Bricks -> Lego Furniture -> Lego House -> Lego Neighbourhood -> Lego World!他们都是乐高。
  5. 和乐高不同的是,有些特殊的View用来组合像Text一样的基本元件,他们叫View Combiner
  6. 因此,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)
                }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354